Example #1
0
    def update_results_files_xdb(self, results_txt_file):

        self.logger.info("Calling update_results_files_xdb()")

        status = True
        g_component_list = ComputedMetricsSummary.ParseMetaDataFile(
            self.meta_data_file, None, None)

        parse_out_file(results_txt_file, g_component_list, self.logger)

        req_metrics = ComputedMetricsSummary.ParseReqMetricsFile(
            self.requested_metrics, g_component_list)

        for component in g_component_list.values():
            recurse_list(component, g_component_list)

        for component in g_component_list.values():
            for comp in g_component_list.values():
                if component.ComponentID in comp.Children and not comp.IsConfigurationID:
                    # component is actually a child, so parent's metric data
                    # should be updated - provided that child metrics are larger
                    self.logger.debug(comp)
                    if 'FactorOfSafety' in component.MetricsInfo:
                        component.MetricsInfo[
                            'FactorOfSafety'] = comp.MetricsInfo[
                                'FactorOfSafety']
                    if 'VonMisesStress' in component.MetricsInfo:
                        component.MetricsInfo[
                            'VonMisesStress'] = comp.MetricsInfo[
                                'VonMisesStress']
                    break

            if component.CadType == "PART":
                self.logger.info(str(len(component.FEAResults)))
                for name in component.FEAResults.keys():
                    self.logger.info("the key name is " + name)
                fos = float(component.Allowables.mechanical__strength_tensile
                            ) / component.FEAResults["VM"]
                #fos = float(component.MaterialProperty['Mises'])  / component.FEAResults["VM"]
                component.FEAResults['FOS'] = fos
                if 'FactorOfSafety' in component.MetricsInfo:
                    component.MetricsOutput[
                        component.MetricsInfo['FactorOfSafety']] = fos
                if 'VonMisesStress' in component.MetricsInfo:
                    component.MetricsOutput[component.MetricsInfo[
                        'VonMisesStress']] = component.FEAResults["VM"]

        ################  CSV  ###############################
        with open(self.filename + '.csv', 'wb') as f:
            writer = csv.writer(f)
            writer.writerow([
                "Unique ID", "Allowable Stress", "Maximum Stress",
                "Factor of Safety"
            ])
            for component in g_component_list.values():
                if component.CadType == "PART":
                    writer.writerow([
                        component.ComponentID,
                        str(component.Allowables.mechanical__strength_tensile),
                        str(component.FEAResults["VM"]),
                        str(component.FEAResults["FOS"])
                    ])

        ################  Populate Assembly Results  #########
        for component in g_component_list.values():
            self.logger.info('ComponentID: %s' % component.ComponentID)
            self.logger.info(component)
            self.logger.info('')
            if component.CadType == "ASSEMBLY" and not component.IsConfigurationID:
                FOS = []
                VM = []
                for part in component.Children:
                    FOS.append(g_component_list[part].FEAResults["FOS"])
                    VM.append(g_component_list[part].FEAResults["VM"])
                component.FEAResults["FOS"] = min(FOS)
                component.FEAResults["VM"] = max(VM)
                component.MetricsOutput[
                    component.MetricsInfo['FactorOfSafety']] = min(FOS)
                component.MetricsOutput[
                    component.MetricsInfo['VonMisesStress']] = max(VM)

        ################  Populate Metrics  #################
        computed_values_xml = ComputedMetricsSummary.WriteXMLFile(
            g_component_list)

        ################  Update Results Json  ##############
        if os.path.exists(self.results_json):
            UpdateReportJson_CAD.update_manifest(self.results_json,
                                                 computed_values_xml)
        else:
            self.logger.error("Could not update file: " + self.results_json +
                              ", file does not exist.")
            status = False

            self.logger.info(
                "Post Processing Complete, CSV and metrics updated")

        return status
