Skip to content

Commit 6ae9943

Browse files
committed
Remove code duplication. A bit new fragment-quality functional structure
1 parent 5bf9937 commit 6ae9943

2 files changed

Lines changed: 32 additions & 84 deletions

File tree

src/fairmd/lipids/bin/evaluate_quality.py

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -60,44 +60,30 @@ def _evaluate_op_qualities(simulations) -> int:
6060
for lipidname, lipid in simulation.lipids.items():
6161
md_lipid_ops = simulation.op_data[lipidname]
6262

63+
# TODO: bb is merged into headgroup. But sn-s do not.. Cryptic rule.
64+
# TODO: What to do with other types of lipids?
6365
fragments = mollib.get_fragments(lipid.mapping_dict)
64-
fragment_qual_dict = {}
65-
data_dict = {}
66+
fragment_qual_perexp = {}
67+
lipid_quality_perexp = {}
68+
lipid_quality_perexp = {}
6669

6770
for expid in simulation["EXPERIMENT"]["ORDERPARAMETER"].get(lipidname, []):
6871
print(f"OP quality of simulation data in {simulation['path']}")
6972
print(
7073
f".. evaluating {lipidname} lipid using experimental data from {expid}",
7174
)
72-
OP_qual_data = {}
7375
exp_lipid_ops = opexps.loc(expid).data[lipidname]
74-
exp_error = 0.02 # TODO: hardcoded error value, should be taken from experiment data when available
75-
76-
for key, op_array_ in md_lipid_ops.items():
77-
OP_array = op_array_.copy()
78-
if key not in exp_lipid_ops:
79-
continue
80-
OP_exp_val = exp_lipid_ops[key][0]
81-
if not np.isnan(OP_exp_val):
82-
op_quality = qq.prob_op_within_trustinterval(
83-
op_exp=OP_exp_val,
84-
exp_error=exp_error,
85-
op_sim=OP_array[0],
86-
op_sim_sd=OP_array[2],
87-
)
88-
OP_array += [OP_exp_val, exp_error, op_quality]
89-
OP_qual_data[key] = OP_array
90-
9176
# save qualities of simulation-vs-experiment into a dictionary
92-
data_dict[expid] = OP_qual_data
77+
lipid_quality_perexp[expid] = qq.atomic_quality(exp_lipid_ops, md_lipid_ops)
9378

9479
# calculate quality for molecule fragments headgroup, sn-1, sn-2
95-
# TODO: bb is merged into headgroup. But sn-s do not.. Cryptic rule.
96-
# TODO: What to do with other types of lipids?
97-
fragment_qual_dict[expid] = qq.fragment_quality(fragments, exp_lipid_ops, md_lipid_ops)
80+
_frq = qq.atomic2fragment_quality(lipid_quality_perexp[expid], fragments)
81+
_frw = qq.weights_of_fragments_in_data(fragments, exp_lipid_ops)
82+
# dot product [ qualities * weights ]
83+
fragment_qual_perexp[expid] = {k: _frq[k] * _frw[k] for k in fragments}
9884

9985
# Experiment-merged fragment quality for the lipid
100-
fragment_quality_merged = qq.fragment_quality_unite_multexp(lipidname, fragment_qual_dict, fragments)
86+
fragment_quality_merged = qq.fragment_quality_unite_multexp(lipidname, fragment_qual_perexp, fragments)
10187
system_quality[lipidname] = fragment_quality_merged
10288

10389
# Write FQ for the lipid
@@ -116,9 +102,9 @@ def _evaluate_op_qualities(simulations) -> int:
116102

117103
# write into the OrderParameters_quality.json quality data file
118104
outfile1 = os.path.join(wdir, lipidname + "_OrderParameters_quality.json")
119-
_round_quality_values(data_dict)
105+
_round_quality_values(lipid_quality_perexp)
120106
with open(outfile1, "w") as f:
121-
json.dump(data_dict, f, cls=CompactJSONEncoder)
107+
json.dump(lipid_quality_perexp, f, cls=CompactJSONEncoder)
122108

123109
system_qual_output = qq.systemQuality(system_quality, simulation)
124110
# make system quality file

