Ejemplo n.º 1
0
 def v02_flatten_action_count(self, prefix, node_description):
     if 'local' in node_description:
         local_nodes = node_description['local']
         for local_node in local_nodes:
             ASSERT_MSG(
                 "name" in local_node, 'action count error... '
                 'component format violation: "name" needs to be '
                 'specified as a key in node description')
             if prefix is None:
                 full_name = local_node['name']
             else:
                 full_name = prefix + '.' + local_node['name']
             self.action_counts[full_name] = local_node['action_counts']
     if 'subtree' in node_description:
         for subtree_node_description in node_description['subtree']:
             ASSERT_MSG(
                 "name" in subtree_node_description,
                 'action count error... '
                 'component format violation: "name" needs to be '
                 'specified as a key in node description')
             if prefix is None:
                 subtree_prefix = subtree_node_description['name']
             else:
                 subtree_prefix = prefix + '.' + subtree_node_description[
                     'name']
             self.v02_flatten_action_count(subtree_prefix,
                                           subtree_node_description)
Ejemplo n.º 2
0
 def process_name(self, name):
     """
     list component: reformat the component names to their format stored in ERTs
     standalone component: no modifications
     """
     if '[' not in name and ']' not in name:
         ASSERT_MSG(name in self.energy_reference_table,
                    "Cannot find %s's ERT" % name)
         return name
     else:
         new_name = ''
         hierarchical_name_list = name.split('.')
         for name_segment in hierarchical_name_list:
             base_name_segment = EnergyCalculator.belongs_to_list(
                 name_segment)
             new_name += base_name_segment + '.'
         new_name = new_name[:-1]
         if self.flattened_list is not None:
             ASSERT_MSG(
                 new_name in self.flattened_list,
                 "According to the flattened architecture, %s is not a legal name "
                 % name)
             saved_entry = self.flattened_list[new_name]
             for idx in range(len(saved_entry['format'])):
                 range_location = saved_entry['format'][idx]
                 range_min = int(saved_entry['range'][idx][0])
                 range_max = int(saved_entry['range'][idx][1])
                 curr_idx = int(hierarchical_name_list[range_location][hierarchical_name_list[range_location].find('[')+1:\
                                                        hierarchical_name_list[range_location].find(']')])
                 ASSERT_MSG(
                     range_max >= curr_idx >= range_min,
                     'Invalid list component name %s, index out of bound' %
                     name)
         # INFO('list component detected:', name, 'projected into', new_name)
         return new_name
Ejemplo n.º 3
0
    def construct_action_counts(self, action_counts_list):
        if 'version' not in action_counts_list:
            ERROR_CLEAN_EXIT(
                'please specify the version of parser your input format adheres to using '
                '"version" key at top level')

        if action_counts_list['version'] == 0.1:
            raw_action_counts = action_counts_list['nodes']
            if 'nodes' not in action_counts_list:
                ERROR_CLEAN_EXIT(
                    'v0.1 error: action counts...\n Tree nodes should be the value of top level key "nodes", '
                    '"nodes" not found at top-level')
            if not len(raw_action_counts) == 1:
                ERROR_CLEAN_EXIT(
                    'the first level list of your action counts should only have one node, '
                    'which is your design\'s root node')
            self.design_name = raw_action_counts[0]['name']
            for node_description in raw_action_counts[0]['nodes']:
                self.v01_flatten_action_count(self.design_name,
                                              node_description)

        if action_counts_list['version'] == 0.2:
            ASSERT_MSG(
                'subtree' in action_counts_list,
                'v0.2 error: action counts... \n'
                'the action counts mut contain the "subtree" key at the top level'
            )
            raw_action_counts = action_counts_list['subtree']
            ASSERT_MSG(
                len(raw_action_counts) == 1, 'v0.2 error: action counts... \n'
                'the first level list of your action counts should only have one node, '
                'which is your design\'s root node')
            self.design_name = action_counts_list['subtree'][0]['name']
            self.v02_flatten_action_count(self.design_name,
                                          action_counts_list['subtree'][0])
