{ "cells": [ { "cell_type": "markdown", "id": "f28898f0", "metadata": {}, "source": [ "# Performing a Substance Compliance Query" ] }, { "cell_type": "markdown", "id": "8322b67e", "metadata": {}, "source": [ "A Substance Compliance Query determines whether one or more substances are compliant with the specified indicators.\n", "This example checks several materials for substances included on two watch lists (\"EU REACH - The Candidate List\", and\n", "\"The SIN List 2.1\"), specifying substance amounts and thresholds for compliance." ] }, { "cell_type": "markdown", "id": "b26b57e9", "metadata": {}, "source": [ "## Connecting to Granta MI" ] }, { "cell_type": "markdown", "id": "43ea2ed4", "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": "c1759520", "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": "99cbac8a", "metadata": {}, "source": [ "## Defining an Indicator" ] }, { "cell_type": "markdown", "id": "3f639790", "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": "1d252504", "metadata": {}, "source": [ "First, create two ``WatchListIndicator`` objects." ] }, { "cell_type": "code", "execution_count": null, "id": "5f99eedf", "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": "644a83f2", "metadata": { "tags": [] }, "source": [ "## Building and Running the Query" ] }, { "cell_type": "markdown", "id": "ecc096bf", "metadata": {}, "source": [ "Next, define the query itself. Substances can be referenced by Granta MI record reference, CAS\n", "Number, EC Number, or Chemical Name.\n", "\n", "The substance quantity is an optional argument, and defaults to 100% if not specified." ] }, { "cell_type": "code", "execution_count": null, "id": "497ec153", "metadata": { "tags": [] }, "outputs": [], "source": [ "from ansys.grantami.bomanalytics import queries\n", "\n", "sub_query = queries.SubstanceComplianceQuery().with_indicators([svhc, sin])\n", "sub_query = sub_query.with_cas_numbers_and_amounts([(\"50-00-0\", 10),\n", " (\"110-00-9\", 25),\n", " (\"302-17-0\", 100),\n", " (\"7440-23-5\", 100)])" ] }, { "cell_type": "markdown", "id": "5fc4a34d", "metadata": {}, "source": [ "Finally, run the query. Passing a ``SubstanceComplianceQuery`` object to the ``Connection.run()`` method returns a\n", "``SubstanceComplianceQueryResult`` object." ] }, { "cell_type": "code", "execution_count": null, "id": "ad05d4fc", "metadata": { "tags": [] }, "outputs": [], "source": [ "sub_result = cxn.run(sub_query)\n", "sub_result" ] }, { "cell_type": "markdown", "id": "6112c847", "metadata": { "tags": [] }, "source": [ "The result object contains two properties: ``compliance_by_substance_and_indicator`` and ``compliance_by_indicator``." ] }, { "cell_type": "markdown", "id": "ae4447d5", "metadata": {}, "source": [ "## Results Grouped by Substance" ] }, { "cell_type": "markdown", "id": "15c6f99c", "metadata": { "tags": [] }, "source": [ "``compliance_by_substance_and_indicator`` contains a list of ``SubstanceWithComplianceResult`` objects that contain\n", "the reference to the substance record and the compliance status in the list of indicators. To determine which\n", "substances are compliant, we can loop over each one and compare the indicator to a certain threshold. For this\n", "example, we will only examine the SVHC indicator." ] }, { "cell_type": "markdown", "id": "ea91ea48", "metadata": {}, "source": [ "The possible states of the indicator are available on the ``Indicator.available_flags`` attribute and can be compared\n", "using standard Python operators." ] }, { "cell_type": "code", "execution_count": null, "id": "bf257d01", "metadata": { "tags": [] }, "outputs": [], "source": [ "compliant_substances = []\n", "non_compliant_substances = []\n", "threshold = svhc.available_flags.WatchListAboveThreshold\n", "\n", "for substance in sub_result.compliance_by_substance_and_indicator:\n", " if (substance.indicators[\"SVHC\"] >= threshold):\n", " non_compliant_substances.append(substance)\n", " else:\n", " compliant_substances.append(substance)" ] }, { "cell_type": "markdown", "id": "427b1377", "metadata": {}, "source": [ "Now print the SVHC and Non-SVHC substances." ] }, { "cell_type": "code", "execution_count": null, "id": "4786faff", "metadata": { "tags": [] }, "outputs": [], "source": [ "compliant_cas_numbers = [sub.cas_number for sub in compliant_substances]\n", "print(f'Non-SVHC substances: {\", \".join(compliant_cas_numbers)}')\n", "\n", "non_compliant_cas_numbers = [sub.cas_number for sub in non_compliant_substances]\n", "print(f'SVHCs: {\", \".join(non_compliant_cas_numbers)}')" ] }, { "cell_type": "markdown", "id": "d4022638", "metadata": {}, "source": [ "## Results Grouped by Indicator" ] }, { "cell_type": "markdown", "id": "6449aa56", "metadata": {}, "source": [ "Alternatively, using the ``compliance_by_indicator`` property provides a single indicator result that summarizes the\n", "results across all substances in the query. This would be useful in a situation where we have a 'concept' material\n", "stored outside of Granta MI, and we want to determine its compliance. We know it contains the substances specified in\n", "the query above, and so using ``compliance_by_indicator`` will tell us if that concept material is compliant based on\n", "the worst result of the individual substances." ] }, { "cell_type": "code", "execution_count": null, "id": "aaa8a85e", "metadata": { "tags": [] }, "outputs": [], "source": [ "if sub_result.compliance_by_indicator[\"SVHC\"] >= threshold:\n", " print(\"One or more substances is an SVHC in a quantity > 0.1%\")\n", "else:\n", " print(\"No SVHCs, or SVHCs are present in a quantity < 0.1%\")" ] }, { "cell_type": "markdown", "id": "e491e78b", "metadata": {}, "source": [ "Note that this property does not tell us which substance is responsible for the non-compliance. This would require\n", "performing a more granular analysis as shown above, or importing the material into Granta MI and running a compliance\n", "query on that material record." ] } ], "metadata": { "jupytext": { "formats": "ipynb,py:light" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }