Пример #1
0
def test_loop_bound_when_fparser_not_initialised():
    '''This reproduces #1272: a gocean custom loop boundary could
    not be parsed if the parser has not been initialised previously.
    Reproduce this bug by re-initialising fparser with an empty
    class list.
    '''
    GOLoop.add_bounds("go_offset_sw:go_ct:internal_we_halo:1:2:3:4")
Пример #2
0
def test_goloop_no_parent():
    ''' Attempt to generate code for a loop that has no GOSchedule
    as a parent '''
    from psyclone.gocean1p0 import GOLoop
    goloop = GOLoop(loop_type="inner")
    # Try and generate the code for this loop even though it
    # has no parent schedule and no children
    with pytest.raises(GenerationError):
        goloop.gen_code(None)
Пример #3
0
def test_goloop_no_children():
    ''' Attempt to generate code for a loop that has no child
    kernel calls '''
    gosched = GOInvokeSchedule('name', [])
    gojloop = GOLoop(parent=gosched, loop_type="outer")
    goiloop = GOLoop(parent=gosched, loop_type="inner")
    gosched.addchild(gojloop)
    gojloop.loop_body.addchild(goiloop)
    # Try and generate the code for this loop even though it
    # has no children
    with pytest.raises(GenerationError):
        goiloop.gen_code(None)
Пример #4
0
def test_goloop_lower_to_language_level(monkeypatch):
    ''' Tests that the GOLoop lower_to_language_level method provides the start
    and stop expressions for the loops using the upper/lower_bound methods. '''
    schedule = Schedule()
    goloop = GOLoop(loop_type="inner", parent=schedule)
    assert goloop.start_expr.value == 'NOT_INITIALISED'
    assert goloop.stop_expr.value == 'NOT_INITIALISED'
    monkeypatch.setattr(GOLoop, "lower_bound",
                        lambda x: Literal("1", INTEGER_TYPE))
    monkeypatch.setattr(GOLoop, "upper_bound",
                        lambda x: Literal("1", INTEGER_TYPE))

    goloop.lower_to_language_level()
    assert goloop.start_expr.value == '1'
    assert goloop.stop_expr.value == '1'
Пример #5
0
def test_goloop_unsupp_offset():
    ''' Attempt to generate code for a loop with constant bounds with
    an unsupported index offset '''
    from psyclone.gocean1p0 import GOLoop, GOSchedule, GOKern
    gosched = GOSchedule([])
    gojloop = GOLoop(parent=gosched, loop_type="outer")
    goiloop = GOLoop(parent=gosched, loop_type="inner")
    gosched.addchild(gojloop)
    gojloop.addchild(goiloop)
    gokern = GOKern()
    # Set the index-offset of this kernel to a value that is not
    # supported when using constant loop bounds
    gokern._index_offset = "offset_se"
    goiloop.addchild(gokern)
    with pytest.raises(GenerationError):
        goiloop.gen_code(None)
Пример #6
0
def test_goloop_unsupp_offset():
    ''' Attempt to generate code for a loop with constant bounds with
    an unsupported index offset '''
    gosched = GOInvokeSchedule('name', [])
    # This test expects constant loop bounds
    gosched._const_loop_bounds = True
    gojloop = GOLoop(parent=gosched, loop_type="outer")
    goiloop = GOLoop(parent=gosched, loop_type="inner")
    gosched.addchild(gojloop)
    gojloop.loop_body.addchild(goiloop)
    gokern = GOKern()
    # Set the index-offset of this kernel to a value that is not
    # supported when using constant loop bounds
    gokern._index_offset = "offset_se"
    goiloop.loop_body.addchild(gokern)
    with pytest.raises(GenerationError):
        goiloop.gen_code(None)
Пример #7
0
def test_goloop_no_children():
    ''' Attempt to generate code for a loop that has no child
    kernel calls '''
    from psyclone.gocean1p0 import GOLoop, GOSchedule
    gosched = GOSchedule([])
    gojloop = GOLoop(parent=gosched, loop_type="outer")
    goiloop = GOLoop(parent=gosched, loop_type="inner")
    gosched.addchild(gojloop)
    gojloop.addchild(goiloop)
    # Try and generate the code for this loop even though it
    # has no children
    with pytest.raises(GenerationError):
        goiloop.gen_code(None)