Ejemplo n.º 4
0
def v02_validate_top_level_architecture_description(architecture_description):
    INFO("architecture description file version: 0.2, checking syntax...")
    if 'subtree' not in architecture_description:
        ERROR_CLEAN_EXIT(
            'v0.2 error: architecture description...',
            'Architecture tree nodes should be the value of top level key "subtree", '
            '"subtree" not found at top-level')
    # top-level subtree should only have one node
    ASSERT_MSG((
        isinstance(architecture_description['subtree'], list)
        and len(architecture_description['subtree']) == 1
    ), 'v0.2 error: top-level subtree must be a single element list that contains the design name'
               )
    if 'local' in architecture_description:
        ERROR_CLEAN_EXIT(
            'v0.2 error: architecture description...',
            'The first level list of your architecture description should only have a subtree, '
            'which is your design\'s root node')

    # design name syntax check
    raw_architecture_description = architecture_description['subtree'][0]
    if 'name' not in raw_architecture_description:
        ERROR_CLEAN_EXIT(
            "v0.2 error: architecture description...\n"
            "please specify the design name as top-level key-value pair =>  name: <design_name>"
        )
    design_name = raw_architecture_description['name']
    if 'local' in raw_architecture_description:
        ASSERT_MSG(
            isinstance(raw_architecture_description['local'], list),
            "v0.2 error: %s.local has to be a list of components" %
            design_name)
    if 'subtree' in raw_architecture_description:
        ASSERT_MSG(isinstance(raw_architecture_description['subtree'], list),
                   "v0.2 error: %s.subtree has to be a list" % design_name)
Ejemplo n.º 5
0
def v02_tree_node_classification(self, node_description, prefix, node_attrs):
    if 'subtree' in node_description:
        ASSERT_MSG(isinstance(node_description['subtree'], list),
                   "v0.2 error: %s.subtree has to be a list" % prefix)
        self.flatten_architecture_subtree(prefix, node_description['subtree'],
                                          node_attrs)

    if 'local' in node_description:
        ASSERT_MSG(
            isinstance(node_description['local'], list),
            "v0.2 error: %s.local has to be a list of components" % prefix)
        for c_id in range(len(node_description['local'])):
            local_node_list_length, local_node_name_base = v02_is_component_list(
                node_description['local'][c_id]['name'], node_attrs)
            if not local_node_list_length == 0:
                for local_node_list_item_idx in range(local_node_list_length):
                    local_node_name = prefix + '.' + local_node_name_base + '[' + str(
                        local_node_list_item_idx) + ']'
                    self.construct_new_leaf_node_description(
                        local_node_name, node_description['local'][c_id],
                        node_attrs)
            else:
                local_node_name = prefix + '.' + node_description['local'][
                    c_id]['name']
                self.construct_new_leaf_node_description(
                    local_node_name, node_description['local'][c_id],
                    node_attrs)
    if 'subtree' not in node_description and 'local' not in node_description:
        ERROR_CLEAN_EXIT('v0.2 error: architecture description...',
                         'Unrecognized tree node type', node_description)
Ejemplo n.º 6
0
 def extract_ERT(self, raw_ERT):
     if 'version' not in raw_ERT:
         self.energy_reference_table = raw_ERT
     else:
         ASSERT_MSG(
             'version' in raw_ERT,
             'v>=0.2 error: ERT ... \n ERT must contain "version" key')
         ASSERT_MSG(
             'tables' in raw_ERT,
             'v>=0.2 error: ERT ... \n ERT must contain "tables" key')
         ERT_version = raw_ERT['version']
         if ERT_version <= 0.2:
             self.energy_reference_table = raw_ERT['tables']
         else:
             ERROR_CLEAN_EXIT('ERT version: ', ERT_version,
                              'no parser available...')
Ejemplo n.º 7
0
 def eval_primitive_action_energy(self, estimator_plug_in_interface):
     """
     :param estimator_plug_in_interface: dictionary that adheres to
            Accelergy-external estimator interface format
     :return energy estimation of the action
     """
     best_accuracy = 0
     best_estimator = None
     for estimator in self.estimator_plug_ins:
         accuracy = estimator.primitive_action_supported(
             estimator_plug_in_interface)
         ASSERT_MSG(
             type(accuracy) is int or type(accuracy) is float,
             'Wrong plug-in accuracy: %s ...  Returned accuracy must be integers or floats'
             % (estimator))
         if accuracy > best_accuracy:
             best_accuracy = accuracy
             best_estimator = estimator
     if best_estimator is None:
         ERROR_CLEAN_EXIT('cannot find estimator plug-in:',
                          estimator_plug_in_interface,
                          'Available plug-ins:', self.estimator_plug_ins)
     energy = round(
         best_estimator.estimate_energy(estimator_plug_in_interface),
         self.decimal_place)
     if self.verbose:
         INFO('Received energy estimation for primitive class:\n',
              estimator_plug_in_interface, '\n estimated by:',
              best_estimator, ' ---> estimated energy:', str(energy), 'pJ')
     return energy
