Source code for libmuscle.manager.test.test_hammer

import pytest
from ymmsl import load
from ymmsl.v0_2 import Conduit, Model, Reference, resolve

from libmuscle.manager.hammer import Plate, flatten


[docs] @pytest.fixture def c1() -> Conduit: return Conduit('macro.out', 'micro.in')
[docs] @pytest.fixture def c2() -> Conduit: return Conduit('micro.in', 'micro.program.in')
[docs] @pytest.fixture def c3() -> Conduit: return Conduit('micro.program.out', 'micro.out')
[docs] @pytest.fixture def c4() -> Conduit: return Conduit('micro.out', 'macro.in')
[docs] @pytest.fixture def c5() -> Conduit: return Conduit('micro.in', 'micro.program2.in')
[docs] @pytest.fixture def plate(c1: Conduit, c2: Conduit, c3: Conduit, c4: Conduit, c5: Conduit) -> Plate: plate = Plate() plate.add(c1, True, False) plate.add(c2, False, True) plate.add(c3, True, False) plate.add(c4, False, True) plate.add(c5, False, True) return plate
[docs] def has_conduit(model: Model, sender: str, receiver: str, filters: str = '') -> bool: """Helper function for checking whether a model has a given conduit. They're in a list, this makes it easier to search, although still inefficient. Filters are given in a single string, separated by single spaces, e.g. 'last pad'. """ for conduit in model.conduits: if conduit.sender == sender and conduit.receiver == receiver: cfs = ' '.join([f.value for f in conduit.filters]) if cfs == filters: return True return False
[docs] def test_plate(plate, c2, c5) -> None: macro_out_conduits = plate.pop_by_sender(Reference('micro.in')) assert macro_out_conduits == { Reference('micro.program.in'): (c2, True), Reference('micro.program2.in'): (c5, True)} # check that they were removed assert len(plate.pop_by_sender(Reference('micro.in'))) == 0
[docs] def test_plate2(plate, c1) -> None: micro2_in_conduits = plate.pop_by_receiver(Reference('micro.in')) assert micro2_in_conduits == {Reference('macro.out'): (c1, True)} # check that it was removed assert len(plate.pop_by_receiver(Reference('micro.in'))) == 0
[docs] def test_flatten_simple() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Simple nested test configuration\n' 'models:\n' ' outer:\n' ' description: Model containing a submodel\n' ' components:\n' ' init:\n' ' ports:\n' ' o_f: init_out\n' ' description: Creates initial conditions\n' ' implementation: p1\n' ' sim:\n' ' ports:\n' ' f_init: init_in\n' ' description: Simulates stuff\n' ' implementation: inner\n' ' conduits:\n' ' init.init_out: sim.init_in\n' ' inner:\n' ' ports:\n' ' f_init: init_in\n' ' description: Nested simulation model\n' ' components:\n' ' simulate:\n' ' ports:\n' ' f_init: in\n' ' description: Simulation program\n' ' implementation: p2\n' ' conduits:\n' ' init_in: simulate.in\n' 'programs:\n' ' p1:\n' ' description: Program that calculates initial conditions\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Program that simulates stuff\n' ' executable: /home/user/codes/p2\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'outer' in flat_config.models model = flat_config.root_model() assert model.name == 'outer' assert len(model.components) == 2 assert 'init' in model.components assert 'sim.simulate' in model.components assert len(model.conduits) == 1 assert has_conduit(model, 'init.init_out', 'sim.simulate.in')
[docs] def test_flatten_deep() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Three-deep nested test configuration\n' 'models:\n' ' top:\n' ' description: Model at the top\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_i: out\n' ' s: in\n' ' description: Calls c2\n' ' implementation: p1\n' ' c2:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Submodel\n' ' implementation: middle\n' ' conduits:\n' ' c1.out: c2.init_in\n' ' c2.final_out: c1.in\n' ' middle:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Middle simulation model, empty shell really\n' ' components:\n' ' c3:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Forward again...\n' ' implementation: bottom\n' ' conduits:\n' ' init_in: c3.init_in\n' ' c3.final_out: final_out\n' ' bottom:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Bottom simulation model\n' ' components:\n' ' c4:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Actual simulation model\n' ' implementation: p2\n' ' conduits:\n' ' init_in: c4.init_in\n' ' c4.final_out: final_out\n' 'programs:\n' ' p1:\n' ' description: Program that calculates initial conditions\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Program that simulates stuff\n' ' executable: /home/user/codes/p2\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'top' in flat_config.models model = flat_config.root_model() assert model.name == 'top' assert len(model.components) == 2 assert 'c1' in model.components assert 'c2.c3.c4' in model.components assert len(model.conduits) == 2 assert has_conduit(model, 'c1.out', 'c2.c3.c4.init_in') assert has_conduit(model, 'c2.c3.c4.final_out', 'c1.in')
[docs] def test_flatten_nested_ensemble() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Three-deep nested test configuration\n' 'models:\n' ' top:\n' ' description: Model at the top\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_i: out\n' ' s: in\n' ' description: Calls c2\n' ' implementation: p1\n' ' c2:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Submodel\n' ' implementation: middle\n' ' multiplicity: 10\n' ' conduits:\n' ' c1.out: c2.init_in\n' ' c2.final_out: c1.in\n' ' middle:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: This should get replicated\n' ' components:\n' ' c3:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Another subcomponent\n' ' implementation: nested\n' ' multiplicity: 5\n' ' conduits:\n' ' init_in: c3.init_in\n' ' c3.final_out: final_out\n' ' nested:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: This should get replicated, nested\n' ' components:\n' ' c4:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: First component\n' ' implementation: p2\n' ' c5:\n' ' ports:\n' ' f_init: init_in\n' ' o_f: final_out\n' ' description: Second component\n' ' implementation: p2\n' ' conduits:\n' ' init_in: c4.init_in\n' ' c4.final_out: c5.init_in\n' ' c5.final_out: final_out\n' 'programs:\n' ' p1:\n' ' description: Program that calculates initial conditions\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Program that simulates stuff\n' ' executable: /home/user/codes/p2\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'top' in flat_config.models model = flat_config.root_model() assert model.name == 'top' assert len(model.components) == 3 assert 'c1' in model.components assert 'c2.c3.c4' in model.components assert model.components['c2.c3.c4'].multiplicity == [10, 5] assert 'c2.c3.c5' in model.components assert model.components['c2.c3.c5'].multiplicity == [10, 5] assert len(model.conduits) == 3 assert has_conduit(model, 'c2.c3.c4.final_out', 'c2.c3.c5.init_in') assert has_conduit(model, 'c1.out', 'c2.c3.c4.init_in') assert has_conduit(model, 'c2.c3.c5.final_out', 'c1.in')
[docs] def test_flatten_conduit_filters() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Macro-micro to macro-micro dispatch, nested\n' 'models:\n' ' macro_micro:\n' ' ports:\n' ' f_init: macro_state_in micro_state_in\n' ' o_f: macro_state_out micro_state_out\n' ' description: Macro-micro model\n' ' components:\n' ' macro:\n' ' ports:\n' ' f_init: state_in\n' ' o_i: boundary_out\n' ' s: boundary_in\n' ' o_f: state_out\n' ' description: |\n' ' Macro model that communicates its boundary conditions\n' ' while running, and outputs its state at the end.\n' ' implementation: p1\n' ' micro:\n' ' ports:\n' ' f_init: state_in boundary_in\n' ' o_f: state_out boundary_out\n' ' description: |\n' ' Stateful micro model that takes new boundary conditions and\n' ' optionally a new state on each run. If a new state is received\n' ' then it re-initialises, otherwise the current state is kept.\n' ' Re-equilibrates to the new boundary conditions, then outputs\n' ' boundary and state at the end of each run.\n' ' implementation: p2\n' ' conduits:\n' ' macro_state_in: macro.state_in\n' ' micro_state_in: pad micro.state_in\n' ' macro.boundary_out: micro.boundary_in\n' ' micro.boundary_out: macro.boundary_in\n' ' macro.state_out: macro_state_out\n' ' micro.state_out: last micro_state_out\n' ' init:\n' ' ports:\n' ' o_f: macro_state_out micro_state_out\n' ' description: Creates initial states\n' ' components:\n' ' init_macro:\n' ' ports:\n' ' o_f: state_out\n' ' description: Creates initial conditions for macro\n' ' implementation: p3\n' ' init_micro:\n' ' ports:\n' ' o_f: state_out\n' ' description: Creates initial conditions for micro\n' ' implementation: p4\n' ' conduits:\n' ' init_macro.state_out: macro_state_out\n' ' init_micro.state_out: micro_state_out\n' ' macro_micro_dispatch:\n' ' description: |\n' ' Creates initial conditions, the runs a model until conditions\n' ' change and that model doesn\'t apply anymore, after which we\n' ' pass the state to a second model to finish the calculation.\n' ' Actually uses the same model twice, but maybe with different\n' ' settings. Note that the model here is a closed box, we cannot\n' ' tell that there is a macro-micro inside, other than from the name\n' ' of the model and its ports.\n' ' components:\n' ' init:\n' ' ports:\n' ' o_f: macro_state_out micro_state_out\n' ' description: Calculates initial conditions\n' ' implementation: init\n' ' first:\n' ' ports:\n' ' f_init: macro_state_in micro_state_in\n' ' o_f: macro_state_out micro_state_out\n' ' description: First macro-micro model\n' ' implementation: macro_micro\n' ' second:\n' ' ports:\n' ' f_init: macro_state_in micro_state_in\n' ' description: First macro-micro model\n' ' implementation: macro_micro\n' ' conduits:\n' ' init.macro_state_out: first.macro_state_in\n' ' init.micro_state_out: first.micro_state_in\n' ' first.macro_state_out: second.macro_state_in\n' ' first.micro_state_out: second.micro_state_in\n' 'programs:\n' ' p1:\n' ' description: Implements macro model\n' ' executable: /home/user/codes/macro\n' ' p2:\n' ' description: Implements micro model\n' ' executable: /home/user/codes/micro\n' ' p3:\n' ' description: Program that calculates initial conditions for macro\n' ' executable: /home/user/codes/macro_ic\n' ' p4:\n' ' description: Program that calculates initial conditions for micro\n' ' executable: /home/user/codes/micro_ic\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'macro_micro_dispatch' in flat_config.models model = flat_config.root_model() assert model.name == 'macro_micro_dispatch' assert len(model.components) == 6 assert 'init.init_macro' in model.components assert 'init.init_micro' in model.components assert 'first.macro' in model.components assert 'first.micro' in model.components assert 'second.macro' in model.components assert 'second.micro' in model.components assert len(model.conduits) == 8 assert has_conduit(model, 'init.init_macro.state_out', 'first.macro.state_in') assert has_conduit( model, 'init.init_micro.state_out', 'first.micro.state_in', 'pad') assert has_conduit(model, 'first.macro.boundary_out', 'first.micro.boundary_in') assert has_conduit(model, 'first.micro.boundary_out', 'first.macro.boundary_in') assert has_conduit(model, 'first.macro.state_out', 'second.macro.state_in') assert has_conduit( model, 'first.micro.state_out', 'second.micro.state_in', 'last pad') assert has_conduit(model, 'second.macro.boundary_out', 'second.micro.boundary_in') assert has_conduit(model, 'second.micro.boundary_out', 'second.macro.boundary_in')
[docs] def test_flatten_multicast() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Testing wiring of multicast conduits\n' 'models:\n' ' outer:\n' ' description: Model containing a submodel\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_f: out\n' ' description: Sends some data\n' ' implementation: p1\n' ' c2:\n' ' ports:\n' ' f_init: in\n' ' o_f: out1 out2\n' ' description: Submodel\n' ' implementation: inner\n' ' c3:\n' ' ports:\n' ' f_init: in1 in2 in3\n' ' description: Receives some data\n' ' implementation: p3\n' ' c4:\n' ' ports:\n' ' f_init: in1 in2\n' ' description: Receives some data\n' ' implementation: p3\n' ' conduits:\n' ' c1.out:\n' ' - c2.in\n' ' - c3.in1\n' ' c2.out1:\n' ' - c3.in2\n' ' - c4.in1\n' ' c2.out2:\n' ' - c3.in3\n' ' - c4.in2\n' ' inner:\n' ' ports:\n' ' f_init: in\n' ' o_f: out1 out2\n' ' description: Nested simulation model\n' ' components:\n' ' c1:\n' ' ports:\n' ' f_init: in\n' ' o_f: out\n' ' description: Simulation program\n' ' implementation: p2\n' ' c2:\n' ' ports:\n' ' f_init: in\n' ' o_f: out\n' ' description: Simulation program\n' ' implementation: p2\n' ' conduits:\n' ' in:\n' ' - c1.in\n' ' - c2.in\n' ' c1.out:\n' ' - out1\n' ' - out2\n' 'programs:\n' ' p1:\n' ' description: Program that sends something\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Program that simulates stuff\n' ' executable: /home/user/codes/p2\n' ' p3:\n' ' description: Program that receives some things\n' ' executable: /home/user/codes/p2\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'outer' in flat_config.models model = flat_config.root_model() assert model.name == 'outer' assert len(model.components) == 5 assert 'c1' in model.components assert 'c2.c1' in model.components assert 'c2.c2' in model.components assert 'c3' in model.components assert 'c4' in model.components assert len(model.conduits) == 7 assert has_conduit(model, 'c1.out', 'c3.in1') assert has_conduit(model, 'c1.out', 'c2.c1.in') assert has_conduit(model, 'c1.out', 'c2.c2.in') assert has_conduit(model, 'c2.c1.out', 'c3.in2') assert has_conduit(model, 'c2.c1.out', 'c4.in1') assert has_conduit(model, 'c2.c1.out', 'c3.in3') assert has_conduit(model, 'c2.c1.out', 'c4.in2')
[docs] def test_flatten_passthrough_overload() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Testing model passthroughs and custom implementations\n' 'models:\n' ' framework:\n' ' description: Model with a passthrough\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_f: out\n' ' description: Sends some data\n' ' implementation: p1\n' ' c2:\n' ' ports:\n' ' f_init: in\n' ' o_f: out\n' ' description: Extension point\n' ' implementation: passthrough\n' ' c3:\n' ' ports:\n' ' f_init: in\n' ' description: Receives some data\n' ' implementation: p3\n' ' conduits:\n' ' c1.out: c2.in\n' ' c2.out: c3.in\n' ' passthrough:\n' ' ports:\n' ' f_init: in\n' ' o_f: out\n' ' description: Passes through the message\n' ' conduits:\n' ' in: out\n' 'programs:\n' ' p1:\n' ' description: Program that sends something\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Program that simulates stuff\n' ' executable: /home/user/codes/p2\n' ' p3:\n' ' description: Program that receives some things\n' ' executable: /home/user/codes/p2\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'framework' in flat_config.models model = flat_config.root_model() assert model.name == 'framework' assert len(model.components) == 2 assert 'c1' in model.components assert 'c3' in model.components assert len(model.conduits) == 1 assert has_conduit(model, 'c1.out', 'c3.in') nested_config = load(nested_config_yaml) nested_config.custom_implementations[Reference('framework.c2')] = Reference('p2') resolve(Reference([]), nested_config) # apply custom_implementations flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'framework' in flat_config.models model = flat_config.root_model() assert model.name == 'framework' assert len(model.components) == 3 assert 'c1' in model.components assert 'c2' in model.components assert 'c3' in model.components assert len(model.conduits) == 2 assert has_conduit(model, 'c1.out', 'c2.in') assert has_conduit(model, 'c2.out', 'c3.in') assert len(flat_config.custom_implementations) == 0 assert model.components['c2'].implementation == 'p2'
# optional component without implementation
[docs] def test_remove_no_implementation() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Testing a component without implementation\n' 'models:\n' ' optional_micro:\n' ' description: Macro-micro with optional micro\n' ' components:\n' ' macro:\n' ' ports:\n' ' o_i: out\n' ' s: in\n' ' description: |\n' ' Macro model that can run with and without a micro.\n' ' implementation: p1\n' ' micro:\n' ' ports:\n' ' f_init: init\n' ' o_f: final\n' ' description: Optional micro model, not implemented\n' ' conduits:\n' ' macro.out: micro.init\n' ' micro.final: macro.in\n' 'programs:\n' ' p1:\n' ' description: Macro model implementation\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Micro model implementation\n' ' executable: /home/user/codes/p2\n' ) nested_config = load(nested_config_yaml) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'optional_micro' in flat_config.models model = flat_config.root_model() assert model.name == 'optional_micro' assert len(model.components) == 1 assert 'macro' in model.components assert len(model.conduits) == 0 nested_config.custom_implementations[Reference('optional_micro.micro')] = \ Reference('p2') resolve(Reference([]), nested_config) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'optional_micro' in flat_config.models model = flat_config.root_model() assert model.name == 'optional_micro' assert len(model.components) == 2 assert 'macro' in model.components assert 'micro' in model.components assert len(model.conduits) == 2 assert has_conduit(model, 'macro.out', 'micro.init') assert has_conduit(model, 'micro.final', 'macro.in') assert len(flat_config.custom_implementations) == 0 assert model.components['micro'].implementation == 'p2' nested_config = load(nested_config_yaml) nested_config.models['optional_micro'].components['micro'].implementation = \ Reference('p2') nested_config.custom_implementations[Reference('optional_micro.micro')] = None resolve(Reference([]), nested_config) flat_config = flatten(nested_config) assert len(flat_config.models) == 1 assert 'optional_micro' in flat_config.models model = flat_config.root_model() assert model.name == 'optional_micro' assert len(model.components) == 1 assert 'macro' in model.components assert len(model.conduits) == 0
[docs] def test_nested_custom_implementations() -> None: nested_config_yaml = ( 'ymmsl_version: v0.2\n' 'description: Testing nested custom implementations\n' 'models:\n' ' outer:\n' ' description: Outer model\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_f: out\n' ' description: Component c1\n' ' implementation: p1\n' ' c2:\n' ' ports:\n' ' f_init: in\n' ' description: Component c1\n' ' implementation: p1\n' ' conduits:\n' ' c1.out: c2.in\n' ' middle:\n' ' ports:\n' ' o_f: out\n' ' description: Middle model\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_f: out\n' ' description: Another component c1\n' ' c2:\n' ' ports:\n' ' o_f: out\n' ' description: Component c2\n' ' conduits:\n' ' c2.out: out\n' ' inner:\n' ' ports:\n' ' o_f: out\n' ' description: Inner model\n' ' components:\n' ' c1:\n' ' ports:\n' ' o_f: out\n' ' description: Yet another c1, it could happen...\n' ' implementation: p1\n' ' conduits:\n' ' c1.out: out\n' 'programs:\n' ' p1:\n' ' description: Program 1\n' ' executable: /home/user/codes/p1\n' ' p2:\n' ' description: Program 2\n' ' executable: /home/user/codes/p2\n' 'custom_implementations:\n' ' outer.c1: middle\n' ' outer.c1.c1: inner\n' ' outer.c1.c2: inner\n' ' outer.c1.c2.c1: p2\n' ) nested_config = load(nested_config_yaml) resolve(Reference([]), nested_config) flat_config = flatten(nested_config) flat_model = flat_config.models['outer'] assert len(flat_model.components) == 3 assert flat_model.components['c1.c1.c1'].implementation == 'p1' assert flat_model.components['c1.c2.c1'].implementation == 'p2' assert len(flat_model.conduits) == 1 assert flat_model.conduits[0].sender == 'c1.c2.c1.out' assert flat_model.conduits[0].receiver == 'c2.in'