src/fairmd/lipids/quality.py

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
TODO: remove code duplication and commented code
55
"""
66

7-
import re
87
import warnings
98

109
import numpy as np
@@ -118,69 +117,32 @@ def atomic_quality(exp_op_data: dict, sim_op_data: dict):
118117
:return: dictionary of type {"nC nH": quality value}.
119118
"""
120119
exp_error = 0.02 # TODO: hardcoded error value, should be taken from experiment data when available
120+
121+
# union of keys in exp_op_data and sim_op_data
122+
all_keys = set(exp_op_data.keys()) | set(sim_op_data.keys())
121123
res_dict = {}
122124

123-
for key_exp, value_exp in exp_op_data.items():
124-
if not np.isnan(value_exp[0]) and key_exp in sim_op_data:
125-
q = prob_op_within_trustinterval(
126-
op_exp=value_exp[0],
127-
exp_error=exp_error,
128-
op_sim=sim_op_data[key_exp][0],
129-
op_sim_sd=sim_op_data[key_exp][2],
130-
)
131-
res_dict[key_exp] = q
125+
for key in all_keys:
126+
if key not in exp_op_data or key not in sim_op_data:
127+
res_dict[key] = np.nan
128+
continue
129+
q = prob_op_within_trustinterval(
130+
op_exp=exp_op_data[key][0],
131+
exp_error=exp_error,
132+
op_sim=sim_op_data[key][0],
133+
op_sim_sd=sim_op_data[key][2],
134+
)
135+
res_dict[key] = q
132136

133137
return res_dict
134138

135139

136-
def fragment_quality(fragments: dict, exp_op_data: dict, sim_op_data: dict):
137-
"""
138-
Calculate quality for a fragmented molecule (times their weights in exp data).
139-
140-
Depends on the experiment file what fragments are in this dictionary.
141-
142-
:param fragments: dictionary of type {fragment:lists of unames}.
143-
:param exp_op_data: dictionary of type {op_uname: [op_value]}.
144-
:param sim_op_data: dictionary of type {op_uname: [op_value, op_sigma, op_sd]}.
145-
146-
:return: dictionary of type {fragment: quality value}.
147-
"""
148-
fragment_weights = weights_of_fragments_in_data(fragments, exp_op_data)
149-
exp_error = 0.02 # TODO: hardcoded error value, should be taken from experiment data when available
150-
151-
# empty dictionary with fragment names as keys
152-
fragment_quality = dict.fromkeys(fragments.keys())
153-
140+
def atomic2fragment_quality(atomic_qual_dict: dict, fragments: dict):
141+
fqdict = dict.fromkeys(fragments.keys())
154142
for frg_name, frg_atoms in fragments.items():
155-
E_sum = 0
156-
AV_sum = 0
157-
if fragment_weights[frg_name] == 0:
158-
fragment_quality[frg_name] = np.nan
159-
continue
160-
for key_exp, value_exp in exp_op_data.items():
161-
if (
162-
key_exp.split()[0] in frg_atoms # process for 1 fragm
163-
and not np.isnan(value_exp[0])
164-
and key_exp in sim_op_data
165-
# If the last is not true, then simulation value is missing.
166-
# This allows to happen for, e.g. CH3-groups in
167-
# UA force fields as CH-bond cannot be reconstructed for this carbon.
168-
):
169-
QE = prob_op_within_trustinterval(
170-
op_exp=value_exp[0],
171-
exp_error=exp_error,
172-
op_sim=sim_op_data[key_exp][0],
173-
op_sim_sd=sim_op_data[key_exp][2],
174-
)
175-
E_sum += QE
176-
AV_sum += 1
177-
if AV_sum > 0:
178-
E_F = (E_sum / AV_sum) * fragment_weights[frg_name]
179-
fragment_quality[frg_name] = E_F
180-
else:
181-
fragment_quality[frg_name] = np.nan
182-
183-
return fragment_quality
143+
q_list = [v for k, v in atomic_qual_dict.items() if k.split(" ")[0] in frg_atoms]
144+
fqdict[frg_name] = np.nanmean(q_list) if len(q_list) > 0 else np.nan
145+
return fqdict
184146

185147

186148
def fragment_quality_unite_multexp(

0 commit comments

Comments
 (0)