Ejemplo n.º 8
0
def flatten_architecture_subtree(
        self,
        prefix,
        subtree_description,
        shared_attributes_dict=None):  # For version 0.2
    ASSERT_MSG(
        isinstance(subtree_description, list),
        'v0.2 error: architecture description...\n %s.subtree needs to be a list'
        % prefix)
    for subtree_item_description in subtree_description:
        if 'name' not in subtree_item_description:
            ERROR_CLEAN_EXIT(
                'v0.2 error: architecture description...',
                ' "name" needs to be specified as a key in node description',
                subtree_item_description)

        if shared_attributes_dict is None and 'attributes' not in subtree_item_description:
            node_attrs = None
        elif shared_attributes_dict is not None and 'attributes' not in subtree_item_description:
            node_attrs = deepcopy(shared_attributes_dict)
        elif shared_attributes_dict is None and 'attributes' in subtree_item_description:
            node_attrs = subtree_item_description['attributes']
        else:  #shared_attributes_dict is not None and attributes in node_description
            node_attrs = deepcopy(shared_attributes_dict)
            node_attrs.update(subtree_item_description['attributes'])

        node_name = subtree_item_description['name']
        # determine if the component is in list format
        list_length, name_base = v02_is_component_list(node_name,
                                                       shared_attributes_dict)
        # if the component is in list format, flatten out and create the instances
        if not list_length == 0:
            for item_idx in range(list_length):
                item_prefix = prefix + '.' + name_base + '[' + str(
                    item_idx) + ']'
                self.v02_tree_node_classification(subtree_item_description,
                                                  item_prefix, node_attrs)

        # if the component is a standalone component, parse the component description directly
        else:
            item_prefix = prefix + '.' + node_name
            self.v02_tree_node_classification(subtree_item_description,
                                              item_prefix, node_attrs)
Ejemplo n.º 9
0
    def interpret_input_path(self, file_path):
        file = load(open(file_path), accelergy_loader)
        for key in file:
            if key == 'architecture':
                content = file[key]
                ASSERT_MSG(
                    'version' in content and 'subtree' in content,
                    'File content not legal: %s, architecture description must contain '
                    '"version" and "subtree" keys' % file_path)
                ASSERT_MSG(
                    type(content['subtree'] is dict),
                    'File content not legal: %s, "subtree" key must have value of type dict'
                    % file_path)
                if self.raw_architecture_description is None:
                    self.raw_architecture_description = file[key]
                else:
                    ASSERT_MSG(
                        self.raw_architecture_description['version'] ==
                        content['version'],
                        'File content not legal: %s, Versions of two architecture description '
                        'related file do not match' % file_path)
                    self.raw_architecture_description.update(
                        file[key]['subtree'])

            if key == 'compound_components':
                content = file[key]
                ASSERT_MSG(
                    'version' in content and 'classes' in content,
                    'File content not legal: %s, component class description must contain '
                    '"version" and "classes" keys' % file_path)
                ASSERT_MSG(
                    type(content['classes'] is list),
                    'File content not legal: %s, "classes" key must have value of type list'
                    % file_path)
                if self.raw_compound_class_description is None:
                    self.raw_compound_class_description = file[key]
                else:
                    ASSERT_MSG(
                        self.raw_compound_class_description['version'] ==
                        content['version'],
                        'File content not legal: %s, Versions of two compound class description '
                        'related file do not match' % file_path)
                    self.raw_compound_class_description['classes'].append(
                        file[key]['classes'])
