diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6e5be2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +.vscode +__pycache__/ +PKG-INFO \ No newline at end of file diff --git a/iris_validation/__init__.py b/iris_validation/__init__.py index e54a19a..18b689a 100644 --- a/iris_validation/__init__.py +++ b/iris_validation/__init__.py @@ -1,41 +1,61 @@ import os -import subprocess -import json from iris_validation.graphics import Panel from iris_validation.metrics import metrics_model_series_from_files -def generate_report(latest_model_path, - latest_reflections_path=None, - latest_sequence_path=None, - latest_distpred_path=None, - previous_model_path=None, - previous_reflections_path=None, - previous_sequence_path=None, - previous_distpred_path=None, - run_covariance=False, - run_molprobity=False, - calculate_rama_z=True, - multiprocessing=True, - wrap_in_html=True, - output_dir=None): +def generate_report( + latest_model_path, + latest_reflections_path=None, + latest_sequence_path=None, + latest_distpred_path=None, + previous_model_path=None, + previous_reflections_path=None, + previous_sequence_path=None, + previous_distpred_path=None, + run_covariance=False, + run_molprobity=False, + calculate_rama_z=True, + multiprocessing=True, + latest_model_metrics_json=None, + previous_model_metrics_json=None, + data_with_percentiles=None, # only works with model_metrics_json files + discrete_metrics_to_display=None, + continuous_metrics_to_display=None, + residue_bars_to_display=None, + percentile_bar_label=None, + percentile_bar_range=None, + wrap_in_html=True, + output_dir=None, +): model_paths = (previous_model_path, latest_model_path) reflections_paths = (previous_reflections_path, latest_reflections_path) sequence_paths = (previous_sequence_path, latest_sequence_path) distpred_paths = (previous_distpred_path, latest_distpred_path) + model_json_paths = (previous_model_metrics_json, latest_model_metrics_json) - model_series = metrics_model_series_from_files(model_paths, - reflections_paths, - sequence_paths, - distpred_paths, - run_covariance, - run_molprobity, - calculate_rama_z, - multiprocessing) + model_series = metrics_model_series_from_files( + model_paths, + reflections_paths, + sequence_paths, + distpred_paths, + run_covariance, + run_molprobity, + calculate_rama_z, + model_json_paths, + data_with_percentiles, + multiprocessing, + ) model_series_data = model_series.get_raw_data() - panel = Panel(model_series_data) + panel = Panel( + model_series_data, + continuous_metrics_to_display=continuous_metrics_to_display, + discrete_metrics_to_display=discrete_metrics_to_display, + residue_bars_to_display=residue_bars_to_display, + percentile_bar_label=percentile_bar_label, + percentile_bar_range=percentile_bar_range, + ) panel_string = panel.dwg.tostring() if wrap_in_html: diff --git a/iris_validation/graphics/chain.py b/iris_validation/graphics/chain.py index 1a02e06..53bc591 100644 --- a/iris_validation/graphics/chain.py +++ b/iris_validation/graphics/chain.py @@ -7,16 +7,26 @@ from iris_validation._defs import COLORS, CHAIN_VIEW_RINGS, CHAIN_VIEW_GAP_ANGLE -class ChainView(): - def __init__(self, data, chain_index, canvas_size=(1000, 1000), hidden=False): +class ChainView: + def __init__( + self, + data, + chain_index, + canvas_size=(1000, 1000), + hidden=False, + ChainViewRings_inp=None, + ): self.data = data + self.chain_view_rings = CHAIN_VIEW_RINGS + if ChainViewRings_inp: + self.chain_view_rings = ChainViewRings_inp self.chain_index = chain_index self.canvas_size = canvas_size self.hidden = hidden self.dwg = None self.cfa_cache = { } - self.num_rings = len(CHAIN_VIEW_RINGS) + self.num_rings = len(self.chain_view_rings) self.num_versions = self.data['num_versions'] self.num_segments = self.data['aligned_length'] self.center = (self.canvas_size[0] // 2, self.canvas_size[1] // 2) @@ -58,7 +68,7 @@ def _draw(self): # Draw data rings - for ring_id, ring_metric in enumerate(CHAIN_VIEW_RINGS): + for ring_id, ring_metric in enumerate(self.chain_view_rings): self._add_ring(ring_id, ring_metric) # Draw missing-data shade diff --git a/iris_validation/graphics/js/constants.js b/iris_validation/graphics/js/constants.js index be00a52..c285d33 100644 --- a/iris_validation/graphics/js/constants.js +++ b/iris_validation/graphics/js/constants.js @@ -6,3 +6,4 @@ const boxColors = {box_colors}; const boxLabels = {box_labels}; const gapDegrees = {gap_degrees}; const chainSelectorColors = {chain_selector_colors}; +const bar_y_lim = {bar_y_lim}; diff --git a/iris_validation/graphics/js/interaction.js b/iris_validation/graphics/js/interaction.js index 993b222..8cbeda6 100644 --- a/iris_validation/graphics/js/interaction.js +++ b/iris_validation/graphics/js/interaction.js @@ -98,8 +98,7 @@ function getResidueViewData() { bccPoints.push([x, y]); }; barOffsetY = bccPoints[2][1]; - barMultiplierY = -(bccPoints[2][1]-bccPoints[0][1]) / 100; - + barMultiplierY = -(bccPoints[2][1]-bccPoints[0][1]) / (bar_y_lim[1]-bar_y_lim[0]); // Boxplot ranges for (var versionID = 0; versionID < modelData[selectedChain]['num_versions']; ++versionID) { barLineYs.push([ ]); // Model-version holder @@ -120,14 +119,16 @@ function getResidueViewData() { let metricMax = Math.max.apply(null, allPercentileValues); let metricMean = mean(allPercentileValues); let metricStd = standardDeviation(allPercentileValues); - let metricLow = Math.max(0, metricMean-metricStd); - let metricHigh = Math.min(100, metricMean+metricStd); + let metricLow = Math.max(bar_y_lim[0], metricMean-metricStd); + let metricHigh = Math.min(bar_y_lim[1], metricMean+metricStd); let distributionValues = [ metricMin, metricMax, metricLow, metricMean, metricHigh ]; let versionLineYs = [ ]; for (var valueID = 0; valueID < 5; ++valueID) { - let lineY = parseFloat((barOffsetY + barMultiplierY * distributionValues[valueID]).toFixed(1)); + let barValueLim = Math.max(0.0, distributionValues[valueID]-bar_y_lim[0]); + let lineY = parseFloat((barOffsetY + barMultiplierY * barValueLim).toFixed(1)); versionLineYs.push(lineY); }; + console.log(versionLineYs); barLineYs[versionID].push(versionLineYs); }; }; @@ -277,7 +278,8 @@ function updateSelectedResidue() { boxplots[barID].setAttribute('opacity', 1); let barValue = modelData[selectedChain]['percentile_values'][metricID][selectedVersion][selectedResidue]; // Set main line coordinates - barY = parseFloat((barOffsetY + barMultiplierY * barValue).toFixed(1)); + let barValueLim = Math.max(0.0, barValue-bar_y_lim[0]); + barY = parseFloat((barOffsetY + barMultiplierY * barValueLim).toFixed(1)); barMainlines[barID].setAttribute('y1', barY); barMainlines[barID].setAttribute('y2', barY); // Set bar label text and position diff --git a/iris_validation/graphics/panel.py b/iris_validation/graphics/panel.py index 7f7bb3f..38aefa7 100644 --- a/iris_validation/graphics/panel.py +++ b/iris_validation/graphics/panel.py @@ -7,7 +7,15 @@ from iris_validation.graphics.chain import ChainView from iris_validation.graphics.residue import ResidueView -from iris_validation._defs import COLORS, CHAIN_VIEW_RINGS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS, CHAIN_VIEW_GAP_ANGLE +from iris_validation._defs import ( + COLORS, + CHAIN_VIEW_RINGS, + RESIDUE_VIEW_BOXES, + RESIDUE_VIEW_BARS, + CHAIN_VIEW_GAP_ANGLE, + DISCRETE_METRICS, + CONTINUOUS_METRICS, +) JS_PATH = os.path.join(os.path.dirname(__file__), 'js') @@ -15,11 +23,36 @@ JS_INTERACTION_PATH = os.path.join(JS_PATH, 'interaction.js') -class Panel(): - def __init__(self, data, canvas_size=(1500, 1000)): +class Panel: + def __init__( + self, + data, + canvas_size=(1500, 1000), + continuous_metrics_to_display=None, + discrete_metrics_to_display=None, + residue_bars_to_display=None, + percentile_bar_label=None, + percentile_bar_range=None, + ): self.data = data self.canvas_size = canvas_size - + self.chain_view_rings = CHAIN_VIEW_RINGS + if continuous_metrics_to_display: + self.chain_view_rings = self.get_chain_view_rings( + continuous_metrics_to_display, + discrete_metrics_to_display=discrete_metrics_to_display, + ) + self.residue_view_bars = RESIDUE_VIEW_BARS + if residue_bars_to_display is not None: + self.residue_view_bars = self.get_residue_view_bars(residue_bars_to_display) + if percentile_bar_label: + self.percentile_bar_label = percentile_bar_label + else: + self.percentile_bar_label = None + if percentile_bar_range: + self.percentile_bar_range = percentile_bar_range + else: + self.percentile_bar_range = [0, 100] self.dwg = None self.javascript = None self.chain_views = None @@ -36,8 +69,12 @@ def __init__(self, data, canvas_size=(1500, 1000)): # TODO: Make this nicer def _verify_chosen_metrics(self): - global CHAIN_VIEW_RINGS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS - for metric_list in (CHAIN_VIEW_RINGS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS): + global RESIDUE_VIEW_BOXES + for metric_list in ( + self.chain_view_rings, + RESIDUE_VIEW_BOXES, + self.residue_view_bars, + ): if not isinstance(metric_list, list): raise ValueError('Chosen metrics in the _defs.py file must be lists') for metric_index in reversed(range(len(metric_list))): @@ -55,7 +92,7 @@ def _verify_chosen_metrics(self): def _generate_javascript(self): json_data = json.dumps(self.data) num_chains = len(self.chain_ids) - bar_metric_ids = [ metric['id'] for metric in RESIDUE_VIEW_BARS ] + bar_metric_ids = [metric["id"] for metric in self.residue_view_bars] box_metric_ids = [ metric['id'] for metric in RESIDUE_VIEW_BOXES ] box_colors = json.dumps([ metric['seq_colors'] for metric in RESIDUE_VIEW_BOXES ]) box_labels = json.dumps([ metric['seq_labels'] for metric in RESIDUE_VIEW_BOXES ]) @@ -67,23 +104,35 @@ def _generate_javascript(self): with open(JS_INTERACTION_PATH, 'r', encoding='utf8') as infile: js_interation = infile.read() - js_constants = js_constants.format(model_data=json_data, - num_chains=num_chains, - bar_metric_ids=bar_metric_ids, - box_metric_ids=box_metric_ids, - box_colors=box_colors, - box_labels=box_labels, - gap_degrees=gap_degrees, - chain_selector_colors=self.swtich_colors) + js_constants = js_constants.format( + model_data=json_data, + num_chains=num_chains, + bar_metric_ids=bar_metric_ids, + box_metric_ids=box_metric_ids, + box_colors=box_colors, + box_labels=box_labels, + gap_degrees=gap_degrees, + chain_selector_colors=self.swtich_colors, + bar_y_lim=self.percentile_bar_range, + ) self.javascript = js_constants + js_interation def _generate_subviews(self): self.chain_views = [ ] for chain_index, chain_data in enumerate(self.data): - chain_view = ChainView(chain_data, chain_index, hidden=chain_index>0).dwg + chain_view = ChainView( + chain_data, + chain_index, + hidden=chain_index > 0, + ChainViewRings_inp=self.chain_view_rings, + ).dwg self.chain_views.append(chain_view) - self.residue_view = ResidueView().dwg + self.residue_view = ResidueView( + ResidueViewBars_inp=self.residue_view_bars, + percentile_bar_label=self.percentile_bar_label, + percentile_bar_range=self.percentile_bar_range, + ).dwg def _draw(self): middle_gap = 30 @@ -284,3 +333,38 @@ def _draw(self): self.residue_view.attribs['x'] = str(view_adj_x) self.residue_view.attribs['viewBox'] = f'{width_buffer} {height_buffer} {viewbox_width} {viewbox_height}' self.dwg.add(self.residue_view) + + def _add_metrics(self, metrics_to_display, metrics_source): + view = [] + for metric_name in metrics_to_display: + for metric_info in metrics_source: + if metric_info["short_name"] == metric_name: + view.append(metric_info) + break + return view + + def get_chain_view_rings( + self, + continuous_metrics_to_display, + discrete_metrics_to_display=None + ): + chain_view = [] + + # add discrete types first + if discrete_metrics_to_display: + chain_view.extend( + self._add_metrics(discrete_metrics_to_display, DISCRETE_METRICS) + ) + else: + chain_view.extend( + self._add_metrics([m["short_name"] for m in CHAIN_VIEW_RINGS if m["type"] == "discrete"], DISCRETE_METRICS) + ) + + chain_view.extend( + self._add_metrics(continuous_metrics_to_display, CONTINUOUS_METRICS) + ) + + return chain_view + + def get_residue_view_bars(self, residue_bars_to_display): + return self._add_metrics(residue_bars_to_display, CONTINUOUS_METRICS) diff --git a/iris_validation/graphics/residue.py b/iris_validation/graphics/residue.py index b358b5e..6d9dd21 100644 --- a/iris_validation/graphics/residue.py +++ b/iris_validation/graphics/residue.py @@ -4,19 +4,34 @@ from iris_validation._defs import COLORS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS -class ResidueView(): - def __init__(self, canvas_size=(400, 1000)): +class ResidueView: + def __init__( + self, + canvas_size=(400, 1000), + ResidueViewBars_inp=None, + percentile_bar_label=None, + percentile_bar_range=None, + ): self.canvas_size = canvas_size self.dwg = None self.svg_id = 'iris-residue-view' - + self.residue_view_bars = RESIDUE_VIEW_BARS + if ResidueViewBars_inp: + self.residue_view_bars = ResidueViewBars_inp self.box_names = [ metric['short_name'] for metric in RESIDUE_VIEW_BOXES ] - self.bar_names = [ metric['long_name'] for metric in RESIDUE_VIEW_BARS ] + self.bar_names = [ metric['long_name'] for metric in self.residue_view_bars ] # TODO: allow any number of bars self.bar_names = self.bar_names[:2] + self.percentile_bar_label = "Percentiles" + if percentile_bar_label: + self.percentile_bar_label = percentile_bar_label + if percentile_bar_range: + self.percentile_bar_range = percentile_bar_range + else: + self.percentile_bar_range = [0, 100] self._draw() def _draw(self): @@ -104,29 +119,41 @@ def _draw(self): id=f'{self.svg_id}-bar-charts-container')) # Bar chart axis + label_step = ( + self.percentile_bar_range[1] - self.percentile_bar_range[0] + ) / 10.0 for label_id in range(10+1): height = bar_charts_bounds[1] + label_id*(bar_charts_bounds[3]-bar_charts_bounds[1])/10 self.dwg.add(self.dwg.line((bar_charts_bounds[0]-5, height), (bar_charts_bounds[0]+5, height), stroke=COLORS['BLACK'], stroke_width=2, stroke_opacity=1)) - self.dwg.add(self.dwg.text(str(100-label_id*10), - insert=(bar_charts_bounds[0]-8, height+5), - font_size=18, - font_family='Arial', - fill=COLORS['BLACK'], - fill_opacity=1, - text_anchor='end', - alignment_baseline='central')) + + self.dwg.add( + self.dwg.text( + str(round(self.percentile_bar_range[1] - label_id * label_step, 1)), + insert=(bar_charts_bounds[0] - 8, height + 5), + font_size=18, + font_family="Arial", + fill=COLORS["BLACK"], + fill_opacity=1, + text_anchor="end", + alignment_baseline="central", + ) + ) # Bar chart bottom label - self.dwg.add(self.dwg.text('Percentiles', - insert=(self.canvas_size[0]/2, bar_charts_bounds[3]+50), - font_size=18, - font_family='Arial', - fill=COLORS['BLACK'], - fill_opacity=1, - text_anchor='middle', - alignment_baseline='central')) + self.dwg.add( + self.dwg.text( + self.percentile_bar_label, + insert=(self.canvas_size[0] / 2, bar_charts_bounds[3] + 50), + font_size=18, + font_family="Arial", + fill=COLORS["BLACK"], + fill_opacity=1, + text_anchor="middle", + alignment_baseline="central", + ) + ) bar_chart_width = bar_charts_bounds[2] - bar_charts_bounds[0] for bar_id, bar_name in enumerate(self.bar_names): diff --git a/iris_validation/metrics/__init__.py b/iris_validation/metrics/__init__.py index b3033c6..6be127a 100644 --- a/iris_validation/metrics/__init__.py +++ b/iris_validation/metrics/__init__.py @@ -4,9 +4,9 @@ import json import clipper -from iris_validation.utils import ONE_LETTER_CODES -from iris_validation.metrics.residue import MetricsResidue -from iris_validation.metrics.chain import MetricsChain +# from iris_validation.utils import ONE_LETTER_CODES +# from iris_validation.metrics.residue import MetricsResidue +# from iris_validation.metrics.chain import MetricsChain from iris_validation.metrics.model import MetricsModel from iris_validation.metrics.series import MetricsModelSeries from iris_validation.metrics.reflections import ReflectionsHandler @@ -197,24 +197,29 @@ def _get_tortoize_data(model_path, seq_nums, model_id=None, out_queue=None): return rama_z_data -def metrics_model_series_from_files(model_paths, - reflections_paths=None, - sequence_paths=None, - distpred_paths=None, - run_covariance=False, - run_molprobity=False, - calculate_rama_z=True, - multiprocessing=True): + +def metrics_model_series_from_files( + model_paths, + reflections_paths=None, + sequence_paths=None, + distpred_paths=None, + run_covariance=False, + run_molprobity=False, + calculate_rama_z=False, + model_json_paths=None, + data_with_percentiles=None, + multiprocessing=True, +): try: if isinstance(model_paths, str): model_paths = [ model_paths ] model_paths = tuple(model_paths) - if None in model_paths: + if model_paths[-1] is None: raise TypeError except TypeError as exception: raise ValueError('Argument \'model_paths\' should be an iterable of filenames') from exception - path_lists = [ model_paths, reflections_paths, sequence_paths, distpred_paths ] + path_lists = [ model_paths, reflections_paths, sequence_paths, distpred_paths, model_json_paths ] for i in range(1, len(path_lists)): if path_lists[i] is None: path_lists[i] = tuple([ None for _ in model_paths ]) @@ -227,16 +232,44 @@ def metrics_model_series_from_files(model_paths, all_molprobity_data = [ ] all_reflections_data = [ ] all_rama_z_data = [ ] + all_bfactor_data = [] # if externally supplied num_queued = 0 results_queue = Queue() + check_resnum = False for model_id, file_paths in enumerate(zip(*path_lists)): - model_path, reflections_path, sequence_path, distpred_path = file_paths + ( + model_path, + reflections_path, + sequence_path, + distpred_path, + json_data_path, + ) = file_paths + if model_path is None: + continue minimol = _get_minimol_from_path(model_path) seq_nums = _get_minimol_seq_nums(minimol) covariance_data = None molprobity_data = None reflections_data = None rama_z_data = None + bfactor_data = None + + if json_data_path: + check_resnum = True + with open(json_data_path, "r") as j: + json_data = json.load(j) + for metric in json_data: + if metric == "molprobity": + molprobity_data = json_data["molprobity"] + run_molprobity = False + if metric == "rama_z": + rama_z_data = json_data["rama_z"] + calculate_rama_z = False + if metric == "map_fit": + reflections_data = json_data["map_fit"] + reflections_path = None + if metric == "b_factor": + bfactor_data = json_data["b_factor"] if run_covariance: if multiprocessing: p = Process(target=_get_covariance_data, @@ -283,6 +316,7 @@ def metrics_model_series_from_files(model_paths, all_molprobity_data.append(molprobity_data) all_reflections_data.append(reflections_data) all_rama_z_data.append(rama_z_data) + all_bfactor_data.append(bfactor_data) if multiprocessing: for _ in range(num_queued): @@ -295,10 +329,18 @@ def metrics_model_series_from_files(model_paths, all_reflections_data[model_id] = result if result_type == 'rama_z': all_rama_z_data[model_id] = result - metrics_models = [ ] - for model_id, model_data in enumerate(zip(all_minimol_data, all_covariance_data, all_molprobity_data, all_reflections_data, all_rama_z_data)): - metrics_model = MetricsModel(*model_data) + for model_id, model_data in enumerate( + zip( + all_minimol_data, + all_covariance_data, + all_molprobity_data, + all_reflections_data, + all_rama_z_data, + all_bfactor_data, + ) + ): + metrics_model = MetricsModel(*model_data, check_resnum, data_with_percentiles) metrics_models.append(metrics_model) metrics_model_series = MetricsModelSeries(metrics_models) diff --git a/iris_validation/metrics/chain.py b/iris_validation/metrics/chain.py index 9ee0ab6..b07b62e 100644 --- a/iris_validation/metrics/chain.py +++ b/iris_validation/metrics/chain.py @@ -1,8 +1,19 @@ from iris_validation.metrics.residue import MetricsResidue -class MetricsChain(): - def __init__(self, mmol_chain, parent_model=None, covariance_data=None, molprobity_data=None, density_scores=None, rama_z_data=None): +class MetricsChain: + def __init__( + self, + mmol_chain, + parent_model=None, + covariance_data=None, + molprobity_data=None, + density_scores=None, + rama_z_data=None, + bfactor_data=None, + check_resnum=False, + data_with_percentiles=None, + ): self.minimol_chain = mmol_chain self.parent_model = parent_model self.covariance_data = covariance_data @@ -14,15 +25,41 @@ def __init__(self, mmol_chain, parent_model=None, covariance_data=None, molprobi self.residues = [ ] self.length = len(mmol_chain) self.chain_id = str(mmol_chain.id().trim()) - + dict_ext_percentiles = {} # stores the percentiles supplied externally for residue_index, mmol_residue in enumerate(mmol_chain): previous_residue = mmol_chain[residue_index-1] if residue_index > 0 else None next_residue = mmol_chain[residue_index+1] if residue_index < len(mmol_chain)-1 else None seq_num = int(mmol_residue.seqnum()) - residue_covariance_data = None if covariance_data is None else covariance_data[seq_num] - residue_molprobity_data = None if molprobity_data is None else molprobity_data[seq_num] - residue_density_scores = None if density_scores is None else density_scores[seq_num] - residue_rama_z_score = None if rama_z_data is None else rama_z_data.get(seq_num, None) + res_id = str(mmol_residue.id()).strip() + # covariance + residue_covariance_data = get_data_from_dict(covariance_data, + id=res_id,seq_num=seq_num,check_resnum=check_resnum) + # molprobity + residue_molprobity_data = get_data_from_dict(molprobity_data, + id=res_id,seq_num=seq_num,check_resnum=check_resnum) + # density scores + residue_density_scores = get_data_from_dict(density_scores, + id=res_id,seq_num=seq_num,check_resnum=check_resnum, + with_percentiles=data_with_percentiles,percentile_key="map_fit", + dict_ext_percentiles=dict_ext_percentiles) + # rama_z + if rama_z_data is None: + residue_rama_z_score = None + elif check_resnum: + try: + residue_rama_z_score = rama_z_data[res_id] + except KeyError: + residue_rama_z_score = None + else: + try: + residue_rama_z_score = rama_z_data[seq_num] + except KeyError: + residue_rama_z_score = None + # ext b-factor + residue_bfact_score = get_data_from_dict(bfactor_data, + id=res_id,seq_num=seq_num,check_resnum=check_resnum, + with_percentiles=data_with_percentiles,percentile_key="b_factor", + dict_ext_percentiles=dict_ext_percentiles) residue = MetricsResidue( mmol_residue, residue_index, @@ -32,7 +69,10 @@ def __init__(self, mmol_chain, parent_model=None, covariance_data=None, molprobi residue_covariance_data, residue_molprobity_data, residue_density_scores, - residue_rama_z_score) + residue_rama_z_score, + residue_bfact_score, + dict_ext_percentiles, + ) self.residues.append(residue) for residue_index, residue in enumerate(self.residues): @@ -86,3 +126,18 @@ def b_factor_lists(self): else: ion_bfs.append(residue.avg_b_factor) return all_bfs, aa_bfs, mc_bfs, sc_bfs, non_aa_bfs, water_bfs, ligand_bfs, ion_bfs + +def get_data_from_dict(data_dict, id, seq_num, check_resnum, with_percentiles=None, percentile_key=None, dict_ext_percentiles=None): + if data_dict is None: + return None + if not check_resnum: + return data_dict.get(seq_num, None) + + try: + if with_percentiles and percentile_key in with_percentiles: + dict_ext_percentiles[percentile_key] = data_dict[id][-1] + return data_dict[id][0] + else: + return data_dict[id] + except KeyError: + return None \ No newline at end of file diff --git a/iris_validation/metrics/model.py b/iris_validation/metrics/model.py index 530c196..892d9fd 100644 --- a/iris_validation/metrics/model.py +++ b/iris_validation/metrics/model.py @@ -3,8 +3,18 @@ from iris_validation.metrics.percentiles import PercentileCalculator -class MetricsModel(): - def __init__(self, mmol_model, covariance_data=None, molprobity_data=None, reflections_data=None, rama_z_data=None): +class MetricsModel: + def __init__( + self, + mmol_model, + covariance_data=None, + molprobity_data=None, + reflections_data=None, + rama_z_data=None, + bfactor_data=None, + check_resnum=False, + data_with_percentiles=None, + ): self.minimol_model = mmol_model self.covariance_data = covariance_data self.molprobity_data = molprobity_data @@ -28,7 +38,20 @@ def __init__(self, mmol_model, covariance_data=None, molprobity_data=None, refle chain_molprobity_data = None if molprobity_data is None else molprobity_data[chain_id] chain_density_scores = None if self.density_scores is None else self.density_scores[chain_id] chain_rama_z_data = None if rama_z_data is None else rama_z_data[chain_id] - chain = MetricsChain(mmol_chain, self, chain_covariance_data, chain_molprobity_data, chain_density_scores, chain_rama_z_data) + chain_bfactor_data = ( + None if bfactor_data is None else bfactor_data[chain_id] + ) + chain = MetricsChain( + mmol_chain, + self, + chain_covariance_data, + chain_molprobity_data, + chain_density_scores, + chain_rama_z_data, + chain_bfactor_data, + check_resnum=check_resnum, + data_with_percentiles=data_with_percentiles, + ) chain.remove_non_aa_residues() self.chains.append(chain) diff --git a/iris_validation/metrics/reflections.py b/iris_validation/metrics/reflections.py index 1d30cff..9a38cf1 100644 --- a/iris_validation/metrics/reflections.py +++ b/iris_validation/metrics/reflections.py @@ -6,7 +6,7 @@ from iris_validation import utils -class ReflectionsHandler(): +class ReflectionsHandler: def __init__(self, f_reflections=None, xmap=None, minimol=None): self.f_reflections = f_reflections self.xmap = xmap diff --git a/iris_validation/metrics/residue.py b/iris_validation/metrics/residue.py index 86b628d..595cff3 100644 --- a/iris_validation/metrics/residue.py +++ b/iris_validation/metrics/residue.py @@ -6,8 +6,21 @@ from iris_validation._defs import RAMACHANDRAN_THRESHOLDS -class MetricsResidue(): - def __init__(self, mmol_residue, index_in_chain=None, previous_residue=None, next_residue=None, parent_chain=None, covariance_data=None, molprobity_data=None, density_scores=None, rama_z_score=None): +class MetricsResidue: + def __init__( + self, + mmol_residue, + index_in_chain=None, + previous_residue=None, + next_residue=None, + parent_chain=None, + covariance_data=None, + molprobity_data=None, + density_scores=None, + rama_z_score=None, + bfact_score=None, + dict_ext_percentiles=None, + ): self.minimol_residue = mmol_residue self.initialised_with_context = index_in_chain is not None self.index_in_chain = index_in_chain @@ -32,6 +45,9 @@ def __init__(self, mmol_residue, index_in_chain=None, previous_residue=None, nex # B-factors self.max_b_factor, self.avg_b_factor, self.std_b_factor, self.mc_b_factor, self.sc_b_factor = utils.analyse_b_factors(mmol_residue, self.is_aa, self.backbone_atoms) + # override precalculated + if bfact_score: + self.avg_b_factor, self.std_b_factor = bfact_score # Backbone torsion angles self.phi = clipper.MMonomer.protein_ramachandran_phi(self.previous_residue, mmol_residue) if self.previous_residue else None @@ -100,11 +116,40 @@ def __init__(self, mmol_residue, index_in_chain=None, previous_residue=None, nex # Percentiles percentile_calculator = self.parent_chain.parent_model.percentile_calculator - self.avg_b_factor_percentile = percentile_calculator.get_percentile(0, self.avg_b_factor) - self.max_b_factor_percentile = percentile_calculator.get_percentile(1, self.max_b_factor) - self.std_b_factor_percentile = percentile_calculator.get_percentile(2, self.std_b_factor) - self.fit_score_percentile = percentile_calculator.get_percentile(3, self.fit_score) - self.mainchain_fit_score_percentile = percentile_calculator.get_percentile(4, self.mainchain_fit_score) - self.sidechain_fit_score_percentile = percentile_calculator.get_percentile(5, self.sidechain_fit_score) - self.covariance_score_percentile = percentile_calculator.get_percentile(6, self.covariance_score) + if "b-factor" in dict_ext_percentiles: + self.avg_b_factor_percentile = dict_ext_percentiles["b-factor"][0] + else: + self.avg_b_factor_percentile = percentile_calculator.get_percentile( + 0, self.avg_b_factor + ) + self.max_b_factor_percentile = percentile_calculator.get_percentile( + 1, self.max_b_factor + ) + if "b-factor" in dict_ext_percentiles: + self.std_b_factor_percentile = dict_ext_percentiles["b-factor"][1] + else: + self.std_b_factor_percentile = percentile_calculator.get_percentile( + 2, self.std_b_factor + ) + if "map_fit" in dict_ext_percentiles: + self.fit_score_percentile = dict_ext_percentiles["map_fit"][0] + else: + self.fit_score_percentile = percentile_calculator.get_percentile( + 3, self.fit_score + ) + if "map_fit" in dict_ext_percentiles: + self.mainchain_fit_score_percentile = dict_ext_percentiles["map_fit"][1] + else: + self.mainchain_fit_score_percentile = percentile_calculator.get_percentile( + 4, self.mainchain_fit_score + ) + if "map_fit" in dict_ext_percentiles: + self.sidechain_fit_score_percentile = dict_ext_percentiles["map_fit"][2] + else: + self.sidechain_fit_score_percentile = percentile_calculator.get_percentile( + 5, self.sidechain_fit_score + ) + self.covariance_score_percentile = percentile_calculator.get_percentile( + 6, self.covariance_score + ) # self.rama_z_score_percentile = percentile_calculator.get_percentile(7, self.rama_z) diff --git a/iris_validation/metrics/series.py b/iris_validation/metrics/series.py index e2fddf2..5483401 100644 --- a/iris_validation/metrics/series.py +++ b/iris_validation/metrics/series.py @@ -1,11 +1,11 @@ -import clipper from iris_validation import utils -from iris_validation.metrics.model import MetricsModel -from iris_validation.metrics.reflections import ReflectionsHandler +# from iris_validation.metrics.model import MetricsModel +# from iris_validation.metrics.reflections import ReflectionsHandler -class MetricsModelSeries(): + +class MetricsModelSeries: def __init__(self, metrics_models): self.metrics_models = metrics_models self.chain_sets = None