Пример #8
0
def test_goloop_unmatched_offsets():
    ''' Attempt to generate code for a loop with constant bounds with
    two different index offsets '''
    gosched = GOInvokeSchedule('name', [])
    gojloop = GOLoop(parent=gosched, loop_type="outer")
    goiloop = GOLoop(parent=gosched, loop_type="inner")
    gosched.addchild(gojloop)
    gojloop.loop_body.addchild(goiloop)
    gokern1 = GOKern()
    gokern2 = GOKern()
    # Set the index-offset of this kernel to a value that is not
    # supported when using constant loop bounds
    gokern1._index_offset = "go_offset_ne"
    gokern2._index_offset = "go_offset_sw"
    goiloop.loop_body.addchild(gokern1)
    goiloop.loop_body.addchild(gokern2)
    with pytest.raises(GenerationError) as excinfo:
        goiloop.gen_code(None)
    # Note that the kernels do not have a name, so there is a double space
    assert "All Kernels must expect the same grid offset but kernel  " \
        "has offset go_offset_sw which does not match go_offset_ne" \
        in str(excinfo.value)
Пример #9
0
 def __init__(self, config, section):
     super(GOceanConfig, self).__init__(section)
     for key in section.keys():
         # Do not handle any keys from the DEFAULT section
         # since they are handled by Config(), not this class.
         if key in config.get_default_keys():
             continue
         if key == "iteration-spaces":
             # The value associated with the iteration-spaces key is a
             # set of lines, each line defining one new iteration space.
             # Each individual iteration space added is checked
             # in add_bounds for correctness.
             value_as_str = str(section[key])
             new_iteration_spaces = value_as_str.split("\n")
             from psyclone.gocean1p0 import GOLoop
             for it_space in new_iteration_spaces:
                 GOLoop.add_bounds(it_space)
         elif key == "access_mapping":
             # Handled in the base class APISpecificConfig
             pass
         else:
             raise ConfigurationError("Invalid key \"{0}\" found in "
                                      "\"{1}\".".format(
                                          key, config.filename))
Пример #10
0
def test_goloop_bounds_invalid_iteration_space():
    ''' Check that the _upper/lower_bound() methods raise the expected error
    if the iteration space is not recognised. '''
    gosched = GOInvokeSchedule('name', [])
    gojloop = GOLoop(parent=gosched, loop_type="outer")
    # Have to turn-off constant loop bounds to get to the error condition
    gosched._const_loop_bounds = False
    # Set the iteration space to something invalid
    gojloop._iteration_space = "broken"
    with pytest.raises(GenerationError) as err:
        gojloop.upper_bound()
    assert "Unrecognised iteration space, 'broken'." in str(err.value)
    with pytest.raises(GenerationError) as err:
        gojloop.lower_bound()
    assert "Unrecognised iteration space, 'broken'." in str(err.value)
Пример #11
0
def test_goloop_no_parent():
    ''' Attempt to generate code for a loop that has no GOInvokeSchedule
    as a parent '''
    # First create with a schedule as one is required to declare the
    # loop variable
    schedule = Schedule()
    goloop = GOLoop(loop_type="inner", parent=schedule)
    schedule.children = [goloop]
    # Now remove parent and children
    goloop.detach()
    goloop.children = []
    # Try and generate the code for this loop even though it
    # has no parent schedule and no children
    with pytest.raises(GenerationError):
        goloop.gen_code(None)
Пример #12
0
def test_goloop_no_parent():
    ''' Attempt to generate code for a loop that has no GOInvokeSchedule
    as a parent '''
    # Attempt to create a GOLoop within a generic Schedule
    schedule = Schedule()
    with pytest.raises(GenerationError) as err:
        goloop = GOLoop(loop_type="inner", parent=schedule)
    assert ("GOLoops must always be constructed with a parent which is inside "
            "(directly or indirectly) of a GOInvokeSchedule" in str(err.value))

    # Now create it in a GOInvokeSchedule but then detach it
    schedule = GOInvokeSchedule('name', [])
    goloop = GOLoop(loop_type="inner", parent=schedule)
    schedule.children = [goloop]
    # Now remove parent and children
    goloop.detach()

    # Try and generate the code for this loop even though it
    # has no parent schedule and no children
    with pytest.raises(GenerationError):
        goloop.gen_code(None)