Ejemplo n.º 10
0
def interpret_input_path(path_arglist):
    raw_architecture_description = None
    raw_compound_class_description = None
    raw_action_counts = None
    raw_ERT = None
    raw_flattened_arch = None

    available_keys = [
        'architecture', 'compound_components', 'action_counts', 'ERT',
        'flattened_architecture'
    ]

    for file_path in path_arglist:

        file = load(open(file_path), accelergy_loader)
        for key in file:

            if key == 'architecture':
                content = file[key]
                #-------------------------------------------------------------------------
                # check syntax of input file
                #-------------------------------------------------------------------------
                ASSERT_MSG(
                    'version' in content,
                    'File content not legal: %s, %s must contain '
                    '"version" key' % (file_path, key))
                tree_root_name = None
                if content['version'] == 0.1:
                    tree_root_name = 'nodes'
                if content['version'] == 0.2:
                    tree_root_name = 'subtree'
                ASSERT_MSG(
                    tree_root_name is not None, 'File content not legal: %s, '
                    'version %s not supported' %
                    (file_path, content['version']))
                ASSERT_MSG(
                    tree_root_name in content,
                    'File content not legal: %s, architecture description must contain %s key'
                    % (file_path, tree_root_name))
                ASSERT_MSG(
                    type(content[tree_root_name]) is list,
                    'File content not legal: %s, %s key must have value of type list'
                    % (file_path, tree_root_name))
                #-------------------------------------------------------------------------
                # merge input file
                #-------------------------------------------------------------------------
                if raw_architecture_description is None:
                    raw_architecture_description = {'architecture': file[key]}
                else:
                    ERROR_CLEAN_EXIT(
                        'File content not legal: %s. '
                        '\n Second architecture description detected... '
                        'Only one architecture is allowed...' % (file_path))
                    # ASSERT_MSG(raw_architecture_description['architecture']['version'] == content['version'],
                    #            'File content not legal: %s, versions of two %s'
                    #            'related file do not match'%(file_path, key))
                    # raw_architecture_description[key].update(file[key])
            if key == 'compound_components':
                content = file[key]
                file_reload = load(open(file_path), accelergy_loader_ordered)
                #-------------------------------------------------------------------------
                # check syntax of input file
                #-------------------------------------------------------------------------
                ASSERT_MSG(
                    'version' in content and 'classes' in content,
                    'File content not legal: %s, %s must contain '
                    '"version" and "classes" keys' % (file_path, key))
                ASSERT_MSG(
                    type(content['classes']) is list,
                    'File content not legal: %s, "classes" key must have value of type list'
                    % file_path)
                #-------------------------------------------------------------------------
                # merge input file
                #-------------------------------------------------------------------------
                if raw_compound_class_description is None:
                    raw_compound_class_description = {
                        'compound_components': file_reload[key]
                    }
                else:
                    ASSERT_MSG(
                        raw_compound_class_description['compound_components']
                        ['version'] == content['version'],
                        'File content not legal: %s, versions of two %s '
                        'related file do not match' % (file_path, key))

                    raw_compound_class_description[key]['classes'].extend(
                        file_reload[key]['classes'])

            if key == 'action_counts':
                content = file[key]
                #-------------------------------------------------------------------------
                # check syntax of input file
                #-------------------------------------------------------------------------
                ASSERT_MSG(
                    'version' in content,
                    'File content not legal: %s, %s must contain '
                    '"version" key ' % (file_path, key))
                if content['version'] == 0.1:
                    ASSERT_MSG(
                        'nodes' in content,
                        'v0.1 error... File content not legal: %s, %s must contain '
                        '"nodes" key ' % (file_path, key))
                    if raw_action_counts is None:
                        raw_action_counts = {key: content}
                    else:
                        ASSERT_MSG(
                            raw_action_counts[key]['version'] ==
                            content['version'],
                            'File content not legal: %s, versions of two %s'
                            'related files do not match' % (file_path, key))
                        if "nodes" in content:
                            if "nodes" in raw_action_counts[key]:
                                raw_action_counts[key]["nodes"].extend(
                                    content["nodes"])
                            else:
                                raw_action_counts[key]["nodes"] = content[
                                    "nodes"]

                if content['version'] == 0.2:

                    ASSERT_MSG(
                        'local' in content or 'subtree' in content,
                        'File content not legal: %s, %s must contain '
                        '"local"/"subtree" key ' % (file_path, key))

                    if "local" in content:
                        ASSERT_MSG(
                            type(content["local"]) is list,
                            'File content not legal: %s, "local" key must have value of type list'
                            % (file_path))

                    if "subtree" in content:
                        ASSERT_MSG(
                            type(content["subtree"]) is list,
                            'File content not legal: %s, "subtree" key must have value of type list'
                            % (file_path))

                    if raw_action_counts is None:
                        raw_action_counts = {key: content}
                    else:
                        ASSERT_MSG(
                            raw_action_counts[key]['version'] ==
                            content['version'],
                            'File content not legal: %s, versions of two %s'
                            'related files do not match' % (file_path, key))
                        if "local" in content:
                            if "local" in raw_action_counts[key]:
                                raw_action_counts[key]["local"].extend(
                                    content["local"])
                            else:
                                raw_action_counts[key]["local"] = content[
                                    "local"]
                        if "subtree" in content:
                            if "subtree" in raw_action_counts[key]:
                                raw_action_counts[key]["subtree"].extend(
                                    content["subtree"])
                            else:
                                raw_action_counts[key]["subtree"] = content[
                                    "subtree"]

            if key == 'ERT':
                content = file[key]
                ASSERT_MSG(
                    'version' in content and 'tables' in content,
                    'File content not legal: %s, ERT must contain '
                    '"version" and "tables" keys' % file_path)
                ASSERT_MSG(
                    type(content['tables'] is dict),
                    'File content not legal: %s, "tables" key must have value of type dict'
                    % file_path)
                if raw_ERT is None:
                    raw_ERT = {key: file[key]}
                else:
                    ASSERT_MSG(
                        raw_ERT[key]['version'] == content['version'],
                        'File content not legal: %s, versions of two %s '
                        'related files do not match' % (file_path, key))
                    raw_ERT[key]['tables'].update(file[key]['tables'])

            if key == 'flattened_architecture':
                content = file[key]
                ASSERT_MSG(
                    'version' in content and 'components' in content,
                    'File content not legal: %s, flattened_architecture description must contain '
                    '"version" and "tables" keys' % file_path)
                ASSERT_MSG(
                    type(content['components'] is dict),
                    'File content not legal: %s, "components" key must have value of type dict'
                    % file_path)
                if raw_flattened_arch is None:
                    raw_flattened_arch = {key: file[key]}
                else:
                    ASSERT_MSG(
                        raw_ERT['version'] == content['version'],
                        'File content not legal: %s, versions of two %s '
                        'related files do not match' % (file_path, key))
                    raw_flattened_arch[key]['components'].update(
                        file[key]['components'])
            if key not in available_keys:
                WARN('File contains unrecognized information:', file_path, key,
                     '\n Accelergy only recognizes the following keys',
                     available_keys)
            else:
                INFO('%s related info found in %s' % (key, file_path))

    return raw_architecture_description, raw_compound_class_description, raw_action_counts, raw_ERT, raw_flattened_arch