Example #2
0
    def update_results_files_xdb_op2(self, result_txt_file):

        self.logger.info("Enter update_results_files_op2()")

        check_failure_criterion = False

        # Open Nastran_mod_out_v2.txt and read line by line
        self.logger.info("Working with {}".format(result_txt_file))

        comp_id_to_value = {}

        globalMetrics = {}

        with open('Nastran_mod_out_v2.txt', 'r') as tb_file:

            # TODO: Need to see if it has Failure_Criteria, or VM or ???
            # TODO: and handle all those cases

            for line in tb_file.readlines():
                # line example: "FailureIndex,d44a4d66-1560-4634-9d20-c83c063be51d,1.0381887E-005"
                split_line = line.strip('\n').split(',')
                # Note if split_line[1] == this means that it is a global field (e.g. TotalVolume/TotalMass), which
                # does not have a comp_id_to_value
                if (len(split_line[1].strip()) != 0):
                    comp_id_to_value[split_line[1]] = split_line[2]
                    # The first entry will always be 'FailureIndex' ...?
                    if split_line[0] == 'FailureIndex':
                        check_failure_criterion = True
                else:
                    # The following was added to support metrics such as TotalVolume/TotalMass
                    globalMetrics[split_line[0].lower()] = split_line[2]

        if check_failure_criterion:  # this is Failure_Criteria territory; all new code.

            # Open CADAssembly.xml (two directories up), and get ComponentID/MetricID map
            self.logger.info(
                "Getting Component/Metric IDs from CADAssembly.xml")

            metric_id_to_comp_id = {}

            cad_assembly_path = os.path.join('..', '..', 'CADAssembly.xml')

            if os.path.exists(cad_assembly_path):
                cad_assm_tree = letree.parse(cad_assembly_path)
                cad_assm_root = cad_assm_tree.getroot()

                metric_path = "Assembly/Analyses/FEA/Metrics/Metric"
                self.logger.info(
                    "Searching for {} in update_results_files_op2()".format(
                        metric_path))
                metrics = cad_assm_root.findall(metric_path)

                for metric in metrics:
                    metric_id = metric.attrib.get('MetricID')
                    comp_id = metric.attrib.get('ComponentID')
                    metric_id_to_comp_id[metric_id] = comp_id

            # Open TB manifest.json and put the value from component_id_to_value_map into the appropriate Metric
            self.logger.info("Updating Metrics in testbench_manifest.json")

            tb_metrics = None
            tb_manifest_json = None

            with open(self.results_json, 'r') as tb_file:
                tb_manifest_json = json.load(tb_file)

            tb_metrics = tb_manifest_json.get('Metrics')

            for metric in tb_metrics:
                metric_id = metric['ID']
                metrid_name = metric['Name']
                print metrid_name
                print globalMetrics
                if (metrid_name.lower() == "fea_total_volume"
                        or metrid_name.lower() == "fea_total_mass"):
                    if (metrid_name.lower() == "fea_total_volume"):
                        metric_value = globalMetrics.get("TotalVolume".lower())
                    else:
                        metric_value = globalMetrics.get("TotalMass".lower())
                    if metric_value is None:
                        msg = "{} did not contain a Value for metric name for FEA_Total_Volume/FEA_Total_Mass {}.".format(
                        )
                        self.write_failed_and_exit()
                else:
                    associated_comp_id = metric_id_to_comp_id.get(metric_id)
                    metric_value = comp_id_to_value.get(associated_comp_id)
                    if metric_value is None:
                        msg = "{} did not contain a Value for Component ID {}.".format(
                        )
                        self.write_failed_and_exit()

                metric['Value'] = metric_value

            tb_manifest_json['Metrics'] = tb_metrics

            with open(self.results_json, 'w') as tb_file:
                json.dump(tb_manifest_json, tb_file, indent=4)

            return True

        else:  # this is essentially the old method, except that the Nastran_mod_out_v2 allows us to skip a step.

            # TODO This is hacky, but I don't have the time to do it properly. JK 8/19/2016

            g_component_list = ComputedMetricsSummary.ParseMetaDataFile(
                self.meta_data_file, None, None)
            parse_out_file_v2(result_txt_file, g_component_list,
                              self.logger)  # new method, skipping a step

            req_metrics = ComputedMetricsSummary.ParseReqMetricsFile(
                self.requested_metrics, g_component_list)

            for component in g_component_list.values():
                recurse_list(component, g_component_list)

            for component in g_component_list.values():
                for comp in g_component_list.values():
                    if component.ComponentID in comp.Children and not comp.IsConfigurationID:
                        # component is actually a child, so parent's metric data
                        # should be updated - provided that child metrics are larger
                        self.logger.debug(comp)
                        if 'FactorOfSafety' in component.MetricsInfo:
                            component.MetricsInfo[
                                'FactorOfSafety'] = comp.MetricsInfo[
                                    'FactorOfSafety']
                        if 'VonMisesStress' in component.MetricsInfo:
                            component.MetricsInfo[
                                'VonMisesStress'] = comp.MetricsInfo[
                                    'VonMisesStress']
                        break

                if component.CadType == "PART":
                    self.logger.info(str(len(component.FEAResults)))
                    for name in component.FEAResults.keys():
                        self.logger.info("the key name is " + name)
                    fos = float(
                        component.Allowables.mechanical__strength_tensile
                    ) / component.FEAResults["VM"]
                    #fos = float(component.MaterialProperty['Mises'])  / component.FEAResults["VM"]
                    component.FEAResults['FOS'] = fos
                    if 'FactorOfSafety' in component.MetricsInfo:
                        component.MetricsOutput[
                            component.MetricsInfo['FactorOfSafety']] = fos
                    if 'VonMisesStress' in component.MetricsInfo:
                        component.MetricsOutput[component.MetricsInfo[
                            'VonMisesStress']] = component.FEAResults["VM"]

            ################  CSV  ###############################
            with open(self.filename + '.csv', 'wb') as f:
                writer = csv.writer(f)
                writer.writerow([
                    "Unique ID", "Allowable Stress", "Maximum Stress",
                    "Factor of Safety"
                ])
                for component in g_component_list.values():
                    if component.CadType == "PART":
                        writer.writerow([
                            component.ComponentID,
                            str(component.Allowables.
                                mechanical__strength_tensile),
                            str(component.FEAResults["VM"]),
                            str(component.FEAResults["FOS"])
                        ])

            ################  Populate Assembly Results  #########
            for component in g_component_list.values():
                self.logger.info('ComponentID: %s' % component.ComponentID)
                self.logger.info(component)
                self.logger.info('')
                if component.CadType == "ASSEMBLY" and not component.IsConfigurationID:
                    FOS = []
                    VM = []
                    for part in component.Children:
                        FOS.append(g_component_list[part].FEAResults["FOS"])
                        VM.append(g_component_list[part].FEAResults["VM"])
                    component.FEAResults["FOS"] = min(FOS)
                    component.FEAResults["VM"] = max(VM)
                    component.MetricsOutput[
                        component.MetricsInfo['FactorOfSafety']] = min(FOS)
                    component.MetricsOutput[
                        component.MetricsInfo['VonMisesStress']] = max(VM)

            ################  Populate Metrics  #################
            computed_values_xml = ComputedMetricsSummary.WriteXMLFile(
                g_component_list)

            ################  Update Results Json  ##############
            if os.path.exists(self.results_json):
                UpdateReportJson_CAD.update_manifest(self.results_json,
                                                     computed_values_xml)
            else:
                msg = "Could not update file: {}, file does not exist.".format(
                    self.results_json)
                self.logger.error(msg)
                self.write_failed_and_exit(msg)

            #################################################################################
            # Update tb_manifest_json with fea_total_volume and fea_total_mass, if specified
            #################################################################################

            tb_metrics = None
            tb_manifest_json = None

            with open(self.results_json, 'r') as tb_file:
                tb_manifest_json = json.load(tb_file)

            tb_metrics = tb_manifest_json.get('Metrics')

            found_mass_or_volume = False
            for metric in tb_metrics:
                metric_id = metric['ID']
                metrid_name = metric['Name']
                print metrid_name
                print globalMetrics
                if (metrid_name.lower() == "fea_total_volume"
                        or metrid_name.lower() == "fea_total_mass"):
                    found_mass_or_volume = True
                    if (metrid_name.lower() == "fea_total_volume"):
                        metric_value = globalMetrics.get("TotalVolume".lower())
                    else:
                        metric_value = globalMetrics.get("TotalMass".lower())

                    metric['Value'] = metric_value

            if found_mass_or_volume:
                tb_manifest_json['Metrics'] = tb_metrics

                with open(self.results_json, 'w') as tb_file:
                    json.dump(tb_manifest_json, tb_file, indent=4)

            return True
        exit(1)

    return odb, metaDataFile, reqMetricsFile, resultsJson, adamsrun