Пример #13
0
 def __init__(self, config, section):
     # pylint: disable=too-many-locals
     super(GOceanConfig, self).__init__(section)
     # Setup the mapping for the grid properties. This dictionary stores
     # the name of the grid property as key (e.g. ``go_grid_dx_t``),
     # with the value being a named tuple with an entry for 'property'
     # and 'type'. The 'property' is a format string to dereference
     # a property, and 'type' is a string.
     # These values are taken from the psyclone config file.
     self._grid_properties = {}
     # Initialise debug_mode settings (default to False if it doesn't exist)
     self._debug_mode = False
     for key in section.keys():
         # Do not handle any keys from the DEFAULT section
         # since they are handled by Config(), not this class.
         if key in config.get_default_keys():
             continue
         if key == "iteration-spaces":
             # The value associated with the iteration-spaces key is a
             # set of lines, each line defining one new iteration space.
             # Each individual iteration space added is checked
             # in add_bounds for correctness.
             value_as_str = str(section[key])
             new_iteration_spaces = value_as_str.split("\n")
             from psyclone.gocean1p0 import GOLoop
             for it_space in new_iteration_spaces:
                 GOLoop.add_bounds(it_space)
         elif key == "access_mapping":
             # Handled in the base class APISpecificConfig
             pass
         elif key == "debug_mode":
             # Boolean that specifies if debug mode is enabled
             try:
                 self._debug_mode = section.getboolean("debug_mode")
             except ValueError as err:
                 six.raise_from(
                     ConfigurationError(
                         "error while parsing DEBUG_MODE in the "
                         "[gocean1p0] section of the config file: {0}".
                         format(str(err))), err)
         elif key == "grid-properties":
             # Grid properties have the format:
             # go_grid_area_u: {0}%%grid%%area_u: array: real,
             # First the name, then the Fortran code to access the property,
             # followed by the type ("array" or "scalar") and then the
             # intrinsic type ("integer" or "real")
             all_props = self.create_dict_from_string(section[key])
             for grid_property in all_props:
                 try:
                     fortran, variable_type, intrinsic_type = \
                         all_props[grid_property].split(":")
                 except ValueError:
                     # Raised when the string does not contain exactly
                     # three values separated by ":"
                     error = "Invalid property \"{0}\" found with value " \
                             "\"{1}\" in \"{2}\". It must have exactly " \
                             "three ':'-delimited separated values: the " \
                             "property, whether it is a scalar or an " \
                             "array, and the intrinsic type (real or " \
                             "integer)." \
                             .format(grid_property,
                                     all_props[grid_property],
                                     config.filename)
                     raise ConfigurationError(error)
                 # Make sure to remove the spaces which the config
                 # file might contain
                 self._grid_properties[grid_property] = \
                     GOceanConfig.make_property(fortran.strip(),
                                                variable_type.strip(),
                                                intrinsic_type.strip())
             # Check that the required values for xstop and ystop
             # are defined:
             for required in [
                     "go_grid_xstop", "go_grid_ystop", "go_grid_data",
                     "go_grid_internal_inner_start",
                     "go_grid_internal_inner_stop",
                     "go_grid_internal_outer_start",
                     "go_grid_internal_outer_stop",
                     "go_grid_whole_inner_start",
                     "go_grid_whole_inner_stop",
                     "go_grid_whole_outer_start", "go_grid_whole_outer_stop"
             ]:
                 if required not in self._grid_properties:
                     error = "The config file {0} does not contain " \
                             "values for the following, mandatory grid " \
                             "property: \"{1}\".".format(config.filename,
                                                         required)
                     raise ConfigurationError(error)
         else:
             raise ConfigurationError("Invalid key \"{0}\" found in "
                                      "\"{1}\".".format(
                                          key, config.filename))
Пример #14
0
def test_invalid_config_files(tmpdir):
    ''' Test various error conditions.
    '''

    # Valid configuration file without iteration spaces. We add several
    # iteration spaces to it to test for various error conditions
    # pylint: disable=invalid-name
    # pylint: disable=too-many-statements
    _CONFIG_CONTENT = '''\
    [DEFAULT]
    DEFAULTAPI = dynamo0.3
    DEFAULTSTUBAPI = dynamo0.3
    DISTRIBUTED_MEMORY = true
    REPRODUCIBLE_REDUCTIONS = false
    REPROD_PAD_SIZE = 8
    [gocean1.0]
    '''
    # Create a config files with gocean1.0 section, but an
    # invalid iteration space:
    content = _CONFIG_CONTENT + "iteration-spaces=a:b"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "An iteration space must be in the form" in str(err.value)
        assert "But got \"a:b\"" in str(err.value)

    # Try a multi-line specification to make sure all lines are tested
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:1:2:3:4\n        d:e"
    config_file = tmpdir.join("config2")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "An iteration space must be in the form" in str(err.value)
        assert "But got \"d:e\"" in str(err.value)

    # Invalid {} expression in first loop bound
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:{X}:2:3:4"
    config_file = tmpdir.join("config3")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Only '{start}' and '{stop}' are allowed as bracketed "\
               "expression in an iteration space." in str(err.value)
        assert "But got {X}" in str(err.value)

    # Invalid {} expression in last loop bound:
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:1:2:3:{Y}"
    config_file = tmpdir.join("config4")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Only '{start}' and '{stop}' are allowed as bracketed "\
               "expression in an iteration space." in str(err.value)
        assert "But got {Y}" in str(err.value)

    # Add an invalid key:
    content = _CONFIG_CONTENT + "invalid-key=value"
    config_file = tmpdir.join("config5")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid key \"invalid-key\" found in \"{0}\".".\
            format(str(config_file)) in str(err.value)

        for i in [
                "DEFAULTAPI", "DEFAULTSTUBAPI", "DISTRIBUTED_MEMORY",
                "REPRODUCIBLE_REDUCTIONS"
        ]:
            # They keys are returned in lower case
            assert i.lower() in config.get_default_keys()

    with pytest.raises(InternalError) as err:
        GOLoop.add_bounds(1)
    # Different error message (for type) in python2 vs python3:
    assert "The parameter 'bound_info' must be a string, got '1' "\
           "(type <type 'int'>)" in str(err.value) or \
           "The parameter 'bound_info' must be a string, got '1' "\
           "(type <class 'int'>)" in str(err.value)

    # Test syntactically invalid loop boundaries
    with pytest.raises(ConfigurationError) as err:
        GOLoop.add_bounds("offset:field:space:1(:2:3:4")
    assert "Expression '1(' is not a valid do loop boundary" in str(err.value)
    with pytest.raises(ConfigurationError) as err:
        GOLoop.add_bounds("offset:field:space:1:2:3:4+")
    assert "Expression '4+' is not a valid do loop boundary" in str(err.value)

    # Test invalid field properties - too many fields
    content = _CONFIG_CONTENT + "grid-properties = a: {0}%%b:c:d:e"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid property \"a\" found with value \"{0}%b:c:d:e\"" \
               in str(err.value)

    # Test invalid field properties - not enough fields
    content = _CONFIG_CONTENT + "grid-properties = a:b"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid property \"a\" found with value \"b\"" \
               in str(err.value)

    # Test missing required values
    content = _CONFIG_CONTENT + "grid-properties = a:b:array:real"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        # The config file {0} does not contain values for "..."
        assert "does not contain values for the following, mandatory grid " \
            "property: \"go_grid_xstop\"" in str(err.value)