Ejemplo n.º 11
0
    def generate_ERTs(self, raw_architecture_description,
                      raw_compound_class_description, output_path, precision,
                      flatten_arch_flag, verbose, show_ERT_summary):
        """
        main function to start the energy reference generator
        parses the input files
        produces the energy reference tables for components
        """
        print('\n=========================================')
        print('Generating energy reference tables')
        print('=========================================')

        # Interpret inputs
        self.verbose = verbose
        self.output_path = output_path
        self.decimal_place = precision
        self.flattened_arch = flatten_arch_flag
        self.show_ERT_summary = show_ERT_summary

        # Load accelergy config
        self.config = config_file_checker()

        # interpret the list of files
        # for file_path in path_arglist:
        #     self.interpret_input_path(file_path)
        #     INFO('Input file parsed: ', file_path)
        self.raw_architecture_description = raw_architecture_description[
            'architecture']
        self.raw_compound_class_description = raw_compound_class_description[
            'compound_components']

        # self.design = load(open(self.design_path), accelergy_loader)
        # INFO('design loaded:', design_path)
        self.construct_compound_class_description()

        # Load the primitive classes library
        self.construct_primitive_class_description()
        ASSERT_MSG(
            not len(self.primitive_class_description) == 0,
            'No primitive component class found, '
            'please check if the paths in config file are correct')

        # Parse the architecture description and save the parsed version if flag high
        self.construct_save_architecture_description()

        # Instantiate the estimation plug-ins as intances of the corresponding plug-in classes
        self.instantiate_estimator_plug_ins()
        ASSERT_MSG(
            not len(self.estimator_plug_ins) == 0,
            'No estimation plug-in found, '
            'please check if the paths in config file are correct')

        # Generate Energy Reference Tables for the components
        self.generate_ERTs_for_architecture()

        if self.flattened_arch or self.show_ERT_summary:
            self.generate_easy_to_read_flattened_architecture_ERT_summary()

        # Save the ERTs to ERT.yaml in the output directory
        ERT_file_path = self.output_path + '/' + 'ERT.yaml'
        ERT_dict = {
            'ERT': {
                'version': self.arch_version,
                'tables': self.energy_reference_table
            }
        }
        write_yaml_file(ERT_file_path, ERT_dict)
        print('---> Finished: ERT generation finished, ERT saved to:\n',
              os.path.abspath(output_path))