# ===================================================================================================

# ===================================================================================================
# START
#

if __name__ == '__main__':
    global gVersion
    if not os.path.isdir('log'):
        os.makedirs('log')
    gLogger = cad_library.setuplogger('ABQ_CompletePostProcess')
    odbName, metaDataFilePath, reqMetricsFilePath, resultsJsonPath, adams = ParseArgs(
    )
    componentList = ComputedMetricsSummary.ParseMetaDataFile(
        metaDataFilePath, odbName, adams)
    reqMetrics = ComputedMetricsSummary.ParseReqMetricsFile(
        reqMetricsFilePath, componentList)
    CalculateMetrics(odbName, componentList, reqMetrics)
    computedValuesXml = ComputedMetricsSummary.WriteXMLFile(componentList)
    ComputedMetricsSummary.WriteMetric2File(componentList)
    UpdateReportJson_CAD.update_manifest(resultsJsonPath, computedValuesXml)

    if adams:
        utility_functions.CopyOrDeleteResults(odbName, resultsJsonPath, True)
    else:
        utility_functions.CopyOrDeleteResults(odbName, resultsJsonPath)
        i += 1
        
    if not any([odb, metaDataFile, reqMetricsFile, resultsJson]):
        exit(1)
    
    return odb, metaDataFile, reqMetricsFile, resultsJson, adamsrun
    
    
