Skip to content

Commit 51e0e63

Browse files
committed
add atomic point quality dataset generator
1 parent be9bc96 commit 51e0e63

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

developer/gen-atomicq-dataset.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/usr/bin/env python3
2+
3+
from abc import ABC
4+
5+
import numpy as np
6+
import pandas as pd
7+
8+
from fairmd.lipids.api import get_OP
9+
from fairmd.lipids.core import System, initialize_databank
10+
from fairmd.lipids.experiment import ExperimentCollection
11+
12+
13+
class OPQDataError(Exception):
14+
"""Our specific exception"""
15+
16+
17+
class OPQDataStorer(ABC):
18+
def __init__(self, s: System, lname: str) -> None:
19+
self._s = s
20+
self._lname = lname
21+
22+
def _cvt_op_df(self, opdict: dict, err_pos: int) -> pd.DataFrame:
23+
op_clean = pd.DataFrame(
24+
columns=["c", "h", "val", "err"],
25+
)
26+
smi2uname = self._get_smi2uname()
27+
for smid, uname in smi2uname.items():
28+
_c_dict = {
29+
k: [v[0], 0.02 if err_pos >= len(v) else v[err_pos]] # check
30+
for k, v in opdict.items()
31+
if k.split()[0] == uname
32+
}
33+
_c_dict_len = len(_c_dict)
34+
if _c_dict_len == 0:
35+
continue
36+
vearr = np.array([_c_dict.popitem()[1] for _ in range(_c_dict_len)])
37+
if _c_dict_len == 1: # one H
38+
op_clean.loc[len(op_clean)] = [smid, 1, vearr[0, 0], vearr[0, 1]]
39+
elif _c_dict_len == 3: # three H. They are always symmetric.
40+
op_clean.loc[len(op_clean)] = [smid, 1, np.mean(vearr[:, 0]), np.mean(vearr[:, 1])]
41+
elif _c_dict_len == 2: # four H. They could be asymmetric.
42+
vearr = vearr[np.argsort(np.abs(vearr[:, 0]))]
43+
op_clean.loc[len(op_clean)] = [smid, 1, vearr[0, 0], np.mean(vearr[0, 1])]
44+
op_clean.loc[len(op_clean)] = [smid, 2, vearr[1, 0], np.mean(vearr[1, 1])]
45+
else:
46+
msg = f"Unexpected number of H for {uname} in instance {self.ass_id} // {self._lname}: {_c_dict_len}. Cannot store."
47+
raise OPQDataError(msg)
48+
return op_clean.astype({"c": np.int64, "h": np.int64, "val": np.float64, "err": np.float64})
49+
50+
def prepare_sim_dataframe(self) -> None:
51+
"""Prepare dataframe for storing."""
52+
opdict = get_OP(self._s)[self._lname]
53+
self._sim_op = self._cvt_op_df(opdict, 2)
54+
55+
@property
56+
def ass_id(self) -> str:
57+
"""Get id of assoc object"""
58+
return self._s["ID"]
59+
60+
def _get_smi2uname(self) -> dict:
61+
mol = self._s.lipids[self._lname]
62+
smiles = mol.metadata["bioschema_properties"]["smiles"]
63+
print(smiles)
64+
s2u = {}
65+
for uname, aprops in mol.mapping_dict.items():
66+
if "SMILEIDX" in aprops:
67+
smid = int(aprops["SMILEIDX"])
68+
s2u[smid] = uname
69+
if not s2u:
70+
# NO SMILEIDX. Cannot store.
71+
msg = f"Instance {self.ass_id} // {self._lname} cannot be stored: we don't have SMILEIDX."
72+
raise OPQDataError(msg)
73+
return dict(sorted(s2u.items()))
74+
75+
def add_experiment_data(self, exp_opdict: dict) -> None:
76+
"""Add experimental OP data to the storer. We will use it for Q estimation."""
77+
if not hasattr(self, "_exp_opdict"):
78+
self._exp_opdicts = []
79+
self._exp_opdicts.append(self._cvt_op_df(exp_opdict, 1))
80+
81+
def average_experiment_data(self) -> None:
82+
"""Average experimental OP data if we have more than one."""
83+
concdf = pd.concat(self._exp_opdicts, ignore_index=True)
84+
self._exp_opdict = concdf.groupby(["c", "h"]).mean().reset_index()
85+
86+
def compute_q_points(self) -> None:
87+
"""Compute shift btw simulation and mean(exp) datapoints"""
88+
df1 = self._sim_op.merge(
89+
self._exp_opdict,
90+
on=["c", "h"],
91+
how="inner",
92+
suffixes=("_s", "_e"),
93+
)
94+
df1["val_e"] = df1["val_e"].abs() * np.sign(df1["val_s"])
95+
self._qpoints = df1
96+
97+
def store_to_hdf5(self, hdf_fname: str) -> None:
98+
"""Store the record"""
99+
_mcontent = self._s["COMPOSITION"]
100+
mcontent = {"name": [], "inchikey": [], "number": [], "asymmetry": []}
101+
for lname, lip in self._s.lipids.items():
102+
ik = lip.metadata["bioschema_properties"]["inChIKey"]
103+
cnt = _mcontent[lname]["COUNT"]
104+
if isinstance(cnt, int):
105+
asm = np.nan
106+
cnt = [cnt / 2, cnt / 2]
107+
else:
108+
asm = cnt[0] / sum(cnt)
109+
mcontent["name"] += [lname]
110+
mcontent["inchikey"] += [ik]
111+
mcontent["number"] += [sum(cnt) / 2]
112+
mcontent["asymmetry"] += [asm]
113+
hydration = self._s.get_hydration()
114+
scontent = self._s.solution_composition(basis="molar")
115+
temperature = self._s["TEMPERATURE"]
116+
inchikey = self._s.lipids[self._lname].metadata["bioschema_properties"]["inChIKey"]
117+
smiles = self._s.lipids[self._lname].metadata["bioschema_properties"]["smiles"]
118+
ff_name = self._s.readme.get("FF", False)
119+
# store all vars and df to the HDF5 table
120+
group = f"SIM_{self._s['ID']}__{self._lname}"
121+
with pd.HDFStore(hdf_fname, "a") as store:
122+
# DataFrame table
123+
store.put(f"{group}/op_values", self._qpoints, format="table", data_columns=True)
124+
store.put(f"{group}/simulation_table", pd.DataFrame(mcontent), format="table", data_columns=True)
125+
# Metadata attributes - I
126+
op_storer = store.get_storer(f"{group}/op_values")
127+
upd_attr = {
128+
"inchikey": inchikey,
129+
"smiles": smiles,
130+
"fmdl_simid": self._s["ID"],
131+
}
132+
for k, v in upd_attr.items():
133+
op_storer.attrs[k] = v
134+
# -//- II
135+
sample_storer = store.get_storer(f"{group}/simulation_table")
136+
upd_attr = {
137+
"temperature": temperature,
138+
"hydration": hydration,
139+
"solution": ", ".join([f"{k:<25} {v * 100:>6.1f}%" for k, v in sorted(scontent.items())]),
140+
}
141+
if ff_name:
142+
upd_attr["ff_name"] = ff_name
143+
for k, v in upd_attr.items():
144+
sample_storer.attrs[k] = v
145+
146+
147+
H5_QSIMS_MASTER = "atomic-opq-dataset.h5"
148+
149+
def load_sims() -> None:
150+
print("Generating OP dataset from simulations.")
151+
exps = ExperimentCollection.load_from_data("OPExperiment")
152+
sims = initialize_databank()
153+
for sim in sims:
154+
paired_opedict = sim["EXPERIMENT"].get("ORDERPARAMETER", {})
155+
for lname in sim.lipids:
156+
if len(paired_opedict.get(lname, [])) == 0:
157+
continue
158+
print(sim)
159+
160+
ods = OPQDataStorer(sim, lname)
161+
try:
162+
ods.prepare_sim_dataframe()
163+
except OPQDataError as e:
164+
print("ERROR: ", e)
165+
continue
166+
167+
for expid in paired_opedict[lname]:
168+
_exp = exps.loc(expid)
169+
ods.add_experiment_data(_exp.data[lname])
170+
ods.average_experiment_data()
171+
172+
ods.compute_q_points()
173+
ods.store_to_hdf5(H5_QSIMS_MASTER)
174+
175+
176+
if __name__ == "__main__":
177+
load_sims()

0 commit comments

Comments
 (0)