Ejemplo n.º 12
0
    def generate_component_ert(self, component_info, is_primitive_class):
        """
        According to component type, processes the received information differently

        primitive component:
            - base case of this function
            - apply default attributes, directly chooses the best estimator and generates ERT

        compound component: parse the subcomponet information and evaluate the ERT for the subcomponent actions first

            - top-level compound component with actions
                1. no argument, generate for the action by parsing its definition and recursively call this function
                2. argument ranges: generate energy/action for one possible argument value(s) a time,
                                    and loops through all the possibilities

            - low-level compound components
              1. no argument, generate for the action by parsing its definition and recursively call this function
              2. argument values: since it is low-level, its argument values should already be defined by the
                                  high-level compound component's action argument value

        :param component_info:  dictionary the physical information about a component
        :param is_primitive_class: boolean whether this component is primitive
        :return: ERT for the component
        """

        # case 1: if is_primitive_class: directly call energy estimators
        if is_primitive_class:
            primitive_class_name = component_info['class']
            ERT = {}  # energy reference table for this class
            class_description = self.primitive_class_description[
                primitive_class_name]
            if 'actions' not in component_info:
                action_list = class_description['actions']
            else:
                action_list = component_info['actions']

            # apply the default primitive attribute values
            if 'attributes' not in component_info:
                component_info['attributes'] = {}
            for attr_name, attr_default in class_description[
                    'attributes'].items():
                if attr_name not in component_info['attributes']:
                    component_info['attributes'].update(
                        {attr_name: attr_default})

            for action in action_list:
                ERT[action['name']] = self.construct_interface_and_estimate(
                    action, component_info)
            return ERT

        # case 2: if not is_primitive_class: parse the compound class information
        else:
            compound_component_ERT = {}
            compound_class_name = component_info['class']
            compound_component_attributes = component_info['attributes']
            ASSERT_MSG(
                compound_class_name in self.compound_class_description,
                'Cannot find class definition: %s' % compound_class_name)
            compound_class_description = self.compound_class_description[
                compound_class_name]
            if 'subcomponents' not in compound_class_description:
                ERROR_CLEAN_EXIT(
                    'compound class description missing subcomponents:',
                    compound_class_name)
            defined_component = getattr(
                self, self.compound_component_constructor)(component_info)
            # generate ERTs for the compound actions
            compound_actions = deepcopy(defined_component['actions'])
            for action in compound_actions:
                compound_action_name = action['name']
                if 'arguments' in action and action['arguments'] is not None:
                    compound_arg_name, compound_arg_info = list(
                        action['arguments'].items())[0]
                    if type(compound_arg_info) is int:
                        argument_value_provided = True
                    else:
                        argument_value_provided = False

                    # if arguments are ranges, then this is the top level compound component
                    if not argument_value_provided:
                        # action is a list of energy values
                        action_ERT = self.initialize_ERT_for_action_with_arg_ranges(
                            action)
                        # for each arg values combo for the compound component
                        for arg_val_combo in action_ERT:

                            if self.compound_class_version >= 0.2:
                                action_def_checker = 'v' + str(self.compound_class_version).replace('.', '') \
                                                     + '_check_subcomponent_name_in_action_def'
                                binding_dict = deepcopy(
                                    compound_component_attributes)
                                binding_dict.update(arg_val_combo['arguments'])
                                action = getattr(self, action_def_checker)(
                                    action,
                                    defined_component['subcomponents'].keys(),
                                    binding_dict)

                            # for each subcomponent involved in action definition
                            for subcomponent_action in action['subcomponents']:
                                subcomponent_name = subcomponent_action['name']
                                # retrieve hardware info from the updated subcomponent list
                                subcomponent_info = defined_component[
                                    'subcomponents'][subcomponent_name]
                                # for each action that is related to this subcomponent
                                for subaction in subcomponent_action[
                                        'actions']:
                                    subaction_energy = self.eval_subcomponent_action_for_ERT(
                                        subaction, subcomponent_info,
                                        arg_val_combo['arguments'],
                                        compound_component_attributes)
                                    arg_val_combo['energy'] = round(
                                        arg_val_combo['energy'] +
                                        subaction_energy, self.decimal_place)

                    # if arguments are static values, then this is a compound component that is subcomponent of another compound component
                    else:
                        action_ERT = {
                            'energy': 0,
                            'arguments': action['arguments']
                        }
                        if self.compound_class_version >= 0.2:
                            action_def_checker = 'v' + str(self.compound_class_version).replace('.', '') \
                                                 + '_check_subcomponent_name_in_action_def'
                            binding_dict = deepcopy(
                                compound_component_attributes)
                            binding_dict.update(action['arguments'])
                            action = getattr(self, action_def_checker)(
                                action,
                                defined_component['subcomponents'].keys(),
                                binding_dict)
                        for subcomponent_action in action[
                                'subcomponents']:  # for each subcomponent involved in action definition
                            subcomponent_name = subcomponent_action['name']
                            subcomponent_info = defined_component[
                                'subcomponents'][
                                    subcomponent_name]  # retrieve hardware info from the updated subcomponent list
                            for subaction in subcomponent_action[
                                    'actions']:  # for each action that is related to this subcomponent
                                subaction_energy = self.eval_subcomponent_action_for_ERT(
                                    subaction, subcomponent_info,
                                    action['arguments'],
                                    compound_component_attributes)
                                action_ERT['energy'] = round(
                                    action_ERT['energy'] + subaction_energy,
                                    self.decimal_place)

                else:
                    # if the compound action has no arguments
                    action_ERT = {'energy': 0, 'arguments': None}
                    if self.compound_class_version >= 0.2:
                        action_def_checker = 'v' + str(self.compound_class_version).replace('.', '') \
                                             + '_check_subcomponent_name_in_action_def'
                        binding_dict = deepcopy(compound_component_attributes)
                        action = getattr(self, action_def_checker)(
                            action, defined_component['subcomponents'].keys(),
                            binding_dict)
                    subaction_energy = 0
                    for subcomponent_action in action[
                            'subcomponents']:  # for each subcomponent involved in action definition
                        subcomponent_name = subcomponent_action['name']
                        subcomponent_info = defined_component['subcomponents'][
                            subcomponent_name]  # retrieve hardware info from the updated subcomponent list
                        is_primitive_class = self.is_primitive_class(
                            subcomponent_info['class'])
                        if is_primitive_class:
                            sub_class_info = self.primitive_class_description[
                                subcomponent_info['class']]
                        else:
                            sub_class_info = self.compound_class_description[
                                subcomponent_info['class']]

                        for subaction in subcomponent_action[
                                'actions']:  # for each action that is related to this subcomponent
                            for class_action in sub_class_info['actions']:
                                if class_action['name'] == subaction['name']:
                                    if 'arguments' in class_action:
                                        ERROR_CLEAN_EXIT(
                                            subaction, ' from class: ',
                                            subcomponent_info['name'],
                                            ' has no argument values specified from upper level class when used as a subcomponent for compound calss: ',
                                            compound_class_name)
                            subaction_energy = self.eval_subcomponent_action_for_ERT(
                                subaction, subcomponent_info, None,
                                compound_component_attributes)
                            action_ERT['energy'] = round(
                                action_ERT['energy'] + subaction_energy,
                                self.decimal_place)

                # record the generated ERT for the compound component
                compound_component_ERT[compound_action_name] = deepcopy(
                    action_ERT)
            return compound_component_ERT