Пример #15
0
def test_invalid_config_files():
    ''' Test various error conditions.
    '''

    # Valid configuration file without iteration spaces. We add several
    # iteration spaces to it to test for various error conditions
    # pylint: disable=invalid-name
    # pylint: disable=too-many-statements
    _CONFIG_CONTENT = '''\
    [DEFAULT]
    DEFAULTAPI = dynamo0.3
    DEFAULTSTUBAPI = dynamo0.3
    DISTRIBUTED_MEMORY = true
    REPRODUCIBLE_REDUCTIONS = false
    REPROD_PAD_SIZE = 8
    [gocean1.0]
    '''
    # Create a config files with gocean1.0 section, but an
    # invalid iteration space:
    content = _CONFIG_CONTENT + "iteration-spaces=a:b"
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(new_name)
        assert "An iteration space must be in the form" in str(err)
        assert "But got \"a:b\"" in str(err)

    # Try a multi-line specification to make sure all lines are tested
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:1:2:3:4\n        d:e"
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(new_name)
        assert "An iteration space must be in the form" in str(err)
        assert "But got \"d:e\"" in str(err)

    # Invalid {} expression in first loop bound
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:{X}:2:3:4"
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(new_name)
        assert "Only '{start}' and '{stop}' are allowed as bracketed "\
               "expression in an iteration space." in str(err)
        assert "But got {X}" in str(err)

    # Invalid {} expression in last loop bound:
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:1:2:3:{Y}"
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(new_name)
        assert "Only '{start}' and '{stop}' are allowed as bracketed "\
               "expression in an iteration space." in str(err)
        assert "But got {Y}" in str(err)

    # Add an invalid key:
    content = _CONFIG_CONTENT + "invalid-key=value"
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(new_name)
        assert "Invalid key \"invalid-key\" found in \"{0}\".".\
            format(new_name) in str(err)

        for i in [
                "DEFAULTAPI", "DEFAULTSTUBAPI", "DISTRIBUTED_MEMORY",
                "REPRODUCIBLE_REDUCTIONS"
        ]:
            # They keys are returned in lower case
            assert i.lower() in config.get_default_keys()

    with pytest.raises(InternalError) as err:
        GOLoop.add_bounds(1)
    # Different error message (for type) in python2 vs python3:
    assert "The parameter 'bound_info' must be a string, got '1' "\
           "(type <type 'int'>)" in str(err) or \
           "The parameter 'bound_info' must be a string, got '1' "\
           "(type <class 'int'>)" in str(err)

    # Test syntactically invalid loop boundaries
    with pytest.raises(ConfigurationError) as err:
        GOLoop.add_bounds("offset:field:space:1(:2:3:4")
    assert "Expression '1(' is not a valid do loop boundary" in str(err)
    with pytest.raises(ConfigurationError) as err:
        GOLoop.add_bounds("offset:field:space:1:2:3:4+")
    assert "Expression '4+' is not a valid do loop boundary" in str(err)