# ===================================================================================================

# ===================================================================================================
# START
#

if __name__ == '__main__':
    global gVersion
    if not os.path.isdir('log'):
        os.makedirs('log')
    gLogger = cad_library.setuplogger('ABQ_CompletePostProcess')
    odbName, metaDataFilePath, reqMetricsFilePath, resultsJsonPath, adams = ParseArgs()
    componentList = ComputedMetricsSummary.ParseMetaDataFile(metaDataFilePath, odbName, adams)
    reqMetrics = ComputedMetricsSummary.ParseReqMetricsFile(reqMetricsFilePath, componentList)
    CalculateMetrics(odbName, componentList, reqMetrics)     
    computedValuesXml = ComputedMetricsSummary.WriteXMLFile(componentList) 
    ComputedMetricsSummary.WriteMetric2File(componentList)
    UpdateReportJson_CAD.update_manifest(resultsJsonPath, computedValuesXml)
    
    if adams:
        utility_functions.CopyOrDeleteResults(odbName, resultsJsonPath, True)
    else:
        utility_functions.CopyOrDeleteResults(odbName, resultsJsonPath)
Example #5
0
for component in gComponentList.values():
    print component.ComponentID
    print vars(component)
    print ''
    if component.CadType == "ASSEMBLY" and not component.IsConfigurationID:
        FOS = []
        VM = []
        for part in component.Children:
            FOS.append(gComponentList[part].FEAResults["FOS"])
            VM.append(gComponentList[part].FEAResults["VM"])
        component.FEAResults["FOS"] = min(FOS)
        component.FEAResults["VM"] = max(VM)
        component.MetricsOutput[component.MetricsInfo['FactorOfSafety']] = min(FOS)
        component.MetricsOutput[component.MetricsInfo['VonMisesStress']] = max(VM)


