Robert Elder commited on
Commit
d21d254
·
1 Parent(s): f9ba676

adding exposure3 module with QRF model

Browse files
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
+ (&lt;= 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 (&lt;= 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 &gt;
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 &gt;=
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

  • SHA256: 309a923bc8a29fa479d28c5421f671274bf5c08667207630467bce622fa635fb
  • Pointer size: 130 Bytes
  • Size of remote file: 90.2 kB
exposure3_module/static/images/FDAlogo.png ADDED

Git LFS Details

  • SHA256: b77c9db36215bee929580dba4ba52fe335ab4d35c750cc60e1d1922d348a4dd3
  • Pointer size: 130 Bytes
  • Size of remote file: 26.2 kB
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"> &#129318; </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 (&deg;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"> &#129318; </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" >&#9432;</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">&#9432;</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 (&deg;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 &#34;Other polymer&#34;. 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 &#34;Other polymer&#34;, 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">&#9432;</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
+ &nbsp; &rArr; 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, &#8804; 24 hours = limited.
137
+ For limited exposures (&#8804; 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"> &#129318; </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 (&deg;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 (&deg;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
  &#x2022; <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
+ &#x2022; <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
  &#x2022; <a href="/efficiency"> Extraction efficiency </a>
74
 
75
  <p>