Robert Elder commited on
Commit ·
d21d254
1
Parent(s): f9ba676
adding exposure3 module with QRF model
Browse files- CHRIS.py +3 -0
- exposure3_module/__init__.py +5 -0
- exposure3_module/exposure.py +164 -0
- exposure3_module/static/Changelog.html +17 -0
- exposure3_module/static/Changelog.md +19 -0
- exposure3_module/static/exposure_COU.html +91 -0
- exposure3_module/static/exposure_COU.md +34 -0
- exposure3_module/static/images/FDAgraphic.png +3 -0
- exposure3_module/static/images/FDAlogo.png +3 -0
- exposure3_module/static/md2html.sh +5 -0
- exposure3_module/templates/exposure3_MwError.html +68 -0
- exposure3_module/templates/exposure3_chemError.html +24 -0
- exposure3_module/templates/exposure3_index.html +182 -0
- exposure3_module/templates/exposure3_metalError.html +68 -0
- exposure3_module/templates/exposure3_report.html +138 -0
- functions.py +2 -1
- qrf_functions.py +82 -0
- qrf_model_bundle_37.pkl +3 -0
- qrf_train.py +38 -0
- qrf_x.xlsx +3 -0
- qrf_y.xlsx +3 -0
- templates/main.html +8 -0
CHRIS.py
CHANGED
|
@@ -18,6 +18,9 @@ app.register_blueprint(blueprint)
|
|
| 18 |
from exposure2_module import blueprint
|
| 19 |
app.register_blueprint(blueprint)
|
| 20 |
|
|
|
|
|
|
|
|
|
|
| 21 |
from equivalency_module import blueprint
|
| 22 |
app.register_blueprint(blueprint)
|
| 23 |
|
|
|
|
| 18 |
from exposure2_module import blueprint
|
| 19 |
app.register_blueprint(blueprint)
|
| 20 |
|
| 21 |
+
from exposure3_module import blueprint
|
| 22 |
+
app.register_blueprint(blueprint)
|
| 23 |
+
|
| 24 |
from equivalency_module import blueprint
|
| 25 |
app.register_blueprint(blueprint)
|
| 26 |
|
exposure3_module/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
|
| 3 |
+
blueprint = Blueprint('exposure3_module', __name__, template_folder='templates', static_folder='static', static_url_path='/exposure3_module')
|
| 4 |
+
|
| 5 |
+
from . import exposure
|
exposure3_module/exposure.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import numpy as np
|
| 3 |
+
from flask import render_template, request
|
| 4 |
+
from functions import SigFigs, Piringer, WilkeChang, SheetRelease, SheetRates, RatePlot
|
| 5 |
+
from functions import Piecewise, PowerLaw
|
| 6 |
+
from qrf_functions import QRF_Apply
|
| 7 |
+
from . import blueprint
|
| 8 |
+
from polymers import Polymers, Polymers3
|
| 9 |
+
from ChemID import ResolveChemical
|
| 10 |
+
|
| 11 |
+
import rdkit
|
| 12 |
+
from rdkit.Chem import AllChem as Chem
|
| 13 |
+
|
| 14 |
+
# get additional physical properties, options are: logp, rho, mp
|
| 15 |
+
#get_properties = [] # don't get any; this breaks ceramics logic
|
| 16 |
+
#get_properties = ['logp','rho','mp'] # get all three
|
| 17 |
+
get_properties = ['mp'] # only get mp
|
| 18 |
+
# show additional physical properties
|
| 19 |
+
show_properties = False
|
| 20 |
+
# output additional info for physical properties
|
| 21 |
+
debug = False
|
| 22 |
+
# flag for testing new polymer categories
|
| 23 |
+
#use_new = True
|
| 24 |
+
|
| 25 |
+
ORGANIC_ATOM_SET = {5, 6, 7, 8, 9, 15, 16, 17, 35, 53}
|
| 26 |
+
METAL_ATOM_SET = set([3,4,11,12,13] + list(range(19,31+1)) + list(range(37,50+1)) + list(range(55,84+1)) + list(range(87,114+1)) + [116])
|
| 27 |
+
|
| 28 |
+
# load polymer data including Ap values
|
| 29 |
+
polymers, categories, params = Polymers3()
|
| 30 |
+
|
| 31 |
+
# load the index page for the exposure module
|
| 32 |
+
@blueprint.route('/exposure3', methods=['GET'])
|
| 33 |
+
def exposure():
|
| 34 |
+
return render_template('exposure3_index.html', polymers=polymers)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# build the report page for the exposure module
|
| 38 |
+
@blueprint.route('/exposure3', methods=['POST'])
|
| 39 |
+
def exp_post():
|
| 40 |
+
|
| 41 |
+
chemName = request.form["chemName"]
|
| 42 |
+
IDtype = request.form["IDtype"]
|
| 43 |
+
|
| 44 |
+
if debug:
|
| 45 |
+
iupac, cas, smiles, MW, LogP, LogP_origin, rho, rho_origin, mp, mp_origin, molImage, error = ResolveChemical(chemName, IDtype, debug=debug, get_properties=['logp','rho','mp'])
|
| 46 |
+
LogP_origin, rho_origin, mp_origin = f' ({LogP_origin})', f' ({rho_origin})', f' ({mp_origin})',
|
| 47 |
+
else:
|
| 48 |
+
LogP_origin, rho_origin, mp_origin = '','',''
|
| 49 |
+
iupac, cas, smiles, MW, LogP, rho, mp, molImage, error = ResolveChemical(chemName, IDtype, get_properties=get_properties)
|
| 50 |
+
|
| 51 |
+
if error > 0:
|
| 52 |
+
# TODO output more useful info
|
| 53 |
+
return render_template('exposure3_chemError.html')
|
| 54 |
+
|
| 55 |
+
#MW = SigFigs(MW, 6)
|
| 56 |
+
if 'logp' not in get_properties:
|
| 57 |
+
LogP = 'Not searched'
|
| 58 |
+
elif LogP is np.nan or LogP is None:
|
| 59 |
+
LogP = 'Not found'
|
| 60 |
+
else:
|
| 61 |
+
LogP = SigFigs(LogP, 4)
|
| 62 |
+
if 'rho' not in get_properties:
|
| 63 |
+
rho = 'Not searched'
|
| 64 |
+
elif rho is np.nan or rho is None:
|
| 65 |
+
rho = 'Not found'
|
| 66 |
+
else:
|
| 67 |
+
rho = SigFigs(rho, 4)
|
| 68 |
+
if 'mp' not in get_properties:
|
| 69 |
+
mp = 'Not searched'
|
| 70 |
+
elif mp is np.nan or mp is None:
|
| 71 |
+
mp = 'Not found'
|
| 72 |
+
|
| 73 |
+
# metals/ceramics logic
|
| 74 |
+
ceramic = False
|
| 75 |
+
mol = Chem.MolFromSmiles(smiles)
|
| 76 |
+
#mol = Chem.AddHs(mol)
|
| 77 |
+
atom_num_list = [a.GetAtomicNum() for a in mol.GetAtoms()]
|
| 78 |
+
is_metal = set(atom_num_list) <= METAL_ATOM_SET
|
| 79 |
+
if is_metal:
|
| 80 |
+
# if all atoms are metals -> this is a metal
|
| 81 |
+
#if natoms == 1 and smiles != '[C]':
|
| 82 |
+
# only one atom, except for carbon -> assumed metal
|
| 83 |
+
# return render_template('chemError.html')
|
| 84 |
+
return render_template('exposure3_metalError.html', show_properties=show_properties, chemName=chemName, MW=MW, LogP=LogP, rho=rho, mp=mp, iupac=iupac,
|
| 85 |
+
cas=cas, smiles=smiles, molImage=molImage,
|
| 86 |
+
LogP_origin=LogP_origin, rho_origin=rho_origin, mp_origin=mp_origin)
|
| 87 |
+
else:
|
| 88 |
+
# get number of carbon-carbon bonds
|
| 89 |
+
num_CC_bonds = sum([1 if b.GetBeginAtom().GetAtomicNum() == 6 and b.GetEndAtom().GetAtomicNum() == 6 else 0 for b in mol.GetBonds()])
|
| 90 |
+
if not num_CC_bonds and (mp is not None) and mp > 700.:
|
| 91 |
+
# if not a metal, and mp > 700 (sodium chloride has mp ~ 800), and no C-C bonds, assume ceramic...
|
| 92 |
+
#if (type(mp) is float or type(mp) is int) and mp > 1000.:
|
| 93 |
+
# if not a metal but melting point > 1000, assume ceramic
|
| 94 |
+
MW = 1100.
|
| 95 |
+
ceramic = True
|
| 96 |
+
|
| 97 |
+
#if MW < 100. and not use_new:
|
| 98 |
+
# return render_template('exposure3_MwError.html', show_properties=show_properties, chemName=chemName, MW=MW, LogP=LogP, rho=rho, mp=mp, iupac=iupac, cas=cas, smiles=smiles, molImage=molImage,
|
| 99 |
+
# LogP_origin=LogP_origin, rho_origin=rho_origin, mp_origin=mp_origin)
|
| 100 |
+
|
| 101 |
+
amount = float(request.form["amount"])
|
| 102 |
+
mass = float(request.form["mass"])
|
| 103 |
+
density = float(request.form["density"])
|
| 104 |
+
vol = mass / density
|
| 105 |
+
area = float(request.form["area"])
|
| 106 |
+
exposure = request.form["exposure"]
|
| 107 |
+
|
| 108 |
+
polymer = request.form["polymer"]
|
| 109 |
+
pIndex = np.argmax(polymers == polymer)
|
| 110 |
+
|
| 111 |
+
if exposure != "limited":
|
| 112 |
+
time = 24.
|
| 113 |
+
else:
|
| 114 |
+
time = float(request.form["exptime"])
|
| 115 |
+
|
| 116 |
+
if exposure != "long-term":
|
| 117 |
+
TTC = 0.12
|
| 118 |
+
else:
|
| 119 |
+
TTC = 0.0015
|
| 120 |
+
|
| 121 |
+
#assume = np.array((request.form.get("assume1") is not None, request.form.get("assume2") is not None,
|
| 122 |
+
# request.form.get("assume3") is not None, request.form.get("assume4") is not None,
|
| 123 |
+
# request.form.get("assume5") is not None))
|
| 124 |
+
|
| 125 |
+
use_qrf = False
|
| 126 |
+
if polymer == "Other polymer":
|
| 127 |
+
polytg = request.form["polytg"]
|
| 128 |
+
if polytg == '':
|
| 129 |
+
# left blank, use old default (Wilke Chang), which is taken care of by pIndex
|
| 130 |
+
pass
|
| 131 |
+
else:
|
| 132 |
+
polytg = float(polytg)
|
| 133 |
+
use_qrf = True
|
| 134 |
+
#print('b', polytg, type(polytg), file=sys.stderr)
|
| 135 |
+
|
| 136 |
+
if use_qrf:
|
| 137 |
+
#print('using qrf', file=sys.stderr)
|
| 138 |
+
## TODO ceramics
|
| 139 |
+
diff,domain_extrap = QRF_Apply(density, polytg, smiles, quantiles=[0.03,0.5,0.97])
|
| 140 |
+
diff = diff[2] # upper bound
|
| 141 |
+
else:
|
| 142 |
+
## use categories
|
| 143 |
+
category = categories[pIndex]
|
| 144 |
+
diff = Piecewise(MW, params[category])
|
| 145 |
+
domain_extrap = False
|
| 146 |
+
|
| 147 |
+
release = SheetRelease(amount, vol, area, time, diff)
|
| 148 |
+
|
| 149 |
+
MOS = TTC / release
|
| 150 |
+
|
| 151 |
+
release = SigFigs(release, 2)
|
| 152 |
+
MOS = SigFigs(MOS, 2)
|
| 153 |
+
diff = SigFigs(diff, 2)
|
| 154 |
+
|
| 155 |
+
# Generate the rate plot using matplotlib
|
| 156 |
+
tarray = np.arange(1., 31., 1.)
|
| 157 |
+
rates = SheetRates(amount, vol, area, tarray, diff)
|
| 158 |
+
pngImageB64String = RatePlot(tarray, rates)
|
| 159 |
+
|
| 160 |
+
return render_template('exposure3_report.html', show_properties=show_properties, polymers=polymers, pIndex=pIndex, release=release,
|
| 161 |
+
area=area, vol=vol, amount=amount, diff=diff, time=time, exposure=exposure, TTC=TTC,
|
| 162 |
+
MOS=MOS, chemName=chemName, image=pngImageB64String, MW=MW, LogP=LogP, rho=rho, mp=mp, iupac=iupac, cas=cas, smiles=smiles, molImage=molImage,
|
| 163 |
+
LogP_origin=LogP_origin, rho_origin=rho_origin, mp_origin=mp_origin, ceramic=ceramic, domain_extrap=domain_extrap)
|
| 164 |
+
|
exposure3_module/static/Changelog.html
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<title>CHRIS-COU</title>
|
| 5 |
+
<link rel="stylesheet" href="styles.css">
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
|
| 8 |
+
</head>
|
| 9 |
+
|
| 10 |
+
<header>
|
| 11 |
+
<h1 style="text-align:center"><font color="#0070C0">CH</font>emical <font color="#0070C0">RIS</font>k calculator (CHRIS) - Bulk chemicals </h1>
|
| 12 |
+
</header>
|
| 13 |
+
<h2 id="change-log">Change Log</h2>
|
| 14 |
+
<h3 id="version-1.0---2022-05-23">Version 1.0 - 2022-05-23</h3>
|
| 15 |
+
<ul>
|
| 16 |
+
<li>Original release</li>
|
| 17 |
+
</ul>
|
exposure3_module/static/Changelog.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{=html}
|
| 2 |
+
<!DOCTYPE html>
|
| 3 |
+
<html lang="en">
|
| 4 |
+
<head>
|
| 5 |
+
<title>CHRIS-COU</title>
|
| 6 |
+
<link rel="stylesheet" href="styles.css">
|
| 7 |
+
<meta charset="UTF-8">
|
| 8 |
+
|
| 9 |
+
</head>
|
| 10 |
+
|
| 11 |
+
<header>
|
| 12 |
+
<h1 style="text-align:center"><font color="#0070C0">CH</font>emical <font color="#0070C0">RIS</font>k calculator (CHRIS) - Bulk chemicals </h1>
|
| 13 |
+
</header>
|
| 14 |
+
```
|
| 15 |
+
## Change Log
|
| 16 |
+
|
| 17 |
+
### Version 1.0 - 2022-05-23
|
| 18 |
+
|
| 19 |
+
* Original release
|
exposure3_module/static/exposure_COU.html
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<title>Bulk-COU</title>
|
| 5 |
+
<link rel="stylesheet" href="../../static/styles.css">
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
</head>
|
| 8 |
+
|
| 9 |
+
<header>
|
| 10 |
+
<img src="images/FDAlogo.png" style="float: left;" height="100"/>
|
| 11 |
+
<img src="images/FDAgraphic.png" style="float: right;" height="100"/>
|
| 12 |
+
<br clear="all" />
|
| 13 |
+
<h1 style="text-align:center">CHRIS: <font color="#0070C0">CH</font>emical <font color="#0070C0">RIS</font>k calculator - Bulk leachables</h1>
|
| 14 |
+
</header>
|
| 15 |
+
<h2 id="context-of-use-cou">Context of Use (COU)</h2>
|
| 16 |
+
<p>The bulk leachable module of the CHemical RISk calculator (CHRIS) is
|
| 17 |
+
intended to conduct screening level risk assessments to aid in the
|
| 18 |
+
biocompatibility evaluation of bulk additives and impurities in
|
| 19 |
+
polymeric medical device components. These assessments can assist device
|
| 20 |
+
manufacturers by providing instantaneous feedback on whether the
|
| 21 |
+
presence of the bulk chemical would require additional justification
|
| 22 |
+
and/or testing to demonstrate acceptable biological risk. The output of
|
| 23 |
+
CHRIS is a conservative margin of safety (MOS = toxicological safety
|
| 24 |
+
limit ÷ exposure dose) value for a bulk chemical contained within a
|
| 25 |
+
polymeric medical device component. Based on the MOS value, the
|
| 26 |
+
calculator determines if further assessment of one or more
|
| 27 |
+
biocompatibility endpoints is necessary for the specific chemical.</p>
|
| 28 |
+
<p>Because CHRIS only addresses compounds with a distribution that is
|
| 29 |
+
macroscopically homogeneous within the matrix, the tool can only be used
|
| 30 |
+
to assess bulk additives and impurities. Therefore, only compounds that
|
| 31 |
+
are introduced either intentionally or unintentionally during synthesis
|
| 32 |
+
(e.g., residual monomers and oligomers, catalysts, initiators) or
|
| 33 |
+
compounding (e.g., stabilizers, antioxidants, plasticizers) are within
|
| 34 |
+
scope. Surface residuals from processing, cleaning, and sterilization
|
| 35 |
+
are excluded. Also, CHRIS requires the total amount of the chemical to
|
| 36 |
+
be established in advance, e.g., based on a certificate of analysis.
|
| 37 |
+
Further CHRIS only addresses individual chemicals; therefore, a
|
| 38 |
+
favorable outcome by CHRIS does not imply acceptable biological risk for
|
| 39 |
+
the final finished form of a medical device. CHRIS is also not intended
|
| 40 |
+
to establish device classification or identify biocompatibility
|
| 41 |
+
requirements.</p>
|
| 42 |
+
<p>CHRIS provides clinically relevant, yet still conservative, exposure
|
| 43 |
+
dose estimates using a physics-based transport model for polymeric
|
| 44 |
+
systems where transport data are available to support the use of the
|
| 45 |
+
model. The model applies worst-case boundary conditions for release of a
|
| 46 |
+
substance from the polymer matrix and is based on five (5) primary
|
| 47 |
+
assumptions:</p>
|
| 48 |
+
<ol type="1">
|
| 49 |
+
<li>The clinical use environment does not cause the polymer matrix to
|
| 50 |
+
swell or degrade.</li>
|
| 51 |
+
<li>Manufacturing processes do not impact the stability of the
|
| 52 |
+
polymer.</li>
|
| 53 |
+
<li>The chemical is homogeneously distributed throughout the
|
| 54 |
+
polymer.</li>
|
| 55 |
+
<li>The total amount of the chemical is present in dilute concentrations
|
| 56 |
+
(<= 2 m/v %).</li>
|
| 57 |
+
<li>Any particles/aggregates of the chemical present in the polymer are
|
| 58 |
+
much smaller than the smallest component dimension (<= 50x).</li>
|
| 59 |
+
</ol>
|
| 60 |
+
<p>While these assumptions are typically valid for bulk additives and
|
| 61 |
+
impurities in biostable polymers, users of CHRIS must confirm
|
| 62 |
+
conformance to the underlying assumptions or provide supporting
|
| 63 |
+
justification to ensure compliance for a given system. Further, CHRIS
|
| 64 |
+
only enables system specific exposure estimates for fifty-three (53)
|
| 65 |
+
polymeric systems that are generally biostable (non-swelling and
|
| 66 |
+
non-degrading). These polymers are listed below. To estimate chemical
|
| 67 |
+
release based on the model, the diffusion coefficient of the chemical in
|
| 68 |
+
the polymer matrix must be specified. For the fifty-three (53) listed
|
| 69 |
+
polymeric systems, a worst-case (upper bound) diffusion coefficient, as
|
| 70 |
+
a function of molecular weight, has been established based on data from
|
| 71 |
+
the literature. For polymer matrices that are not included in this list,
|
| 72 |
+
CHRIS assigns an ultra-conservative diffusion coefficient that assumes
|
| 73 |
+
the polymer has the properties of water. Note that the worst-case
|
| 74 |
+
diffusion coefficient is only defined over a molecular weight range of
|
| 75 |
+
up to 1100 g/mol. Therefore, for substances with a molecular weight >
|
| 76 |
+
1100 g/mol, the value of the diffusion coefficient assuming a molecular
|
| 77 |
+
weight of 1100 g/mol can be used as a conservative value.</p>
|
| 78 |
+
<p>In the absence of adequate toxicological and exposure data for a
|
| 79 |
+
chemical in a polymeric matrix, a toxicological risk assessment can be
|
| 80 |
+
conducted for systemic biocompatibility endpoints by comparing the
|
| 81 |
+
exposure estimate to an appropriate threshold of toxicological concern
|
| 82 |
+
(TTC). This is the approach used by CHRIS in this module. The TTC values
|
| 83 |
+
are based on systemic toxicity, thus CHRIS can address acute systemic
|
| 84 |
+
toxicity, subacute/subchronic toxicity, genotoxicity, carcinogenicity,
|
| 85 |
+
and reproductive and developmental toxicity. It does not, however,
|
| 86 |
+
address cytotoxicity, sensitization, irritation, hemocompatibility,
|
| 87 |
+
material mediated pyrogenicity, or implantation. Therefore, an MOS >=
|
| 88 |
+
1 implies the chemical will not raise a safety concern with respect to
|
| 89 |
+
only the systemic biocompatibility endpoints, provided the chemical is
|
| 90 |
+
not within the cohort of concern, which is reflected in the output of
|
| 91 |
+
CHRIS.</p>
|
exposure3_module/static/exposure_COU.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{=html}
|
| 2 |
+
<!DOCTYPE html>
|
| 3 |
+
<html lang="en">
|
| 4 |
+
<head>
|
| 5 |
+
<title>Bulk-COU</title>
|
| 6 |
+
<link rel="stylesheet" href="../../static/styles.css">
|
| 7 |
+
<meta charset="UTF-8">
|
| 8 |
+
</head>
|
| 9 |
+
|
| 10 |
+
<header>
|
| 11 |
+
<img src="images/FDAlogo.png" style="float: left;" height="100"/>
|
| 12 |
+
<img src="images/FDAgraphic.png" style="float: right;" height="100"/>
|
| 13 |
+
<br clear="all" />
|
| 14 |
+
<h1 style="text-align:center">CHRIS: <font color="#0070C0">CH</font>emical <font color="#0070C0">RIS</font>k calculator - Bulk leachables</h1>
|
| 15 |
+
</header>
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
## Context of Use (COU)
|
| 19 |
+
|
| 20 |
+
The bulk leachable module of the CHemical RISk calculator (CHRIS) is intended to conduct screening level risk assessments to aid in the biocompatibility evaluation of bulk additives and impurities in polymeric medical device components. These assessments can assist device manufacturers by providing instantaneous feedback on whether the presence of the bulk chemical would require additional justification and/or testing to demonstrate acceptable biological risk. The output of CHRIS is a conservative margin of safety (MOS = toxicological safety limit ÷ exposure dose) value for a bulk chemical contained within a polymeric medical device component. Based on the MOS value, the calculator determines if further assessment of one or more biocompatibility endpoints is necessary for the specific chemical.
|
| 21 |
+
|
| 22 |
+
Because CHRIS only addresses compounds with a distribution that is macroscopically homogeneous within the matrix, the tool can only be used to assess bulk additives and impurities. Therefore, only compounds that are introduced either intentionally or unintentionally during synthesis (e.g., residual monomers and oligomers, catalysts, initiators) or compounding (e.g., stabilizers, antioxidants, plasticizers) are within scope. Surface residuals from processing, cleaning, and sterilization are excluded. Also, CHRIS requires the total amount of the chemical to be established in advance, e.g., based on a certificate of analysis. Further CHRIS only addresses individual chemicals; therefore, a favorable outcome by CHRIS does not imply acceptable biological risk for the final finished form of a medical device. CHRIS is also not intended to establish device classification or identify biocompatibility requirements.
|
| 23 |
+
|
| 24 |
+
CHRIS provides clinically relevant, yet still conservative, exposure dose estimates using a physics-based transport model for polymeric systems where transport data are available to support the use of the model. The model applies worst-case boundary conditions for release of a substance from the polymer matrix and is based on five (5) primary assumptions:
|
| 25 |
+
|
| 26 |
+
1. The clinical use environment does not cause the polymer matrix to swell or degrade.
|
| 27 |
+
1. Manufacturing processes do not impact the stability of the polymer.
|
| 28 |
+
1. The chemical is homogeneously distributed throughout the polymer.
|
| 29 |
+
1. The total amount of the chemical is present in dilute concentrations (<= 2 m/v %).
|
| 30 |
+
1. Any particles/aggregates of the chemical present in the polymer are much smaller than the smallest component dimension (<= 50x).
|
| 31 |
+
|
| 32 |
+
While these assumptions are typically valid for bulk additives and impurities in biostable polymers, users of CHRIS must confirm conformance to the underlying assumptions or provide supporting justification to ensure compliance for a given system. Further, CHRIS only enables system specific exposure estimates for fifty-three (53) polymeric systems that are generally biostable (non-swelling and non-degrading). These polymers are listed below. To estimate chemical release based on the model, the diffusion coefficient of the chemical in the polymer matrix must be specified. For the fifty-three (53) listed polymeric systems, a worst-case (upper bound) diffusion coefficient, as a function of molecular weight, has been established based on data from the literature. For polymer matrices that are not included in this list, CHRIS assigns an ultra-conservative diffusion coefficient that assumes the polymer has the properties of water. Note that the worst-case diffusion coefficient is only defined over a molecular weight range of up to 1100 g/mol. Therefore, for substances with a molecular weight > 1100 g/mol, the value of the diffusion coefficient assuming a molecular weight of 1100 g/mol can be used as a conservative value.
|
| 33 |
+
|
| 34 |
+
In the absence of adequate toxicological and exposure data for a chemical in a polymeric matrix, a toxicological risk assessment can be conducted for systemic biocompatibility endpoints by comparing the exposure estimate to an appropriate threshold of toxicological concern (TTC). This is the approach used by CHRIS in this module. The TTC values are based on systemic toxicity, thus CHRIS can address acute systemic toxicity, subacute/subchronic toxicity, genotoxicity, carcinogenicity, and reproductive and developmental toxicity. It does not, however, address cytotoxicity, sensitization, irritation, hemocompatibility, material mediated pyrogenicity, or implantation. Therefore, an MOS >= 1 implies the chemical will not raise a safety concern with respect to only the systemic biocompatibility endpoints, provided the chemical is not within the cohort of concern, which is reflected in the output of CHRIS.
|
exposure3_module/static/images/FDAgraphic.png
ADDED
|
Git LFS Details
|
exposure3_module/static/images/FDAlogo.png
ADDED
|
Git LFS Details
|
exposure3_module/static/md2html.sh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
pandoc exposure_COU.md > exposure_COU.html
|
| 4 |
+
pandoc Changelog.md > Changelog.html
|
| 5 |
+
|
exposure3_module/templates/exposure3_MwError.html
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>CHRIS-MwError</title>
|
| 6 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
|
| 7 |
+
|
| 8 |
+
<style>
|
| 9 |
+
* {
|
| 10 |
+
box-sizing: border-box;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
/* Create two equal columns that floats next to each other */
|
| 14 |
+
.column {
|
| 15 |
+
float: left;
|
| 16 |
+
width: 50%;
|
| 17 |
+
padding: 10px;
|
| 18 |
+
vertical-align: top;
|
| 19 |
+
align: center;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/* Clear floats after the columns */
|
| 23 |
+
.row:after {
|
| 24 |
+
content: "";
|
| 25 |
+
display: table;
|
| 26 |
+
clear: both;
|
| 27 |
+
}
|
| 28 |
+
</style>
|
| 29 |
+
|
| 30 |
+
</head>
|
| 31 |
+
|
| 32 |
+
<img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
|
| 33 |
+
<img src="{{ url_for('static',filename='images/FDAgraphic.png') }}" style="float: right;" height="100"/>
|
| 34 |
+
<br clear="all" />
|
| 35 |
+
|
| 36 |
+
<body>
|
| 37 |
+
|
| 38 |
+
<div style="font-size:5rem;text-align:center"> 🤦 </div>
|
| 39 |
+
|
| 40 |
+
<h2> Compound </h2>
|
| 41 |
+
|
| 42 |
+
<div class="container">
|
| 43 |
+
<div class="row">
|
| 44 |
+
<div class="column">
|
| 45 |
+
Input :: {{chemName}} <br> <br>
|
| 46 |
+
IUPAC Name :: {{iupac}} <br> <br>
|
| 47 |
+
CAS :: {{cas}} <br> <br>
|
| 48 |
+
Molecular weight (g/mol) :: {{'%0.4f'%MW|float}} <br> <br>
|
| 49 |
+
{% if show_properties %}
|
| 50 |
+
LogKow :: {{LogP}}{{LogP_origin}} <br> <br>
|
| 51 |
+
Density (g/cm<sup>3</sup>) :: {{rho}}{{rho_origin}} <br> <br>
|
| 52 |
+
Melting point (°C) :: {{mp}}{{mp_origin}} <br> <br>
|
| 53 |
+
{% endif %}
|
| 54 |
+
SMILES :: {{smiles}}
|
| 55 |
+
</div>
|
| 56 |
+
<div class="column">
|
| 57 |
+
<img src="{{molImage}}"/>
|
| 58 |
+
</div>
|
| 59 |
+
</div>
|
| 60 |
+
</div>
|
| 61 |
+
|
| 62 |
+
<p style="font-size:2rem;text-align:center">
|
| 63 |
+
Unfortunately, CHRIS cannot be used for chemicals with Mw < 100 g/mol. Please return to the previous page to evaluate
|
| 64 |
+
a different chemical.
|
| 65 |
+
</p>
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
</body>
|
exposure3_module/templates/exposure3_chemError.html
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>CHRIS-ChemError</title>
|
| 6 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
|
| 7 |
+
|
| 8 |
+
</head>
|
| 9 |
+
|
| 10 |
+
<img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
|
| 11 |
+
<img src="{{ url_for('static',filename='images/FDAgraphic.png') }}" style="float: right;" height="100"/>
|
| 12 |
+
<br clear="all" />
|
| 13 |
+
|
| 14 |
+
<body>
|
| 15 |
+
|
| 16 |
+
<div style="font-size:5rem;text-align:center"> 🤦 </div>
|
| 17 |
+
|
| 18 |
+
<p style="font-size:2rem;text-align:center">
|
| 19 |
+
Uh-oh! Something went wrong. We were unable to match your chemical. Please return to the previous page and try a
|
| 20 |
+
different identifier.
|
| 21 |
+
</p>
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
</body>
|
exposure3_module/templates/exposure3_index.html
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>CHRIS</title>
|
| 6 |
+
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
| 7 |
+
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
| 8 |
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
|
| 9 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
|
| 10 |
+
|
| 11 |
+
</head>
|
| 12 |
+
|
| 13 |
+
<img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
|
| 14 |
+
<img src="{{ url_for('static',filename='images/FDAgraphic.png') }}" style="float: right;" height="100"/>
|
| 15 |
+
<br clear="all" />
|
| 16 |
+
|
| 17 |
+
<header>
|
| 18 |
+
<h1 style="text-align:center"><font color="#0070C0">CH</font>emical <font color="#0070C0">RIS</font>k calculators (CHRIS) - Bulk chemicals (v2)</h1>
|
| 19 |
+
</header>
|
| 20 |
+
|
| 21 |
+
<p><strong>Note: This module only addresses risk associated with the bulk chemicals, excluding color additives, of known initial quantity.
|
| 22 |
+
For color additives, please use the <a href="/color"> color additive module</a>. </strong></p>
|
| 23 |
+
|
| 24 |
+
<p> For details on how to use the CHRIS bulk chemical module, please click the information icons next to each section header and read the
|
| 25 |
+
<a href="{{url_for('.static', filename='exposure_COU.html')}}"> context of use (COU)</a>, which includes limitations of use.
|
| 26 |
+
For a history of updates, please see the <a href="{{url_for('.static', filename='Changelog.html')}}"> changelog</a>. </p>
|
| 27 |
+
|
| 28 |
+
<body>
|
| 29 |
+
|
| 30 |
+
<form method="POST">
|
| 31 |
+
|
| 32 |
+
<!-- Chemical input section -->
|
| 33 |
+
|
| 34 |
+
<h3> Bulk chemical <button type="button" class="Info_btn" data-toggle="modal" data-target="#LeachModal" >ⓘ</button> </h3>
|
| 35 |
+
|
| 36 |
+
Identifier type: <select name="IDtype">
|
| 37 |
+
<option value="CAS" selected>CAS</option>
|
| 38 |
+
<option value="SMILES" >SMILES</option>
|
| 39 |
+
<option value="common" >Common name</option>
|
| 40 |
+
</select> <br>
|
| 41 |
+
|
| 42 |
+
Identifier: <input name="chemName" id="chemName" type="text" value="" required><br>
|
| 43 |
+
|
| 44 |
+
Amount (mg): <input name="amount" id="amount" value="1.0" step="any" min="0.000001" type="number" required> <br>
|
| 45 |
+
|
| 46 |
+
<!-- Modal -->
|
| 47 |
+
<div id="LeachModal" class="modal fade" role="dialog">
|
| 48 |
+
<div class="modal-dialog">
|
| 49 |
+
|
| 50 |
+
<!-- Modal content-->
|
| 51 |
+
<div class="modal-content">
|
| 52 |
+
<div class="modal-header">
|
| 53 |
+
<h4 class="modal-title">Bulk chemical</h4>
|
| 54 |
+
</div>
|
| 55 |
+
<div class="modal-body">
|
| 56 |
+
<p><em>Identifier type and identifier</em> - Please select the identifier type and enter the chemical identifier.
|
| 57 |
+
For example, if the CAS number is known, select CAS from the pull down menu and enter the CAS number in the identifier field.
|
| 58 |
+
If the CAS number is unknown, CHRIS can identify the molecular structure through the SMILES code, which can
|
| 59 |
+
be found for many chemicals using <a href="https://pubchem.ncbi.nlm.nih.gov">PubChem</a> or generated based on
|
| 60 |
+
the molecular structure using tools such as <a href="https://cactus.nci.nih.gov/cgi-bin/osra/index.cgi">OSRA</a>.
|
| 61 |
+
Alternatively, CHRIS can attempt to identify the chemical using a common name for the chemical.</p>
|
| 62 |
+
<p><em>Amount</em> - Enter the total mass of the substance in the component being evaluated expressed in milligrams.</p>
|
| 63 |
+
</div>
|
| 64 |
+
<div class="modal-footer">
|
| 65 |
+
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
| 66 |
+
</div>
|
| 67 |
+
</div>
|
| 68 |
+
|
| 69 |
+
</div>
|
| 70 |
+
</div>
|
| 71 |
+
|
| 72 |
+
<!-- Polymer matrix input section -->
|
| 73 |
+
|
| 74 |
+
<h3> Polymer matrix <button type="button" class="Info_btn" data-toggle="modal" data-target="#PolymerModal">ⓘ</button> </h3>
|
| 75 |
+
Matrix: <select name="polymer" id="polymer" onchange="javascript:polymerCheck();">
|
| 76 |
+
<option value="{{polymers[0]}}" selected>{{polymers[0]}}</option>
|
| 77 |
+
{% for polymer in polymers[1:] %}
|
| 78 |
+
<option value="{{polymer}}">{{polymer}}</option>
|
| 79 |
+
{% endfor %}
|
| 80 |
+
</select> <br>
|
| 81 |
+
Mass (g): <input name="mass" id="mass" step="any" value="1.0" min="0.000001" type="number" required> <br>
|
| 82 |
+
Density (g/cm<sup>3</sup>): <input name="density" id="density" step="any" value="1.0" min="0.01" type="number" required>
|
| 83 |
+
<span id="otherpolymer" style="visibility:hidden">
|
| 84 |
+
<br>Glass transition temperature (°C): <input name="polytg" id="polytg" step="any" min="-273.15" max="600" value="" type="number">
|
| 85 |
+
</span>
|
| 86 |
+
|
| 87 |
+
<!-- Modal -->
|
| 88 |
+
<div id="PolymerModal" class="modal fade" role="dialog">
|
| 89 |
+
<div class="modal-dialog">
|
| 90 |
+
|
| 91 |
+
<!-- Modal content-->
|
| 92 |
+
<div class="modal-content">
|
| 93 |
+
<div class="modal-header">
|
| 94 |
+
<h4 class="modal-title">Polymer Matrix</h4>
|
| 95 |
+
</div>
|
| 96 |
+
<div class="modal-body">
|
| 97 |
+
<p><em>Matrix</em> - Please select your polymer matrix from the list. If your polymer is
|
| 98 |
+
not listed below, please select "Other polymer". For polymer mixtures/blends, co-polymers, or composites (e.g. glass fiber reinforced matrices), the component or phase that is worst-case for exposure, i.e. the softest or least glassy (lowest T<sub>g</sub>) component can be selected if listed (which, in turn, assumes the entire system is composed of the worst-case component or phase). In these scenarios, a justification should be provided for the choice of worst-case component of the polymer system. </p>
|
| 99 |
+
<p><em>Mass</em> - Enter the mass of the polymer matrix in grams.</p>
|
| 100 |
+
<p><em>Density</em> - Enter the estimated density of the polymer matrix in grams per cubic centimeter. Note that a rough estimate (e.g. +/- 10%) is acceptable.</p>
|
| 101 |
+
<p><em>Glass transition temperature</em> - For "Other polymer", optionally enter the T<sub>g</sub> of the polymer matrix in degrees Celsius to use a less conservative model. If left blank, a more conservative model is used.</p>
|
| 102 |
+
</div>
|
| 103 |
+
<div class="modal-footer">
|
| 104 |
+
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
| 105 |
+
</div>
|
| 106 |
+
</div>
|
| 107 |
+
|
| 108 |
+
</div>
|
| 109 |
+
</div>
|
| 110 |
+
|
| 111 |
+
<!-- Device characteristics input section -->
|
| 112 |
+
|
| 113 |
+
<h3> Device characteristics <button type=button class="Info_btn" data-toggle="modal" data-target="#DeviceModal">ⓘ</button> </h3>
|
| 114 |
+
Exposed surface area (cm<sup>2</sup>):
|
| 115 |
+
<input name="area" id="area" step="any" value="5.0" min="0.001" type="number" required><br>
|
| 116 |
+
Exposure type:
|
| 117 |
+
<input type="radio" name="exposure" id="long-term" value="long-term" onclick="javascript:exposureCheck();" checked > long-term
|
| 118 |
+
<input type="radio" name="exposure" id="prolonged" value="prolonged" onclick="javascript:exposureCheck();" > prolonged
|
| 119 |
+
<input type="radio" name="exposure" id="limited" value="limited" onclick="javascript:exposureCheck();" > limited
|
| 120 |
+
<span id="limitedtime" style="visibility:hidden">
|
| 121 |
+
⇒ Exposure time (h): <input name="exptime" id="time" step="any" min="0.001" max="24" value="24" type="number" required><br>
|
| 122 |
+
</span>
|
| 123 |
+
|
| 124 |
+
<!-- Modal -->
|
| 125 |
+
<div id="DeviceModal" class="modal fade" role="dialog">
|
| 126 |
+
<div class="modal-dialog">
|
| 127 |
+
|
| 128 |
+
<!-- Modal content-->
|
| 129 |
+
<div class="modal-content">
|
| 130 |
+
<div class="modal-header">
|
| 131 |
+
<h4 class="modal-title">Device characteristics</h4>
|
| 132 |
+
</div>
|
| 133 |
+
<div class="modal-body">
|
| 134 |
+
<p><em>Exposed surface area</em> - Enter the patient contacting surface area of the component being evaluated in square centimeters.
|
| 135 |
+
This includes both direct and indirect patient contact.</p>
|
| 136 |
+
<p><em>Exposure type</em> - Select the appropriate exposure category: > 30 days = long-term, > 24 hours - 30 days = prolonged, ≤ 24 hours = limited.
|
| 137 |
+
For limited exposures (≤ 24 hours), please enter the maximum exposure time in hours.</p>
|
| 138 |
+
</div>
|
| 139 |
+
<div class="modal-footer">
|
| 140 |
+
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
| 141 |
+
</div>
|
| 142 |
+
</div>
|
| 143 |
+
|
| 144 |
+
</div>
|
| 145 |
+
</div>
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
<h3> Exposure assessment </h3>
|
| 149 |
+
Click to screen your device: <button type="submit">Estimate exposure</button>
|
| 150 |
+
|
| 151 |
+
</form>
|
| 152 |
+
|
| 153 |
+
<!-- Javascript to reveal/hide polymer input box (show/hide Tg) -->
|
| 154 |
+
|
| 155 |
+
<script type="text/javascript">
|
| 156 |
+
function polymerCheck() {
|
| 157 |
+
if (document.getElementById('polymer').value == 'Other polymer') {
|
| 158 |
+
document.getElementById('otherpolymer').style.visibility = 'visible';
|
| 159 |
+
} else {
|
| 160 |
+
document.getElementById('otherpolymer').style.visibility = 'hidden';
|
| 161 |
+
document.getElementById('polytg').value = '';
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
$(window).on('pageshow', function() { polymerCheck(); });
|
| 165 |
+
</script>
|
| 166 |
+
|
| 167 |
+
<!-- Javascript to reveal/hide exposure time input box (limited contact) -->
|
| 168 |
+
|
| 169 |
+
<script type="text/javascript">
|
| 170 |
+
function exposureCheck() {
|
| 171 |
+
if (document.getElementById('limited').checked) {
|
| 172 |
+
document.getElementById('limitedtime').style.visibility = 'visible';
|
| 173 |
+
} else {
|
| 174 |
+
document.getElementById('limitedtime').style.visibility = 'hidden';
|
| 175 |
+
document.getElementById('time').value = '24';
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
$(window).on('pageshow', function() { exposureCheck(); });
|
| 179 |
+
</script>
|
| 180 |
+
|
| 181 |
+
</body>
|
| 182 |
+
</html>
|
exposure3_module/templates/exposure3_metalError.html
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>CHRIS-MetalError</title>
|
| 6 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
|
| 7 |
+
|
| 8 |
+
<style>
|
| 9 |
+
* {
|
| 10 |
+
box-sizing: border-box;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
/* Create two equal columns that floats next to each other */
|
| 14 |
+
.column {
|
| 15 |
+
float: left;
|
| 16 |
+
width: 50%;
|
| 17 |
+
padding: 10px;
|
| 18 |
+
vertical-align: top;
|
| 19 |
+
align: center;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/* Clear floats after the columns */
|
| 23 |
+
.row:after {
|
| 24 |
+
content: "";
|
| 25 |
+
display: table;
|
| 26 |
+
clear: both;
|
| 27 |
+
}
|
| 28 |
+
</style>
|
| 29 |
+
|
| 30 |
+
</head>
|
| 31 |
+
|
| 32 |
+
<img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
|
| 33 |
+
<img src="{{ url_for('static',filename='images/FDAgraphic.png') }}" style="float: right;" height="100"/>
|
| 34 |
+
<br clear="all" />
|
| 35 |
+
|
| 36 |
+
<body>
|
| 37 |
+
|
| 38 |
+
<div style="font-size:5rem;text-align:center"> 🤦 </div>
|
| 39 |
+
|
| 40 |
+
<h2> Compound </h2>
|
| 41 |
+
|
| 42 |
+
<div class="container">
|
| 43 |
+
<div class="row">
|
| 44 |
+
<div class="column">
|
| 45 |
+
Input :: {{chemName}} <br> <br>
|
| 46 |
+
IUPAC Name :: {{iupac}} <br> <br>
|
| 47 |
+
CAS :: {{cas}} <br> <br>
|
| 48 |
+
Molecular weight (g/mol) :: {{'%0.4f'%MW|float}} <br> <br>
|
| 49 |
+
{% if show_properties %}
|
| 50 |
+
LogKow :: {{LogP}}{{LogP_origin}} <br> <br>
|
| 51 |
+
Density (g/cm<sup>3</sup>) :: {{rho}}{{rho_origin}} <br> <br>
|
| 52 |
+
Melting point (°C) :: {{mp}}{{mp_origin}} <br> <br>
|
| 53 |
+
{% endif %}
|
| 54 |
+
SMILES :: {{smiles}}
|
| 55 |
+
</div>
|
| 56 |
+
<div class="column">
|
| 57 |
+
<img src="{{molImage}}"/>
|
| 58 |
+
</div>
|
| 59 |
+
</div>
|
| 60 |
+
</div>
|
| 61 |
+
|
| 62 |
+
<p style="font-size:2rem;text-align:center">
|
| 63 |
+
Unfortunately, CHRIS cannot be used for metals. Please return to the previous page to evaluate
|
| 64 |
+
a different chemical.
|
| 65 |
+
</p>
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
</body>
|
exposure3_module/templates/exposure3_report.html
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>CHRIS Report</title>
|
| 6 |
+
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
|
| 7 |
+
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
| 8 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
|
| 9 |
+
|
| 10 |
+
<style>
|
| 11 |
+
* {
|
| 12 |
+
box-sizing: border-box;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
/* Create two equal columns that floats next to each other */
|
| 16 |
+
.column {
|
| 17 |
+
float: left;
|
| 18 |
+
width: 50%;
|
| 19 |
+
padding: 10px;
|
| 20 |
+
vertical-align: top;
|
| 21 |
+
align: center;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/* Clear floats after the columns */
|
| 25 |
+
.row:after {
|
| 26 |
+
content: "";
|
| 27 |
+
display: table;
|
| 28 |
+
clear: both;
|
| 29 |
+
}
|
| 30 |
+
</style>
|
| 31 |
+
|
| 32 |
+
</head>
|
| 33 |
+
|
| 34 |
+
<img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
|
| 35 |
+
<img src="{{ url_for('static',filename='images/FDAgraphic.png') }}" style="float: right;" height="100"/>
|
| 36 |
+
<br clear="all" />
|
| 37 |
+
|
| 38 |
+
<header>
|
| 39 |
+
<h1 style="text-align:center"><font color="#0070C0">CH</font>emical <font color="#0070C0">RIS</font>k calculators (CHRIS) Report - Bulk chemicals (v2)</h1>
|
| 40 |
+
</header>
|
| 41 |
+
|
| 42 |
+
<body>
|
| 43 |
+
|
| 44 |
+
<p> The following report was generated using CHRIS-Bulk Chemicals (RST) v.2.0 on
|
| 45 |
+
<script> document.write(new Date().toLocaleDateString()); </script>.
|
| 46 |
+
</p>
|
| 47 |
+
|
| 48 |
+
<h2> Compound </h2>
|
| 49 |
+
|
| 50 |
+
<div class="container">
|
| 51 |
+
<div class="row">
|
| 52 |
+
<div class="column">
|
| 53 |
+
Input :: {{chemName}} <br> <br>
|
| 54 |
+
IUPAC Name :: {{iupac}} <br> <br>
|
| 55 |
+
CAS :: {{cas}} <br> <br>
|
| 56 |
+
Molecular weight (g/mol) :: {{'%0.4f'%MW|float}}
|
| 57 |
+
{% if ceramic %} :: ceramic detected, assume maximum Mw {% endif %}
|
| 58 |
+
<br> <br>
|
| 59 |
+
{% if show_properties %}
|
| 60 |
+
LogKow :: {{LogP}}{{LogP_origin}}<br> <br>
|
| 61 |
+
Density (g/cm<sup>3</sup>) :: {{rho}}{{rho_origin}}<br> <br>
|
| 62 |
+
Melting point (°C) :: {{mp}}{{mp_origin}}<br> <br>
|
| 63 |
+
{% endif %}
|
| 64 |
+
SMILES :: {{smiles}}
|
| 65 |
+
</div>
|
| 66 |
+
<div class="column">
|
| 67 |
+
<img src="{{molImage}}"/>
|
| 68 |
+
</div>
|
| 69 |
+
</div>
|
| 70 |
+
</div>
|
| 71 |
+
|
| 72 |
+
{% if domain_extrap %}
|
| 73 |
+
<font color="red"> Warning: This polymer/solute combination is outside the model's training domain, so the predicted diffusivity may not be accurate. <br> </font>
|
| 74 |
+
{% endif %}
|
| 75 |
+
|
| 76 |
+
<h2> Exposure </h2>
|
| 77 |
+
|
| 78 |
+
<p>
|
| 79 |
+
<u> Diffusion calculation for leaching from {{polymers[pIndex]}} estimates the worst case single day exposure = {{release}} mg. </u>
|
| 80 |
+
<p>
|
| 81 |
+
|
| 82 |
+
<p>
|
| 83 |
+
This estimate was derived using solutions to the conservative plane sheet model for mass release, \( M \):
|
| 84 |
+
|
| 85 |
+
\[
|
| 86 |
+
M(\tau)= \left\{
|
| 87 |
+
\begin{array}{cr}
|
| 88 |
+
2 M_0 \sqrt{\tau/\pi} & \tau \leq 0.2 \\
|
| 89 |
+
M_0\left(1-8 \exp\left[-\tau \pi^2/4 \right]/\pi^2\right) & \tau > 0.2
|
| 90 |
+
\end{array} \right.
|
| 91 |
+
\]
|
| 92 |
+
|
| 93 |
+
where \( \tau= D t A^2 / V^2 \) and \( A \) and \( V \) are the surface area and volume of the polymer matrix, respectively, \( M_0 \) is total mass initially contained in the polymer, \( D \) is a conservative estimate of the diffusion coefficient of the leachable within the polymer matrix, and \( t \) is time. Based on the input provided, the calculation was based on the following values:
|
| 94 |
+
</p>
|
| 95 |
+
|
| 96 |
+
<p>
|
| 97 |
+
\( A \) = {{area}} cm<sup>2</sup> <br>
|
| 98 |
+
\( V \) = {{vol}} cm<sup>3</sup> <br>
|
| 99 |
+
\( M_0 \) = {{amount}} mg <br>
|
| 100 |
+
\( D \) = {{diff}} cm<sup>2</sup>/s <br>
|
| 101 |
+
\( t \) = {{time}} h <br>
|
| 102 |
+
</p>
|
| 103 |
+
|
| 104 |
+
<p>
|
| 105 |
+
In addition to the maximum daily (day 1) release rate, it can be helpful to examine the decay in release rate over time predicted by the model,
|
| 106 |
+
which is illustrated for the first 30 days of exposure in the plot below:
|
| 107 |
+
</p>
|
| 108 |
+
|
| 109 |
+
<img src="{{ image }}"/>
|
| 110 |
+
|
| 111 |
+
<h2> Screening level toxicological risk assessment </h2>
|
| 112 |
+
|
| 113 |
+
<p>
|
| 114 |
+
The threshold for toxicological concern (TTC) for {{exposure}} contact is {{TTC}} mg. Based on the exposure estimation this results in a margin of safety (MOS) of {{MOS}}.
|
| 115 |
+
</p>
|
| 116 |
+
|
| 117 |
+
{% if MOS >= 1 %}
|
| 118 |
+
|
| 119 |
+
<p>
|
| 120 |
+
<font color="green"> The MOS based on the mutagenic TTC is greater than one; therefore, no further analysis is needed for systemic biocompatibility endpoints. </font>
|
| 121 |
+
</p>
|
| 122 |
+
|
| 123 |
+
<p>
|
| 124 |
+
<font color="red"> *Note*: This assessment assumes that the chemical is not in the cohort of concern. </font>
|
| 125 |
+
</p>
|
| 126 |
+
|
| 127 |
+
{% else %}
|
| 128 |
+
|
| 129 |
+
<p>
|
| 130 |
+
<font color="red"> The MOS based on the mutagenic TTC is less than one; therefore, further analysis is needed to address systemic biocompatibility endpoints. For example, a compound specific tolerable intake value could be independently derived and compared to the exposure estimate provided above. </font>
|
| 131 |
+
</p>
|
| 132 |
+
|
| 133 |
+
{% endif %}
|
| 134 |
+
|
| 135 |
+
<button type="button" onclick="javascript:history.back()">Back</button>
|
| 136 |
+
|
| 137 |
+
</body>
|
| 138 |
+
</html>
|
functions.py
CHANGED
|
@@ -68,12 +68,12 @@ def SheetRates(amount, vol, area, time, D):
|
|
| 68 |
def RatePlot(tarray, rates):
|
| 69 |
|
| 70 |
fig, ax = plt.subplots(figsize=(6, 4))
|
| 71 |
-
plt.subplots_adjust(left=0.15, bottom=0.15, right=0.95, top=0.95, wspace=0.0, hspace=0.0)
|
| 72 |
ax.plot(tarray, rates, 'o')
|
| 73 |
ax.set(
|
| 74 |
xlabel='time (days)',
|
| 75 |
ylabel='release rate (mg/day)',
|
| 76 |
)
|
|
|
|
| 77 |
pngImage = io.BytesIO()
|
| 78 |
FigureCanvas(fig).print_png(pngImage)
|
| 79 |
pngImageB64String = "data:image/png;base64,"
|
|
@@ -150,6 +150,7 @@ def PowerLaw(Mw, A, B):
|
|
| 150 |
logD = A+logMw*B
|
| 151 |
return np.exp(logD)
|
| 152 |
|
|
|
|
| 153 |
#def func_piringer(Mw,T,Ap):
|
| 154 |
# if Mw > 1100.: # if molecule is greater than 1100 g/mol, default to that value as worst case
|
| 155 |
# Mw = 1100.
|
|
|
|
| 68 |
def RatePlot(tarray, rates):
|
| 69 |
|
| 70 |
fig, ax = plt.subplots(figsize=(6, 4))
|
|
|
|
| 71 |
ax.plot(tarray, rates, 'o')
|
| 72 |
ax.set(
|
| 73 |
xlabel='time (days)',
|
| 74 |
ylabel='release rate (mg/day)',
|
| 75 |
)
|
| 76 |
+
plt.tight_layout()
|
| 77 |
pngImage = io.BytesIO()
|
| 78 |
FigureCanvas(fig).print_png(pngImage)
|
| 79 |
pngImageB64String = "data:image/png;base64,"
|
|
|
|
| 150 |
logD = A+logMw*B
|
| 151 |
return np.exp(logD)
|
| 152 |
|
| 153 |
+
|
| 154 |
#def func_piringer(Mw,T,Ap):
|
| 155 |
# if Mw > 1100.: # if molecule is greater than 1100 g/mol, default to that value as worst case
|
| 156 |
# Mw = 1100.
|
qrf_functions.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import pickle
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import sklearn
|
| 6 |
+
from quantile_forest import RandomForestQuantileRegressor
|
| 7 |
+
import mordred
|
| 8 |
+
import mordred.descriptors
|
| 9 |
+
import rdkit
|
| 10 |
+
from rdkit import Chem
|
| 11 |
+
|
| 12 |
+
def QRF_Apply(density, polytg, smiles, quantiles=[0.03,0.5,0.97]):
|
| 13 |
+
with open(f'qrf_model_bundle_37.pkl','rb') as f:
|
| 14 |
+
reg, imp, scaler_X, sub_desc_list = pickle.load(f)
|
| 15 |
+
# get list of descriptors to calculate
|
| 16 |
+
solute_desc_list = sub_desc_list.copy()
|
| 17 |
+
if 'Polymer_Tg' in solute_desc_list:
|
| 18 |
+
solute_desc_list.remove('Polymer_Tg')
|
| 19 |
+
if 'Polymer_Density' in solute_desc_list:
|
| 20 |
+
solute_desc_list.remove('Polymer_Density')
|
| 21 |
+
calc_all = mordred.Calculator(mordred.descriptors)
|
| 22 |
+
desc_strs = [str(d) for d in calc_all.descriptors]
|
| 23 |
+
desc_funcs = {d1:df for d1 in solute_desc_list for d2,df in zip(desc_strs,calc_all.descriptors) if d1 == d2}
|
| 24 |
+
calc = mordred.Calculator(list(desc_funcs.values()))
|
| 25 |
+
m = Chem.MolFromSmiles(smiles)
|
| 26 |
+
m = Chem.AddHs(m)
|
| 27 |
+
descs_mordred = calc(m)
|
| 28 |
+
desc_vals = list(descs_mordred.values())
|
| 29 |
+
desc_vals = [d if (isinstance(d,float) or isinstance(d,int)) else np.nan for d in desc_vals] # filter out mordred errors
|
| 30 |
+
desc_vals = dict(zip(solute_desc_list,desc_vals))
|
| 31 |
+
descs = [polytg if n == 'Polymer_Tg' else (density if n == 'Polymer_Density' else desc_vals[n]) for n in sub_desc_list]
|
| 32 |
+
#descs = imp.transform([descs])
|
| 33 |
+
descs = imp.transform(pd.DataFrame([descs],columns=sub_desc_list)) # avoid warning about missing column names
|
| 34 |
+
descs_scale = scaler_X.transform(descs)
|
| 35 |
+
LogD_pred = reg.predict(descs_scale, quantiles=quantiles)
|
| 36 |
+
D_pred = 10**LogD_pred
|
| 37 |
+
if len(D_pred.shape)>1:
|
| 38 |
+
# return 1D array regardless of quantiles setting
|
| 39 |
+
D_pred = D_pred[0]
|
| 40 |
+
## domain extrapolation check
|
| 41 |
+
df_X = pd.read_excel('qrf_x.xlsx')
|
| 42 |
+
X_train = imp.transform(df_X)
|
| 43 |
+
X_train_scale = scaler_X.transform(X_train)
|
| 44 |
+
dij = QRF_DomainExtrap(reg, X_train_scale, descs_scale)
|
| 45 |
+
domain_extrap = dij > 0
|
| 46 |
+
return D_pred, domain_extrap
|
| 47 |
+
|
| 48 |
+
def QRF_DomainExtrap(estimator, X_train, X_test, D=0.0, return_features=False):
|
| 49 |
+
X_min = X_train.min(axis=0)
|
| 50 |
+
X_max = X_train.max(axis=0)
|
| 51 |
+
X_range = X_max-X_min
|
| 52 |
+
dij = np.zeros(len(X_test))
|
| 53 |
+
if return_features: dij_feats = {i:[] for i in range(len(X_test))}
|
| 54 |
+
for tree in estimator.estimators_:
|
| 55 |
+
feature = tree.tree_.feature
|
| 56 |
+
node_indicator = tree.decision_path(X_test)
|
| 57 |
+
node_indicator = node_indicator.toarray().astype(bool)
|
| 58 |
+
#node_indicator = node_indicator.A.astype(bool)
|
| 59 |
+
feats = [feature[r][:-1] for r in node_indicator]
|
| 60 |
+
mins = [np.array([X_min[i] for i in f]) for f in feats]
|
| 61 |
+
maxs = [np.array([X_max[i] for i in f]) for f in feats]
|
| 62 |
+
ranges = [np.array([X_range[i] for i in f]) for f in feats]
|
| 63 |
+
vals = [X_test[i][feats[i]] for i in range(len(feats))]
|
| 64 |
+
for i in range(len(X_test)):
|
| 65 |
+
dmax = (vals[i]-maxs[i])/ranges[i] + D
|
| 66 |
+
dmin = (mins[i]-vals[i])/ranges[i] + D
|
| 67 |
+
dmax[dmax<0] = 0
|
| 68 |
+
dmin[dmin<0] = 0
|
| 69 |
+
dij[i] += np.sum(dmax**2)
|
| 70 |
+
dij[i] += np.sum(dmin**2)
|
| 71 |
+
if return_features:
|
| 72 |
+
if any(dmax>0):
|
| 73 |
+
dij_feats[i].extend(list(feats[i][dmax>0]))
|
| 74 |
+
if any(dmin>0):
|
| 75 |
+
dij_feats[i].extend(list(feats[i][dmin>0]))
|
| 76 |
+
dij = dij**0.5
|
| 77 |
+
if return_features:
|
| 78 |
+
dij_feats = [list(set(dij_feats[i])) if len(dij_feats[i]) else None for i in range(len(X_test))]
|
| 79 |
+
return dij, dij_feats
|
| 80 |
+
else:
|
| 81 |
+
return dij
|
| 82 |
+
|
qrf_model_bundle_37.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5c382d0399e482d55a4810fc0df8a3c0df15d96b61f518fe93bc328b0cf805ed
|
| 3 |
+
size 15581306
|
qrf_train.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pickle
|
| 2 |
+
import numpy as np
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import sklearn
|
| 5 |
+
import sklearn.impute
|
| 6 |
+
from quantile_forest import RandomForestQuantileRegressor
|
| 7 |
+
|
| 8 |
+
T_target = 37
|
| 9 |
+
T_cut = 5
|
| 10 |
+
qhiv, qlov = 0.97, 0.03
|
| 11 |
+
state = 12345
|
| 12 |
+
|
| 13 |
+
if T_target == 37:
|
| 14 |
+
params = {'bootstrap': True, 'max_depth': 7, 'max_features': 0.4, 'max_samples': 1.0, 'min_samples_leaf': 2, 'min_samples_split': 2, 'n_estimators': 1000} # best from -18-2.py w fout<0.040 (and 0.045)
|
| 15 |
+
|
| 16 |
+
if T_target == 50:
|
| 17 |
+
params = {'bootstrap': True, 'max_depth': 6, 'max_features': 0.4, 'max_samples': 1.0, 'min_samples_leaf': 6, 'min_samples_split': 2, 'n_estimators': 1000} # best from -19.py and -19-2.py with fout<0.040
|
| 18 |
+
|
| 19 |
+
## read data
|
| 20 |
+
df_X = pd.read_excel('qrf_x.xlsx')
|
| 21 |
+
df_y = pd.read_excel('qrf_y.xlsx')
|
| 22 |
+
sub_desc_list = list(df_X.columns)
|
| 23 |
+
|
| 24 |
+
## fit transforms
|
| 25 |
+
imp = sklearn.impute.SimpleImputer(missing_values=np.nan, strategy='mean')
|
| 26 |
+
imp.fit(df_X)
|
| 27 |
+
X_all = imp.transform(df_X)
|
| 28 |
+
y_all = np.array(df_y['LogD'])
|
| 29 |
+
#y_all = y_all.reshape(-1,1) # row to column
|
| 30 |
+
scaler_X = sklearn.preprocessing.StandardScaler().fit(X_all)
|
| 31 |
+
X_all_scale = scaler_X.transform(X_all)
|
| 32 |
+
|
| 33 |
+
reg_all = RandomForestQuantileRegressor(random_state=state, n_jobs=-1, **params)
|
| 34 |
+
reg_all.fit(X_all_scale,y_all)
|
| 35 |
+
#reg_all.fit(X_all_scale,y_all_scale.ravel())
|
| 36 |
+
|
| 37 |
+
with open(f'qrf_model_bundle_{T_target}.pkl','wb') as f:
|
| 38 |
+
pickle.dump([reg_all,imp,scaler_X,sub_desc_list],f)
|
qrf_x.xlsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:98ea75f2a01564c9b1d998b699bbd11ee22954e5971c95addf8bb8a87e43fbd1
|
| 3 |
+
size 195153
|
qrf_y.xlsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e8728d1f5c88d133dc396c8b8cb85313b716252d2f9dc7ed1a67b86780814157
|
| 3 |
+
size 35100
|
templates/main.html
CHANGED
|
@@ -62,6 +62,14 @@ This module can be used similarly to the color additive tool, but can be applied
|
|
| 62 |
the total amount of the chemical is known, e.g. from a certificate of analysis.
|
| 63 |
</p>
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
• <a href="/efficiency"> Extraction efficiency </a>
|
| 66 |
|
| 67 |
<p>
|
|
|
|
| 62 |
the total amount of the chemical is known, e.g. from a certificate of analysis.
|
| 63 |
</p>
|
| 64 |
|
| 65 |
+
• <a href="/exposure3"> Bulk chemicals (excluding color additives) RST (v3) </a>
|
| 66 |
+
|
| 67 |
+
<p>
|
| 68 |
+
This module can be used similarly to the color additive tool, but can be applied to any non-color additive bulk additive or impurity (surface impurities / manufacturing
|
| 69 |
+
residuals are excluded) that is present in a polymeric device component provided that the user can justify the chemical is a bulk species and that
|
| 70 |
+
the total amount of the chemical is known, e.g. from a certificate of analysis. Updated to address additional polymers.
|
| 71 |
+
</p>
|
| 72 |
+
|
| 73 |
• <a href="/efficiency"> Extraction efficiency </a>
|
| 74 |
|
| 75 |
<p>
|