################  Populate Metrics  #################
computedValuesXml = ComputedMetricsSummary.WriteXMLFile(gComponentList)


################  Update Results Json  ##############
if (os.path.exists(args.ResultsJson)):
    UpdateReportJson_CAD.update_manifest(args.ResultsJson, computedValuesXml)
else:
    print "ERROR: Could not update file: " + args.ResultsJson + ", file does not exist."

print "Post Processing Complete, CSV and metrics updated"
sys.exit(0)


Example #6
0
    def update_results_files_op2(self, result_txt_file):

        self.logger.info("Enter update_results_files_op2()")

        check_failure_criterion = False

        # Open Nastran_mod_out_v2.txt and read line by line
        self.logger.info("Working with {}".format(result_txt_file))

        comp_id_to_value = {}

        globalMetrics = {}

        with open('Nastran_mod_out_v2.txt', 'r') as tb_file:

            # TODO: Need to see if it has Failure_Criteria, or VM or ???
            # TODO: and handle all those cases

            for line in tb_file.readlines():
                # line example: "FailureIndex,d44a4d66-1560-4634-9d20-c83c063be51d,1.0381887E-005"
                split_line = line.strip('\n').split(',')
                # Note if split_line[1] == this means that it is a global field (e.g. TotalVolume/TotalMass), which
                # does not have a comp_id_to_value
                if ( len(split_line[1].strip()) !=  0 ):
                    comp_id_to_value[split_line[1]] = split_line[2]
                    # The first entry will always be 'FailureIndex' ...?
                    if split_line[0] == 'FailureIndex':
                        check_failure_criterion = True
                else:
                    # The following was added to support metrics such as TotalVolume/TotalMass
                    globalMetrics[split_line[0].lower()] = split_line[2]

        if check_failure_criterion:  # this is Failure_Criteria territory; all new code.

            # Open CADAssembly.xml (two directories up), and get ComponentID/MetricID map
            self.logger.info("Getting Component/Metric IDs from CADAssembly.xml")

            metric_id_to_comp_id = {}

            cad_assembly_path = os.path.join('..', '..', 'CADAssembly.xml')

            if os.path.exists(cad_assembly_path):
                cad_assm_tree = letree.parse(cad_assembly_path)
                cad_assm_root = cad_assm_tree.getroot()

                metric_path = "Assembly/Analyses/FEA/Metrics/Metric"
                self.logger.info("Searching for {} in update_results_files_op2()".format(metric_path))
                metrics = cad_assm_root.findall(metric_path)

                for metric in metrics:
                    metric_id = metric.attrib.get('MetricID')
                    comp_id = metric.attrib.get('ComponentID')
                    metric_id_to_comp_id[metric_id] = comp_id

            # Open TB manifest.json and put the value from component_id_to_value_map into the appropriate Metric
            self.logger.info("Updating Metrics in testbench_manifest.json")

            tb_metrics = None
            tb_manifest_json = None

            with open(self.results_json, 'r') as tb_file:
                tb_manifest_json = json.load(tb_file)


            tb_metrics = tb_manifest_json.get('Metrics')

            for metric in tb_metrics:
                metric_id = metric['ID']
                metrid_name =  metric['Name']
                print metrid_name
                print globalMetrics
                if ( metrid_name.lower() == "fea_total_volume" or metrid_name.lower() == "fea_total_mass" ):
                    if (  metrid_name.lower() == "fea_total_volume" ):
                        metric_value = globalMetrics.get("TotalVolume".lower())
                    else:
                        metric_value = globalMetrics.get("TotalMass".lower())
                    if metric_value is None:
                        msg = "{} did not contain a Value for metric name for FEA_Total_Volume/FEA_Total_Mass {}.".format()
                        self.write_failed_and_exit()
                else:
                    associated_comp_id = metric_id_to_comp_id.get(metric_id)
                    metric_value = comp_id_to_value.get(associated_comp_id)
                    if metric_value is None:
                        msg = "{} did not contain a Value for Component ID {}.".format()
                        self.write_failed_and_exit()

                metric['Value'] = metric_value

            tb_manifest_json['Metrics'] = tb_metrics

            with open(self.results_json, 'w') as tb_file:
                json.dump(tb_manifest_json, tb_file, indent=4)

            return True

        else:  # this is essentially the old method, except that the Nastran_mod_out_v2 allows us to skip a step.

            # TODO This is hacky, but I don't have the time to do it properly. JK 8/19/2016

            g_component_list = ComputedMetricsSummary.ParseMetaDataFile(self.meta_data_file, None, None)
            parse_out_file_v2(result_txt_file, g_component_list, self.logger)  # new method, skipping a step

            req_metrics = ComputedMetricsSummary.ParseReqMetricsFile(self.requested_metrics, g_component_list)

            for component in g_component_list.values():
                recurse_list(component, g_component_list)

            for component in g_component_list.values():
                for comp in g_component_list.values():
                    if component.ComponentID in comp.Children and not comp.IsConfigurationID:
                        # component is actually a child, so parent's metric data
                        # should be updated - provided that child metrics are larger
                        self.logger.debug(comp)
                        if 'FactorOfSafety' in component.MetricsInfo:
                            component.MetricsInfo['FactorOfSafety'] = comp.MetricsInfo['FactorOfSafety']
                        if 'VonMisesStress' in component.MetricsInfo:
                            component.MetricsInfo['VonMisesStress'] = comp.MetricsInfo['VonMisesStress']
                        break

                if component.CadType == "PART":
                    self.logger.info(str(len(component.FEAResults)))
                    for name in component.FEAResults.keys():
                        self.logger.info("the key name is " + name)
                    fos = float(component.Allowables.mechanical__strength_tensile) / component.FEAResults["VM"]
                    #fos = float(component.MaterialProperty['Mises'])  / component.FEAResults["VM"]
                    component.FEAResults['FOS'] = fos
                    if 'FactorOfSafety' in component.MetricsInfo:
                        component.MetricsOutput[component.MetricsInfo['FactorOfSafety']] = fos
                    if 'VonMisesStress' in component.MetricsInfo:
                        component.MetricsOutput[component.MetricsInfo['VonMisesStress']] = component.FEAResults["VM"]

            ################  CSV  ###############################
            with open(self.filename + '.csv', 'wb') as f:
                writer = csv.writer(f)
                writer.writerow(["Unique ID", "Allowable Stress", "Maximum Stress", "Factor of Safety"])
                for component in g_component_list.values():
                    if component.CadType == "PART":
                        writer.writerow([component.ComponentID,
                                         str(component.Allowables.mechanical__strength_tensile),
                                         str(component.FEAResults["VM"]),
                                         str(component.FEAResults["FOS"])])

            ################  Populate Assembly Results  #########
            for component in g_component_list.values():
                self.logger.info('ComponentID: %s' % component.ComponentID)
                self.logger.info(component)
                self.logger.info('')
                if component.CadType == "ASSEMBLY" and not component.IsConfigurationID:
                    FOS = []
                    VM = []
                    for part in component.Children:
                        FOS.append(g_component_list[part].FEAResults["FOS"])
                        VM.append(g_component_list[part].FEAResults["VM"])
                    component.FEAResults["FOS"] = min(FOS)
                    component.FEAResults["VM"] = max(VM)
                    component.MetricsOutput[component.MetricsInfo['FactorOfSafety']] = min(FOS)
                    component.MetricsOutput[component.MetricsInfo['VonMisesStress']] = max(VM)


            ################  Populate Metrics  #################
            computed_values_xml = ComputedMetricsSummary.WriteXMLFile(g_component_list)

            ################  Update Results Json  ##############
            if os.path.exists(self.results_json):
                UpdateReportJson_CAD.update_manifest(self.results_json, computed_values_xml)
            else:
                msg = "Could not update file: {}, file does not exist.".format(self.results_json)
                self.logger.error(msg)
                self.write_failed_and_exit(msg)

            return True
