{ "cells": [ { "cell_type": "markdown", "id": "a0cb2cb2", "metadata": {}, "source": [ "# Performing a Part Compliance Query" ] }, { "cell_type": "markdown", "id": "e0cc9c73", "metadata": {}, "source": [ "A Part Compliance Query determines whether one or more parts are compliant with the specified indicators. This is\n", "done by first finding all substances directly or indirectly associated with that part, determining compliance for\n", "those substances, and then rolling up the results to the material." ] }, { "cell_type": "markdown", "id": "49483a9d", "metadata": {}, "source": [ "## Connecting to Granta MI" ] }, { "cell_type": "markdown", "id": "d8dfde64", "metadata": {}, "source": [ "Import the ``Connection`` class and create the connection. See the [Getting Started](../0_Getting_started.ipynb)\n", "example for more details." ] }, { "cell_type": "code", "execution_count": null, "id": "875a4593", "metadata": { "tags": [] }, "outputs": [], "source": [ "from ansys.grantami.bomanalytics import Connection\n", "\n", "server_url = \"http://my_grantami_server/mi_servicelayer\"\n", "cxn = Connection(server_url).with_credentials(\"user_name\", \"password\").connect()" ] }, { "cell_type": "markdown", "id": "1f0fed7d", "metadata": {}, "source": [ "## Defining an Indicator" ] }, { "cell_type": "markdown", "id": "335636f3", "metadata": {}, "source": [ "A Compliance query determines compliance against 'Indicators', as opposed to an Impacted Substances query which\n", "determines compliance directly against legislations.\n", "\n", "There are two types of Indicator object (``WatchListIndicator`` and ``RohsIndicator``), and the syntax presented below\n", "applies to both. The differences in the internal implementation of the two objects are described in the API\n", "documentation.\n", "\n", "Generally speaking, if a substance is impacted by a legislation associated with an indicator, and in a quantity\n", "above a specified threshold, the substance is non-compliant with that indicator. This non-compliance applies to\n", "any other items in the BoM hierarchy that directly or indirectly include that substance." ] }, { "cell_type": "markdown", "id": "c36b34b0", "metadata": {}, "source": [ "First, create two ``WatchListIndicator`` objects." ] }, { "cell_type": "code", "execution_count": null, "id": "e2bc2923", "metadata": { "tags": [] }, "outputs": [], "source": [ "from ansys.grantami.bomanalytics import indicators\n", "\n", "svhc = indicators.WatchListIndicator(\n", " name=\"SVHC\",\n", " legislation_ids=[\"Candidate_AnnexXV\"],\n", " default_threshold_percentage=0.1,\n", ")\n", "sin = indicators.WatchListIndicator(\n", " name=\"SIN\",\n", " legislation_ids=[\"SINList\"]\n", ")" ] }, { "cell_type": "markdown", "id": "6517c56e", "metadata": { "tags": [] }, "source": [ "## Building and Running the Query" ] }, { "cell_type": "markdown", "id": "ae3f4e1d", "metadata": {}, "source": [ "Next define the query itself. Parts can be referenced by Granta MI record reference or Part Number. The\n", "table containing the Part records is not required, since this is enforced by the Restricted Substances database\n", "schema." ] }, { "cell_type": "code", "execution_count": null, "id": "5679581a", "metadata": { "tags": [] }, "outputs": [], "source": [ "from ansys.grantami.bomanalytics import queries\n", "\n", "part_query = (\n", " queries.PartComplianceQuery()\n", " .with_part_numbers([\"asm_flap_mating\", \"DRILL\"])\n", " .with_indicators([svhc])\n", ")" ] }, { "cell_type": "markdown", "id": "64ab3182", "metadata": {}, "source": [ "Finally, run the query. Passing a ``PartComplianceQuery`` object to the ``Connection.run()`` method returns a\n", "``PartComplianceQueryResult`` object." ] }, { "cell_type": "code", "execution_count": null, "id": "bbc0ff24", "metadata": { "tags": [] }, "outputs": [], "source": [ "part_result = cxn.run(part_query)\n", "part_result" ] }, { "cell_type": "markdown", "id": "5a223418", "metadata": { "tags": [] }, "source": [ "The result object contains two properties, ``compliance_by_part_and_indicator`` and ``compliance_by_indicator``." ] }, { "cell_type": "markdown", "id": "73e774ac", "metadata": {}, "source": [ "## Results Grouped by Part" ] }, { "cell_type": "markdown", "id": "0265b7e8", "metadata": { "tags": [] }, "source": [ "``compliance_by_part_and_indicator`` contains a list of ``PartWithComplianceResult`` objects with the\n", "reference to the part record and the compliance status for each indicator.\n", "\n", "In Granta MI, Parts can link to the following record types:\n", "\n", "- Parts\n", "- Specifications (which can link to Specifications, Materials, Substances, and Coatings)\n", "- Materials (which can link to Substances)\n", "- Substances\n", "\n", "Because compliance of a part is determined based on the compliance of the items that the record is linked to, the\n", "corresponding ``ResultWithCompliance`` objects are included in the parent ``PartWithComplianceResult``, each with\n", "their own compliance status." ] }, { "cell_type": "markdown", "id": "b5f5fd63", "metadata": {}, "source": [ "Since we specified two part records, we have received two result objects back. In this example, we will only look in\n", "more detail at results for the wing flap assembly." ] }, { "cell_type": "code", "execution_count": null, "id": "8f32e56b", "metadata": { "tags": [] }, "outputs": [], "source": [ "wing = part_result.compliance_by_part_and_indicator[0]\n", "print(f\"Wing compliance status: {wing.indicators['SVHC'].flag.name}\")" ] }, { "cell_type": "markdown", "id": "027cad61", "metadata": {}, "source": [ "This tells us that the wing flap assembly contains an SVHC above the 0.1% threshold." ] }, { "cell_type": "markdown", "id": "7b613fe9", "metadata": {}, "source": [ "We can print the parts below this part that also contain an SVHC above the threshold. The parts referenced by the\n", "``wing`` part are available in the ``parts`` property." ] }, { "cell_type": "code", "execution_count": null, "id": "7ffdf644", "metadata": { "tags": [] }, "outputs": [], "source": [ "above_threshold_flag = svhc.available_flags.WatchListAboveThreshold\n", "parts_contain_svhcs = [part for part in wing.parts\n", " if part.indicators[\"SVHC\"] >= above_threshold_flag]\n", "print(f\"{len(parts_contain_svhcs)} parts that contain SVHCs\")\n", "for part in parts_contain_svhcs:\n", " print(f\"Part: {part.record_history_identity}\")" ] }, { "cell_type": "markdown", "id": "9dd7bbd7", "metadata": { "lines_to_next_cell": 2 }, "source": [ "This process can be performed recursively to show a structure of each part that contains SVHCs either directly or\n", "indirectly. The cells below implement the code above in a function that can be called recursively, and then call it\n", "on the wing flap assembly." ] }, { "cell_type": "code", "execution_count": null, "id": "3e165aee", "metadata": { "tags": [] }, "outputs": [], "source": [ "def recursively_print_parts_with_svhcs(parts, depth=0):\n", " parts_contain_svhcs = [part for part in parts\n", " if part.indicators[\"SVHC\"] >= above_threshold_flag]\n", " for part in parts_contain_svhcs:\n", " print(f\"{' '*depth}- Part: {part.record_history_identity}\")\n", " recursively_print_parts_with_svhcs(part.parts, depth + 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "463515d2", "metadata": { "tags": [] }, "outputs": [], "source": [ "recursively_print_parts_with_svhcs(wing.parts)" ] }, { "cell_type": "markdown", "id": "23b48631", "metadata": { "lines_to_next_cell": 2 }, "source": [ "This can be extended further to include all possible BoM components in the recursive iteration, including\n", "specifications, coatings, and substances." ] }, { "cell_type": "code", "execution_count": null, "id": "36129b32", "metadata": { "tags": [] }, "outputs": [], "source": [ "def recursively_print_parts_with_svhcs(parts, depth=0):\n", " parts_contain_svhcs = [part for part in parts\n", " if part.indicators[\"SVHC\"] >= above_threshold_flag]\n", " for part in parts_contain_svhcs:\n", " print(f\"{' '*depth}- Part: {part.record_history_identity}\")\n", " recursively_print_parts_with_svhcs(part.parts, depth + 1)\n", " print_materials_with_svhcs(part.materials, depth + 1)\n", " print_specifications_with_svhcs(part.specifications, depth + 1)\n", " print_substances_with_svhcs(part.substances, depth + 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "5a8e19d0", "metadata": { "tags": [] }, "outputs": [], "source": [ "def print_materials_with_svhcs(materials, depth=0):\n", " mats_contain_svhcs = [m for m in materials\n", " if m.indicators[\"SVHC\"] >= above_threshold_flag]\n", " for mat in mats_contain_svhcs:\n", " print(f\"{' '*depth}- Material: {mat.record_history_identity}\")\n", " print_substances_with_svhcs(mat.substances, depth + 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "4b57d8fa", "metadata": { "tags": [] }, "outputs": [], "source": [ "def print_specifications_with_svhcs(specifications, depth=0):\n", " specs_contain_svhcs = [s for s in specifications\n", " if s.indicators[\"SVHC\"] >= above_threshold_flag]\n", " for spec in specs_contain_svhcs:\n", " print(f\"{' '*depth}- Specification: {spec.record_history_identity}\")\n", " print_coatings_with_svhcs(spec.coatings, depth + 1)\n", " print_substances_with_svhcs(spec.substances, depth + 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "495fb911", "metadata": { "tags": [] }, "outputs": [], "source": [ "def print_coatings_with_svhcs(coatings, depth=0):\n", " coatings_contain_svhcs = [c for c in coatings\n", " if c.indicators[\"SVHC\"] >= above_threshold_flag]\n", " for coating in coatings_contain_svhcs:\n", " print(f\"{' '*depth}- Coating: {coating.record_history_identity}\")\n", " print_substances_with_svhcs(coating.substances, depth + 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "cbc08a26", "metadata": { "tags": [] }, "outputs": [], "source": [ "def print_substances_with_svhcs(substances, depth=0):\n", " subs_contain_svhcs = [sub for sub in substances\n", " if sub.indicators[\"SVHC\"] >= above_threshold_flag]\n", " for sub in subs_contain_svhcs:\n", " print(f\"{' '*depth}- Substance: {sub.record_history_identity}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "d4642553", "metadata": { "tags": [] }, "outputs": [], "source": [ "recursively_print_parts_with_svhcs(wing.parts)" ] }, { "cell_type": "markdown", "id": "934294d4", "metadata": {}, "source": [ "We have now identified a coating that is causing non-compliance. There is a single coating in the assembly that is\n", "non-compliant, but it appears in 4 non-compliant sub-components. The coating also only contains one\n", "non-compliant substance." ] }, { "cell_type": "markdown", "id": "f1d74cf0", "metadata": {}, "source": [ "## Results Grouped by Indicator" ] }, { "cell_type": "markdown", "id": "fd99d6d8", "metadata": {}, "source": [ "Alternatively, using the ``compliance_by_indicator`` property will give us a single indicator result that rolls up the\n", "results across all parts in the query. This would be useful in a situation where we have a 'concept' assembly stored\n", "outside of Granta MI, and we want to determine its compliance. We know it contains the subassemblies specified in the\n", "query above, and so using ``compliance_by_indicator`` will tell us if that concept assembly is compliant based on the\n", "worst result of the individual subassemblies." ] }, { "cell_type": "code", "execution_count": null, "id": "c636ad8a", "metadata": { "tags": [] }, "outputs": [], "source": [ "if part_result.compliance_by_indicator[\"SVHC\"] >= above_threshold_flag:\n", " print(\"One or more subassemblies contains an SVHC in a quantity > 0.1%\")\n", "else:\n", " print(\"No SVHCs, or SVHCs are present in a quantity < 0.1%\")" ] } ], "metadata": { "jupytext": { "formats": "ipynb,py:light" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }