gnnepcsaft-mcp-server 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of gnnepcsaft-mcp-server might be problematic. Click here for more details.
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/PKG-INFO +1 -1
- gnnepcsaft_mcp_server-0.1.2/gnnepcsaft_mcp_server/mcp_server.py +49 -0
- gnnepcsaft_mcp_server-0.1.2/gnnepcsaft_mcp_server/utils.py +328 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/pyproject.toml +4 -1
- gnnepcsaft_mcp_server-0.1.1/gnnepcsaft_mcp_server/mcp_server.py +0 -40
- gnnepcsaft_mcp_server-0.1.1/gnnepcsaft_mcp_server/utils.py +0 -136
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/README.md +0 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/__init__.py +0 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/models/.gitignore +0 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/models/assoc_8.onnx +0 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/models/assoc_8.onnx.data +0 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/models/msigmae_7.onnx +0 -0
- {gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/models/msigmae_7.onnx.data +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"GNNePCSAFT MCP Server"
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, List
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from .utils import (
|
|
8
|
+
batch_convert_pure_density_to_kg_per_m3,
|
|
9
|
+
batch_critical_points,
|
|
10
|
+
batch_inchi_to_smiles,
|
|
11
|
+
batch_molecular_weights,
|
|
12
|
+
batch_pa_to_bar,
|
|
13
|
+
batch_predict_epcsaft_parameters,
|
|
14
|
+
batch_pure_density,
|
|
15
|
+
batch_pure_h_lv,
|
|
16
|
+
batch_pure_vapor_pressure,
|
|
17
|
+
batch_smiles_to_inchi,
|
|
18
|
+
mixture_density,
|
|
19
|
+
mixture_phase,
|
|
20
|
+
mixture_vapor_pressure,
|
|
21
|
+
pubchem_description,
|
|
22
|
+
pure_phase,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
mcp = FastMCP("gnnepcsaft")
|
|
26
|
+
fn_list: List[Callable[..., Any]] = [
|
|
27
|
+
batch_convert_pure_density_to_kg_per_m3,
|
|
28
|
+
batch_critical_points,
|
|
29
|
+
batch_inchi_to_smiles,
|
|
30
|
+
batch_molecular_weights,
|
|
31
|
+
batch_pa_to_bar,
|
|
32
|
+
batch_predict_epcsaft_parameters,
|
|
33
|
+
batch_pure_density,
|
|
34
|
+
batch_pure_h_lv,
|
|
35
|
+
batch_pure_vapor_pressure,
|
|
36
|
+
batch_smiles_to_inchi,
|
|
37
|
+
mixture_density,
|
|
38
|
+
mixture_phase,
|
|
39
|
+
mixture_vapor_pressure,
|
|
40
|
+
pubchem_description,
|
|
41
|
+
pure_phase,
|
|
42
|
+
]
|
|
43
|
+
for fn in fn_list:
|
|
44
|
+
mcp.add_tool(fn)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run():
|
|
48
|
+
"run stdio"
|
|
49
|
+
mcp.run("stdio")
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"Utils for MCP Server"
|
|
2
|
+
|
|
3
|
+
from json import loads
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Literal, Tuple
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
from urllib.request import HTTPError, urlopen
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import onnxruntime as ort
|
|
11
|
+
from gnnepcsaft.data.ogb_utils import smiles2graph
|
|
12
|
+
from gnnepcsaft.data.rdkit_util import assoc_number, inchitosmiles, mw, smilestoinchi
|
|
13
|
+
from gnnepcsaft.epcsaft.epcsaft_feos import (
|
|
14
|
+
critical_points_feos,
|
|
15
|
+
mix_den_feos,
|
|
16
|
+
mix_vp_feos,
|
|
17
|
+
pure_den_feos,
|
|
18
|
+
pure_h_lv_feos,
|
|
19
|
+
pure_vp_feos,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
file_dir = Path(__file__).parent
|
|
23
|
+
model_dir = file_dir / "models"
|
|
24
|
+
ort.set_default_logger_severity(3)
|
|
25
|
+
|
|
26
|
+
msigmae_onnx = ort.InferenceSession(model_dir / "msigmae_7.onnx")
|
|
27
|
+
assoc_onnx = ort.InferenceSession(model_dir / "assoc_8.onnx")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def predict_epcsaft_parameters(
|
|
31
|
+
smiles: str,
|
|
32
|
+
) -> List[float]:
|
|
33
|
+
"""Predict ePC-SAFT parameters
|
|
34
|
+
`[m, sigma, epsilon/kB, kappa_ab, epsilon_ab/kB, dipole moment, na, nb]` with
|
|
35
|
+
the GNNePCSAFT model.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
smiles (str): SMILES of the molecule.
|
|
39
|
+
"""
|
|
40
|
+
lower_bounds = np.asarray([1.0, 1.9, 50.0, 0.0, 0.0, 0, 0, 0])
|
|
41
|
+
upper_bounds = np.asarray([25.0, 4.5, 550.0, 0.9, 5000.0, np.inf, np.inf, np.inf])
|
|
42
|
+
|
|
43
|
+
inchi = smilestoinchi(smiles)
|
|
44
|
+
|
|
45
|
+
graph = smiles2graph(smiles)
|
|
46
|
+
na, nb = assoc_number(inchi)
|
|
47
|
+
x, edge_index, edge_attr = (
|
|
48
|
+
graph["node_feat"],
|
|
49
|
+
graph["edge_index"],
|
|
50
|
+
graph["edge_feat"],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
assoc = 10 ** (
|
|
54
|
+
assoc_onnx.run(
|
|
55
|
+
None,
|
|
56
|
+
{
|
|
57
|
+
"x": x,
|
|
58
|
+
"edge_index": edge_index,
|
|
59
|
+
"edge_attr": edge_attr,
|
|
60
|
+
},
|
|
61
|
+
)[0][0]
|
|
62
|
+
* np.asarray([-1.0, 1.0])
|
|
63
|
+
)
|
|
64
|
+
if na == 0 and nb == 0:
|
|
65
|
+
assoc *= 0
|
|
66
|
+
msigmae = msigmae_onnx.run(
|
|
67
|
+
None,
|
|
68
|
+
{
|
|
69
|
+
"x": x,
|
|
70
|
+
"edge_index": edge_index,
|
|
71
|
+
"edge_attr": edge_attr,
|
|
72
|
+
},
|
|
73
|
+
)[0][0]
|
|
74
|
+
munanb = np.asarray([0.0, na, nb])
|
|
75
|
+
pred = np.hstack([msigmae, assoc, munanb], dtype=np.float64)
|
|
76
|
+
np.clip(pred, lower_bounds, upper_bounds, out=pred)
|
|
77
|
+
|
|
78
|
+
return pred.tolist() # type: ignore
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def pure_phase(
|
|
82
|
+
vapor_pressure: float, system_pressure: float
|
|
83
|
+
) -> Literal["liquid", "vapor"]:
|
|
84
|
+
"""
|
|
85
|
+
Given the vapor pressure and system pressure, return the phase of the molecule.
|
|
86
|
+
Both pressures must be in the same unit.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
vapor_pressure (float): The calculated vapor pressure of the pure component.
|
|
90
|
+
system_pressure (float): The actual system pressure.
|
|
91
|
+
|
|
92
|
+
"""
|
|
93
|
+
assert isinstance(vapor_pressure, (int, float)), "vapor_pressure must be a number"
|
|
94
|
+
assert isinstance(system_pressure, (int, float)), "system_pressure must be a number"
|
|
95
|
+
assert vapor_pressure > 0, "vapor_pressure must be positive"
|
|
96
|
+
assert system_pressure > 0, "system_pressure must be positive"
|
|
97
|
+
|
|
98
|
+
return "liquid" if vapor_pressure < system_pressure else "vapor"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def mixture_phase(
|
|
102
|
+
bubble_point: float,
|
|
103
|
+
dew_point: float,
|
|
104
|
+
system_pressure: float,
|
|
105
|
+
) -> Literal["liquid", "vapor", "two-phase"]:
|
|
106
|
+
"""
|
|
107
|
+
Given the bubble/dew point of the mixture and the system pressure,
|
|
108
|
+
return the phase of the mixture.
|
|
109
|
+
All pressures must be in the same unit.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
bubble_point (float): The calculated bubble point of the mixture.
|
|
113
|
+
dew_point (float): The calculated dew point of the mixture.
|
|
114
|
+
system_pressure (float): The actual system pressure.
|
|
115
|
+
"""
|
|
116
|
+
assert isinstance(bubble_point, (int, float)), "bubble_point must be a number"
|
|
117
|
+
assert isinstance(dew_point, (int, float)), "dew_point must be a number"
|
|
118
|
+
assert isinstance(system_pressure, (int, float)), "system_pressure must be a number"
|
|
119
|
+
assert bubble_point > 0, "bubble_point must be positive"
|
|
120
|
+
assert dew_point > 0, "dew_point must be positive"
|
|
121
|
+
assert system_pressure > 0, "system_pressure must be positive"
|
|
122
|
+
return (
|
|
123
|
+
"liquid"
|
|
124
|
+
if bubble_point < system_pressure
|
|
125
|
+
else ("two-phase" if dew_point <= system_pressure else "vapor")
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def pubchem_description(smiles: str) -> str:
|
|
130
|
+
"""
|
|
131
|
+
Look for information on PubChem for the SMILES.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
smiles (str): The SMILES of the molecule.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
inchi = smilestoinchi(smiles)
|
|
139
|
+
url = (
|
|
140
|
+
"https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/inchi/description/json?inchi="
|
|
141
|
+
+ quote(inchi, safe="")
|
|
142
|
+
)
|
|
143
|
+
with urlopen(url) as ans:
|
|
144
|
+
ans = loads(ans.read().decode("utf8").strip())
|
|
145
|
+
except (TypeError, HTTPError, ValueError):
|
|
146
|
+
ans = "no data available on this molecule in PubChem."
|
|
147
|
+
return ans
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def mixture_density(
|
|
151
|
+
parameters: List[List[float]],
|
|
152
|
+
state: List[float],
|
|
153
|
+
kij_matrix: List[List[float]],
|
|
154
|
+
) -> float:
|
|
155
|
+
"""Calculates mixture liquid density (mol/m³) with ePC-SAFT.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
parameters: A list of
|
|
159
|
+
`[m, sigma, epsilon/kB, kappa_ab, epsilon_ab/kB, dipole moment, na, nb, MW]`
|
|
160
|
+
for each component of the mixture
|
|
161
|
+
state: A list with the state of the mixture
|
|
162
|
+
`[Temperature (K), Pressure (Pa), mole_fractions_1, mole_fractions_2, ...]`
|
|
163
|
+
kij_matrix: A matrix of binary interaction parameters
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
return mix_den_feos(parameters, state, kij_matrix)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def mixture_vapor_pressure(
|
|
170
|
+
parameters: List[List[float]],
|
|
171
|
+
state: List[float],
|
|
172
|
+
kij_matrix: List[List[float]],
|
|
173
|
+
) -> Tuple[float, float]:
|
|
174
|
+
"""Calculates mixture `(Bubble point (Pa), Dew point (Pa))` with ePC-SAFT.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
parameters: A list of
|
|
178
|
+
`[m, sigma, epsilon/kB, kappa_ab, epsilon_ab/kB, dipole moment, na, nb, MW]`
|
|
179
|
+
for each component of the mixture
|
|
180
|
+
state: A list with the state of the mixture
|
|
181
|
+
`[Temperature (K), Pressure (Pa), mole_fractions_1, molefractions_2, ...]`.
|
|
182
|
+
The pressure should be any `float` value since it's not used in the calculation.
|
|
183
|
+
kij_matrix: A matrix of binary interaction parameters.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
return mix_vp_feos(parameters, state, kij_matrix)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def batch_predict_epcsaft_parameters(
|
|
190
|
+
smiles: List[str],
|
|
191
|
+
) -> List[List[float]]:
|
|
192
|
+
"""Predict ePC-SAFT parameters
|
|
193
|
+
`[m, sigma, epsilon/kB, kappa_ab, epsilon_ab/kB, dipole moment, na, nb]`
|
|
194
|
+
for a list of SMILES with the GNNePCSAFT model.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
smiles (List[str]): SMILES of the molecules.
|
|
198
|
+
"""
|
|
199
|
+
return [predict_epcsaft_parameters(smi) for smi in smiles]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def batch_molecular_weights(
|
|
203
|
+
smiles: List[str],
|
|
204
|
+
) -> List[float]:
|
|
205
|
+
"""Calcultes molecular weight in `g/mol` for a list of SMILES
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
smiles (List[str]): SMILES of the molecules.
|
|
209
|
+
"""
|
|
210
|
+
inchi_list = [smilestoinchi(smi) for smi in smiles]
|
|
211
|
+
return [mw(inchi) for inchi in inchi_list]
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def batch_inchi_to_smiles(
|
|
215
|
+
inchi_list: List[str],
|
|
216
|
+
) -> List[str]:
|
|
217
|
+
"""Transform a list of InChI to SMILES.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
inchi_list (List[str]): List of InChI
|
|
221
|
+
"""
|
|
222
|
+
return [inchitosmiles(inchi) for inchi in inchi_list]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def batch_smiles_to_inchi(
|
|
226
|
+
smiles_list: List[str],
|
|
227
|
+
) -> List[str]:
|
|
228
|
+
"""Transform a list of SMILES to InChI.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
smiles_list (List[str]): List of SMILES
|
|
232
|
+
"""
|
|
233
|
+
return [smilestoinchi(smi) for smi in smiles_list]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def batch_pure_density(
|
|
237
|
+
smiles_list: List[str],
|
|
238
|
+
state: List[float],
|
|
239
|
+
) -> List[float]:
|
|
240
|
+
"""Calculates pure liquid density in `kg/m³` with ePC-SAFT for a list of SMILES.
|
|
241
|
+
The state is the same for all molecules. The GNNePCSAFT model is used to predict
|
|
242
|
+
ePCSAFT parameters.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
smiles_list (List[str]): List of SMILES
|
|
246
|
+
state: A list with
|
|
247
|
+
`[Temperature (K), Pressure (Pa)]`
|
|
248
|
+
"""
|
|
249
|
+
return [
|
|
250
|
+
pure_den_feos(predict_epcsaft_parameters(smi), state) for smi in smiles_list
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def batch_pure_vapor_pressure(
|
|
255
|
+
smiles_list: List[str],
|
|
256
|
+
temperature: float,
|
|
257
|
+
) -> List[float]:
|
|
258
|
+
"""Calculates pure vapor pressure in `Pa` with ePC-SAFT for a list of SMILES.
|
|
259
|
+
The temperature is the same for all molecules. The GNNePCSAFT model is used to predict
|
|
260
|
+
ePCSAFT parameters.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
smiles_list (List[str]): List of SMILES
|
|
264
|
+
temperature: `Temperature (K)`
|
|
265
|
+
"""
|
|
266
|
+
return [
|
|
267
|
+
pure_vp_feos(predict_epcsaft_parameters(smi), [temperature])
|
|
268
|
+
for smi in smiles_list
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def batch_pure_h_lv(
|
|
273
|
+
smiles_list: List[str],
|
|
274
|
+
temperature: float,
|
|
275
|
+
) -> List[float]:
|
|
276
|
+
"""Calculates pure liquid enthalpy of vaporization in `kJ/mol`
|
|
277
|
+
with ePC-SAFT for a list of SMILES.
|
|
278
|
+
The temperature is the same for all molecules.
|
|
279
|
+
The GNNePCSAFT model is used to predict
|
|
280
|
+
ePCSAFT parameters.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
smiles_list (List[str]): List of SMILES
|
|
284
|
+
temperature: `Temperature (K)`
|
|
285
|
+
"""
|
|
286
|
+
return [
|
|
287
|
+
pure_h_lv_feos(predict_epcsaft_parameters(smi), [temperature])
|
|
288
|
+
for smi in smiles_list
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def batch_critical_points(
|
|
293
|
+
smiles_list: List[str],
|
|
294
|
+
) -> List[List[float]]:
|
|
295
|
+
"""
|
|
296
|
+
Calculates critical points `[Temperature (K), Pressure (Pa), Density (mol/m³)]` with ePC-SAFT
|
|
297
|
+
for a list of SMILES. The GNNePCSAFT model is used to predict ePCSAFT parameters.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
smiles_list (List[str]): List of SMILES
|
|
301
|
+
"""
|
|
302
|
+
return [
|
|
303
|
+
critical_points_feos(predict_epcsaft_parameters(smi)) for smi in smiles_list
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def batch_pa_to_bar(
|
|
308
|
+
pressure_in_pa_list: List[float],
|
|
309
|
+
) -> List[float]:
|
|
310
|
+
"""Convert a list of pressure from `Pa` to `bar`.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
pressure_in_pa_list (List[float]): List of pressure in `Pa`
|
|
314
|
+
"""
|
|
315
|
+
return [pa / 100_000.0 for pa in pressure_in_pa_list]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def batch_convert_pure_density_to_kg_per_m3(
|
|
319
|
+
density_list: List[float],
|
|
320
|
+
molecular_weight_list: List[float],
|
|
321
|
+
) -> List[float]:
|
|
322
|
+
"""Convert a list of density from `mol/m³` to `kg/m³`
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
density_list (List[float]): List of density in `mol/m³`
|
|
326
|
+
molecular_weight_list (List[float]): List of molecular weight in `g/mol`
|
|
327
|
+
"""
|
|
328
|
+
return [den * molw / 1000 for den, molw in zip(density_list, molecular_weight_list)]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gnnepcsaft-mcp-server"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Model Context Protocol server for GNNePCSAFT tools"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "wildsonbbl",email = "wil_bbl@hotmail.com"}
|
|
@@ -26,6 +26,9 @@ include = ["gnnepcsaft_mcp_server/models/assoc_8.onnx", "gnnepcsaft_mcp_server/m
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
[tool.poetry.group.dev.dependencies]
|
|
30
|
+
pytest = "^8.3.5"
|
|
31
|
+
|
|
29
32
|
[build-system]
|
|
30
33
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
31
34
|
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"GNNePCSAFT MCP Server"
|
|
2
|
-
|
|
3
|
-
from typing import Any, Callable, List
|
|
4
|
-
|
|
5
|
-
from gnnepcsaft.data.rdkit_util import inchitosmiles, mw, smilestoinchi
|
|
6
|
-
from gnnepcsaft.epcsaft.epcsaft_feos import (
|
|
7
|
-
critical_points_feos,
|
|
8
|
-
mix_den_feos,
|
|
9
|
-
mix_vp_feos,
|
|
10
|
-
pure_den_feos,
|
|
11
|
-
pure_h_lv_feos,
|
|
12
|
-
pure_vp_feos,
|
|
13
|
-
)
|
|
14
|
-
from mcp.server.fastmcp import FastMCP
|
|
15
|
-
|
|
16
|
-
from .utils import mixture_phase, prediction, pubchem_description, pure_phase
|
|
17
|
-
|
|
18
|
-
mcp = FastMCP("gnnepcsaft")
|
|
19
|
-
fn_list: List[Callable[..., Any]] = [
|
|
20
|
-
pure_vp_feos,
|
|
21
|
-
pure_den_feos,
|
|
22
|
-
mix_den_feos,
|
|
23
|
-
mix_vp_feos,
|
|
24
|
-
pure_phase,
|
|
25
|
-
mixture_phase,
|
|
26
|
-
pubchem_description,
|
|
27
|
-
mw,
|
|
28
|
-
smilestoinchi,
|
|
29
|
-
prediction,
|
|
30
|
-
inchitosmiles,
|
|
31
|
-
pure_h_lv_feos,
|
|
32
|
-
critical_points_feos,
|
|
33
|
-
]
|
|
34
|
-
for fn in fn_list:
|
|
35
|
-
mcp.add_tool(fn)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def run():
|
|
39
|
-
"run stdio"
|
|
40
|
-
mcp.run("stdio")
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
"Utils for MCP Server"
|
|
2
|
-
|
|
3
|
-
from json import loads
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import List, Literal
|
|
6
|
-
from urllib.parse import quote
|
|
7
|
-
from urllib.request import HTTPError, urlopen
|
|
8
|
-
|
|
9
|
-
import numpy as np
|
|
10
|
-
import onnxruntime as ort
|
|
11
|
-
from gnnepcsaft.data.ogb_utils import smiles2graph
|
|
12
|
-
from gnnepcsaft.data.rdkit_util import assoc_number, smilestoinchi
|
|
13
|
-
|
|
14
|
-
file_dir = Path(__file__).parent
|
|
15
|
-
model_dir = file_dir / "models"
|
|
16
|
-
ort.set_default_logger_severity(3)
|
|
17
|
-
|
|
18
|
-
msigmae_onnx = ort.InferenceSession(model_dir / "msigmae_7.onnx")
|
|
19
|
-
assoc_onnx = ort.InferenceSession(model_dir / "assoc_8.onnx")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def prediction(
|
|
23
|
-
smiles: str,
|
|
24
|
-
) -> List[float]:
|
|
25
|
-
"""Predict ePC-SAFT parameters
|
|
26
|
-
`[m, sigma, epsilon/kB, kappa_ab, epsilon_ab/kB, dipole moment, na, nb]`
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
smiles (str): SMILES of the molecule.
|
|
30
|
-
"""
|
|
31
|
-
lower_bounds = np.asarray([1.0, 1.9, 50.0, 0.0, 0.0, 0, 0, 0])
|
|
32
|
-
upper_bounds = np.asarray([25.0, 4.5, 550.0, 0.9, 5000.0, np.inf, np.inf, np.inf])
|
|
33
|
-
|
|
34
|
-
inchi = smilestoinchi(smiles)
|
|
35
|
-
|
|
36
|
-
graph = smiles2graph(smiles)
|
|
37
|
-
na, nb = assoc_number(inchi)
|
|
38
|
-
x, edge_index, edge_attr = (
|
|
39
|
-
graph["node_feat"],
|
|
40
|
-
graph["edge_index"],
|
|
41
|
-
graph["edge_feat"],
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
assoc = 10 ** (
|
|
45
|
-
assoc_onnx.run(
|
|
46
|
-
None,
|
|
47
|
-
{
|
|
48
|
-
"x": x,
|
|
49
|
-
"edge_index": edge_index,
|
|
50
|
-
"edge_attr": edge_attr,
|
|
51
|
-
},
|
|
52
|
-
)[0][0]
|
|
53
|
-
* np.asarray([-1.0, 1.0])
|
|
54
|
-
)
|
|
55
|
-
if na == 0 and nb == 0:
|
|
56
|
-
assoc *= 0
|
|
57
|
-
msigmae = msigmae_onnx.run(
|
|
58
|
-
None,
|
|
59
|
-
{
|
|
60
|
-
"x": x,
|
|
61
|
-
"edge_index": edge_index,
|
|
62
|
-
"edge_attr": edge_attr,
|
|
63
|
-
},
|
|
64
|
-
)[0][0]
|
|
65
|
-
munanb = np.asarray([0.0, na, nb])
|
|
66
|
-
pred = np.hstack([msigmae, assoc, munanb], dtype=np.float64)
|
|
67
|
-
np.clip(pred, lower_bounds, upper_bounds, out=pred)
|
|
68
|
-
|
|
69
|
-
return pred.tolist() # type: ignore
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def pure_phase(
|
|
73
|
-
vapor_pressure: float, system_pressure: float
|
|
74
|
-
) -> Literal["liquid", "vapor"]:
|
|
75
|
-
"""
|
|
76
|
-
Given the vapor pressure and system pressure, return the phase of the molecule.
|
|
77
|
-
Both pressures must be in the same unit.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
vapor_pressure (float): The calculated vapor pressure of the pure component.
|
|
81
|
-
system_pressure (float): The actual system pressure.
|
|
82
|
-
|
|
83
|
-
"""
|
|
84
|
-
assert isinstance(vapor_pressure, (int, float)), "vapor_pressure must be a number"
|
|
85
|
-
assert isinstance(system_pressure, (int, float)), "system_pressure must be a number"
|
|
86
|
-
assert vapor_pressure > 0, "vapor_pressure must be positive"
|
|
87
|
-
assert system_pressure > 0, "system_pressure must be positive"
|
|
88
|
-
|
|
89
|
-
return "liquid" if vapor_pressure < system_pressure else "vapor"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def mixture_phase(
|
|
93
|
-
bubble_point: float,
|
|
94
|
-
dew_point: float,
|
|
95
|
-
system_pressure: float,
|
|
96
|
-
) -> Literal["liquid", "vapor", "two-phase"]:
|
|
97
|
-
"""
|
|
98
|
-
Given the bubble/dew point of the mixture and the system pressure,
|
|
99
|
-
return the phase of the mixture.
|
|
100
|
-
All pressures must be in the same unit.
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
bubble_point (float): The calculated bubble point of the mixture.
|
|
104
|
-
dew_point (float): The calculated dew point of the mixture.
|
|
105
|
-
system_pressure (float): The actual system pressure.
|
|
106
|
-
"""
|
|
107
|
-
assert isinstance(bubble_point, (int, float)), "bubble_point must be a number"
|
|
108
|
-
assert isinstance(dew_point, (int, float)), "dew_point must be a number"
|
|
109
|
-
assert isinstance(system_pressure, (int, float)), "system_pressure must be a number"
|
|
110
|
-
assert bubble_point > 0, "bubble_point must be positive"
|
|
111
|
-
assert dew_point > 0, "dew_point must be positive"
|
|
112
|
-
assert system_pressure > 0, "system_pressure must be positive"
|
|
113
|
-
return (
|
|
114
|
-
"liquid"
|
|
115
|
-
if bubble_point < system_pressure
|
|
116
|
-
else ("two-phase" if dew_point <= system_pressure else "vapor")
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def pubchem_description(inchi: str) -> str:
|
|
121
|
-
"""
|
|
122
|
-
Look for information on PubChem for the InChI.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
inchi (str): The InChI of the molecule.
|
|
126
|
-
"""
|
|
127
|
-
url = (
|
|
128
|
-
"https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/inchi/description/json?inchi="
|
|
129
|
-
+ quote(inchi, safe="")
|
|
130
|
-
)
|
|
131
|
-
try:
|
|
132
|
-
with urlopen(url) as ans:
|
|
133
|
-
ans = loads(ans.read().decode("utf8").strip())
|
|
134
|
-
except (TypeError, HTTPError, ValueError):
|
|
135
|
-
ans = "no data available on this molecule in PubChem."
|
|
136
|
-
return ans
|
|
File without changes
|
{gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/__init__.py
RENAMED
|
File without changes
|
{gnnepcsaft_mcp_server-0.1.1 → gnnepcsaft_mcp_server-0.1.2}/gnnepcsaft_mcp_server/models/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|