Example #7
0
    def update_results_files_xdb(self, results_txt_file):

        self.logger.info("Calling update_results_files_xdb()")

        status = True
        g_component_list = ComputedMetricsSummary.ParseMetaDataFile(self.meta_data_file, None, None)

        parse_out_file(results_txt_file, g_component_list, self.logger)

        req_metrics = ComputedMetricsSummary.ParseReqMetricsFile(self.requested_metrics, g_component_list)
        
        for component in g_component_list.values():
            recurse_list(component, g_component_list)

        for component in g_component_list.values():
            for comp in g_component_list.values():
                if component.ComponentID in comp.Children and not comp.IsConfigurationID:
                    # component is actually a child, so parent's metric data
                    # should be updated - provided that child metrics are larger
                    self.logger.debug(comp)
                    if 'FactorOfSafety' in component.MetricsInfo:
                        component.MetricsInfo['FactorOfSafety'] = comp.MetricsInfo['FactorOfSafety']
                    if 'VonMisesStress' in component.MetricsInfo:
                        component.MetricsInfo['VonMisesStress'] = comp.MetricsInfo['VonMisesStress']
                    break
                
            if component.CadType == "PART":
                self.logger.info(str(len(component.FEAResults)))
                for name in component.FEAResults.keys():
                    self.logger.info("the key name is " + name)
                fos = float(component.Allowables.mechanical__strength_tensile) / component.FEAResults["VM"]
                #fos = float(component.MaterialProperty['Mises'])  / component.FEAResults["VM"]
                component.FEAResults['FOS'] = fos
                if 'FactorOfSafety' in component.MetricsInfo:
                    component.MetricsOutput[component.MetricsInfo['FactorOfSafety']] = fos
                if 'VonMisesStress' in component.MetricsInfo:
                    component.MetricsOutput[component.MetricsInfo['VonMisesStress']] = component.FEAResults["VM"]
        
        ################  CSV  ###############################
        with open(self.filename + '.csv', 'wb') as f:
            writer = csv.writer(f)
            writer.writerow(["Unique ID", "Allowable Stress", "Maximum Stress", "Factor of Safety"])
            for component in g_component_list.values():
                if component.CadType == "PART":
                    writer.writerow([component.ComponentID,
                                     str(component.Allowables.mechanical__strength_tensile), 
                                     str(component.FEAResults["VM"]), 
                                     str(component.FEAResults["FOS"])])
                    
        ################  Populate Assembly Results  #########
        for component in g_component_list.values():
            self.logger.info('ComponentID: %s' % component.ComponentID)
            self.logger.info(component)
            self.logger.info('')
            if component.CadType == "ASSEMBLY" and not component.IsConfigurationID:
                FOS = []
                VM = []
                for part in component.Children:
                    FOS.append(g_component_list[part].FEAResults["FOS"])
                    VM.append(g_component_list[part].FEAResults["VM"])
                component.FEAResults["FOS"] = min(FOS)
                component.FEAResults["VM"] = max(VM)
                component.MetricsOutput[component.MetricsInfo['FactorOfSafety']] = min(FOS)
                component.MetricsOutput[component.MetricsInfo['VonMisesStress']] = max(VM)


        ################  Populate Metrics  #################
        computed_values_xml = ComputedMetricsSummary.WriteXMLFile(g_component_list)
        
        ################  Update Results Json  ##############
        if os.path.exists(self.results_json):
            UpdateReportJson_CAD.update_manifest(self.results_json, computed_values_xml)
        else:
            self.logger.error("Could not update file: " + self.results_json + ", file does not exist.")
            status = False

            self.logger.info("Post Processing Complete, CSV and metrics updated")

        return status