From 2de7fdb9847b82d99df75af6d41eb4f2557314fd Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Thu, 14 Nov 2019 12:13:27 -0800 Subject: [PATCH] Squashed 'modules/core/dependency/python-ihm/' changes from 30cfb62e7f..dacfddea3a dacfddea3a Fix syntax error 6a6b3f1a81 Add DHSO and BMSO cross-linkers 941b506723 Test FRETAnalysis constructor type check 9e42a1f303 Allow rejecting old file versions 8145ad5c35 Note compliance with dictionary version 1.04 a8e248d1e8 Merge pull request ihmwg/python-ihm#47 from CAHanke/master 08b1d75231 Adapt to modifications in FLR dictionary 725e06b28a Test with the new Python 3.8 25ba45f17b Update for 0.12 release 9e09ba9335 Note that Location is abstract git-subtree-dir: modules/core/dependency/python-ihm git-subtree-split: dacfddea3a12d71ba097c7066d075dd73f0ac09a --- .../core/dependency/python-ihm/.travis.yml | 1 + .../core/dependency/python-ihm/ChangeLog.rst | 8 + .../core/dependency/python-ihm/MANIFEST.in | 2 +- .../python-ihm/docs/cross_linkers.rst | 12 + .../core/dependency/python-ihm/docs/flr.rst | 17 +- .../dependency/python-ihm/docs/reader.rst | 2 + .../dependency/python-ihm/ihm/__init__.py | 2 +- .../python-ihm/ihm/cross_linkers.py | 15 + .../core/dependency/python-ihm/ihm/dumper.py | 177 ++++++++-- modules/core/dependency/python-ihm/ihm/flr.py | 219 +++++++++++-- .../dependency/python-ihm/ihm/location.py | 1 + .../core/dependency/python-ihm/ihm/reader.py | 207 ++++++++++-- modules/core/dependency/python-ihm/setup.py | 2 +- .../dependency/python-ihm/test/test_dumper.py | 205 ++++++++++-- .../dependency/python-ihm/test/test_flr.py | 256 ++++++++++++--- .../dependency/python-ihm/test/test_reader.py | 310 ++++++++++++++++-- 16 files changed, 1267 insertions(+), 169 deletions(-) diff --git a/modules/core/dependency/python-ihm/.travis.yml b/modules/core/dependency/python-ihm/.travis.yml index 013233386a..e47c213df1 100644 --- a/modules/core/dependency/python-ihm/.travis.yml +++ b/modules/core/dependency/python-ihm/.travis.yml @@ -5,6 +5,7 @@ python: - 2.7 - 3.6 - 3.7 + - 3.8 matrix: include: - dist: trusty diff --git a/modules/core/dependency/python-ihm/ChangeLog.rst b/modules/core/dependency/python-ihm/ChangeLog.rst index 55f84417fc..2080c0ce78 100644 --- a/modules/core/dependency/python-ihm/ChangeLog.rst +++ b/modules/core/dependency/python-ihm/ChangeLog.rst @@ -1,5 +1,13 @@ HEAD ==== + - :func:`ihm.reader.read` has a new optional ``reject_old_file`` argument. + If set, it will raise an exception if asked to read a file that conforms + to too old a version of the IHM extension dictionary. + - Definitions for the DHSO and BMSO cross-linkers are now provided in the + :mod:`ihm.cross_linkers` module. + +0.12 - 2019-10-16 +================= - :class:`ihm.restraint.ResidueFeature` objects can now act on one or more :class:`Residue` objects, which act equivalently to 1-residue ranges (:class:`AsymUnitRange` or :class:`EntityRange`). diff --git a/modules/core/dependency/python-ihm/MANIFEST.in b/modules/core/dependency/python-ihm/MANIFEST.in index aa90463726..900988ce88 100644 --- a/modules/core/dependency/python-ihm/MANIFEST.in +++ b/modules/core/dependency/python-ihm/MANIFEST.in @@ -1,3 +1,3 @@ include src/ihm_format.h include src/ihm_format.i -include src/ihm_format_wrap_0.11.c +include src/ihm_format_wrap_0.12.c diff --git a/modules/core/dependency/python-ihm/docs/cross_linkers.rst b/modules/core/dependency/python-ihm/docs/cross_linkers.rst index 15481c6901..78df5dcf6e 100644 --- a/modules/core/dependency/python-ihm/docs/cross_linkers.rst +++ b/modules/core/dependency/python-ihm/docs/cross_linkers.rst @@ -31,3 +31,15 @@ The :mod:`ihm.cross_linkers` Python module .. data:: edc EDC cross-linker that links a carboxyl group with a primary amine. + +.. data:: dhso + + DHSO (dihydrazide sulfoxide) MS-cleavable cross-linker that links + carboxyl groups, described in + `Gutierrez et al, 2016 `_. + +.. data:: bmso + + BMSO (bismaleimide sulfoxide) MS-cleavable cross-linker that links + cysteines, described in + `Gutierrez et al, 2018 `_. diff --git a/modules/core/dependency/python-ihm/docs/flr.rst b/modules/core/dependency/python-ihm/docs/flr.rst index 1a2100acb5..3ced68a6c6 100644 --- a/modules/core/dependency/python-ihm/docs/flr.rst +++ b/modules/core/dependency/python-ihm/docs/flr.rst @@ -40,12 +40,27 @@ The :mod:`ihm.flr` Python module .. autoclass:: Instrument :members: -.. autoclass:: ExpSetting +.. autoclass:: InstSetting :members: +.. autoclass:: ExpCondition + :members: + .. autoclass:: FRETAnalysis :members: +.. autoclass:: LifetimeFitModel + :members: + +.. autoclass:: RefMeasurementGroup + :members: + +.. autoclass:: RefMeasurement + :members: + +.. autoclass:: RefMeasurementLifetime + :members: + .. autoclass:: FRETDistanceRestraintGroup :members: diff --git a/modules/core/dependency/python-ihm/docs/reader.rst b/modules/core/dependency/python-ihm/docs/reader.rst index 88ba35d7f4..d5c69b4119 100644 --- a/modules/core/dependency/python-ihm/docs/reader.rst +++ b/modules/core/dependency/python-ihm/docs/reader.rst @@ -13,6 +13,8 @@ The :mod:`ihm.reader` Python module .. autoexception:: UnknownKeywordWarning +.. autoexception:: OldFileError + .. autoclass:: Handler :members: diff --git a/modules/core/dependency/python-ihm/ihm/__init__.py b/modules/core/dependency/python-ihm/ihm/__init__.py index ac2f3b7455..0415b23105 100644 --- a/modules/core/dependency/python-ihm/ihm/__init__.py +++ b/modules/core/dependency/python-ihm/ihm/__init__.py @@ -20,7 +20,7 @@ import urllib2 import json -__version__ = '0.11' +__version__ = '0.12' class __UnknownValue(object): # Represent the mmCIF 'unknown' special value diff --git a/modules/core/dependency/python-ihm/ihm/cross_linkers.py b/modules/core/dependency/python-ihm/ihm/cross_linkers.py index afff7ffdf1..eb4492d78b 100644 --- a/modules/core/dependency/python-ihm/ihm/cross_linkers.py +++ b/modules/core/dependency/python-ihm/ihm/cross_linkers.py @@ -38,3 +38,18 @@ smiles='CCN=C=NCCCN(C)C', inchi='1S/C8H17N3/c1-4-9-8-10-6-5-7-11(2)3/h4-7H2,1-3H3', inchi_key='LMDZBCPBFSXMTL-UHFFFAOYSA-N') + +dhso = ihm.ChemDescriptor('DHSO', + chemical_name='dihydrazide sulfoxide', + smiles='NNC(=O)CC[S](=O)CCC(=O)NN', + inchi='1S/C6H14N4O3S/c7-9-5(11)1-3-14(13)4-2-6(12)10-8' + '/h1-4,7-8H2,(H,9,11)(H,10,12)', + inchi_key='XTCXQISMAWBOOT-UHFFFAOYSA-N') + +bmso = ihm.ChemDescriptor('BMSO', + chemical_name='bismaleimide sulfoxide', + smiles='O=C(CC[S](=O)CCC(=O)NCCN1C(=O)C=CC1=O)NCCN2C(=O)C=CC2=O', + inchi='1S/C18H22N4O7S/c23-13(19-7-9-21-15(25)1-2-16(21)26)5-' + '11-30(29)12-6-14(24)20-8-10-22-17(27)3-4-18(22)28/h1-' + '4H,5-12H2,(H,19,23)(H,20,24)', + inchi_key='PUNDHDZIOGBGHG-UHFFFAOYSA-N') diff --git a/modules/core/dependency/python-ihm/ihm/dumper.py b/modules/core/dependency/python-ihm/ihm/dumper.py index ae7f472925..e007aa208b 100644 --- a/modules/core/dependency/python-ihm/ihm/dumper.py +++ b/modules/core/dependency/python-ihm/ihm/dumper.py @@ -54,8 +54,8 @@ class _AuditConformDumper(Dumper): def dump(self, system, writer): with writer.category("_audit_conform") as l: # Update to match the version of the IHM dictionary we support: - l.write(dict_name="ihm-extension.dic", dict_version="0.137", - dict_location=self.URL % "7ea672a") + l.write(dict_name="ihm-extension.dic", dict_version="1.04", + dict_location=self.URL % "3a8e0b9") class _StructDumper(Dumper): @@ -2083,29 +2083,42 @@ def all_experiments(): def dump(self, system, writer): with writer.loop('_flr_experiment', - ['ordinal_id', 'id', 'instrument_id', 'exp_setting_id', - 'sample_id', 'details']) as l: + ['ordinal_id', 'id', 'instrument_id', 'inst_setting_id', + 'exp_condition_id','sample_id', 'details']) as l: ordinal = 1 for x in self._experiments_by_id: for i in range(len(x.sample_list)): l.write(ordinal_id=ordinal, id=x._id, instrument_id=x.instrument_list[i]._id, - exp_setting_id=x.exp_setting_list[i]._id, + inst_setting_id=x.inst_setting_list[i]._id, + exp_condition_id=x.exp_condition_list[i]._id, sample_id=x.sample_list[i]._id, details=x.details_list[i]) ordinal +=1 -class _FLRExpSettingDumper(Dumper): +class _FLRInstSettingDumper(Dumper): def finalize(self, system): - def all_exp_settings(): - return itertools.chain.from_iterable(f._all_exp_settings() + def all_inst_settings(): + return itertools.chain.from_iterable(f._all_inst_settings() for f in system.flr_data) - self._exp_settings_by_id = _assign_all_ids(all_exp_settings) + self._inst_settings_by_id = _assign_all_ids(all_inst_settings) def dump(self, system, writer): - with writer.loop('_flr_exp_setting', ['id', 'details']) as l: - for x in self._exp_settings_by_id: + with writer.loop('_flr_inst_setting', ['id', 'details']) as l: + for x in self._inst_settings_by_id: + l.write(id=x._id, details=x.details) + +class _FLR_ExpConditionDumper(Dumper): + def finalize(self, system): + def all_exp_conditions(): + return itertools.chain.from_iterable(f._all_exp_conditions() + for f in system.flr_data) + self._exp_conditions_by_id = _assign_all_ids(all_exp_conditions) + + def dump(self, system, writer): + with writer.loop('_flr_exp_condition', ['id', 'details']) as l: + for x in self._exp_conditions_by_id: l.write(id=x._id, details=x.details) @@ -2355,6 +2368,90 @@ def dump(self, system, writer): gamma=x.gamma, delta=x.delta, a_b=x.a_b) +class _FLRLifetimeFitModelDumper(Dumper): + def finalize(self, system): + def all_lifetime_fit_models(): + return itertools.chain.from_iterable(f._all_lifetime_fit_models() + for f in system.flr_data) + self._lifetime_fit_models_by_id = _assign_all_ids(all_lifetime_fit_models) + + def dump(self, system, writer): + with writer.loop('_flr_lifetime_fit_model', + ['id', 'name', 'description', + 'external_file_id', 'citation_id']) as l: + for x in self._lifetime_fit_models_by_id: + l.write(id = x._id, name = x.name, + description = x.description, + external_file_id = None if x.external_file is None + else x.external_file._id, + citation_id = None if x.citation is None + else x.citation._id) + + +class _FLRRefMeasurementDumper(Dumper): + def finalize(self, system): + def all_ref_measurement_groups(): + return itertools.chain.from_iterable(f._all_ref_measurement_groups() + for f in system.flr_data) + self._ref_measurement_groups_by_id = _assign_all_ids(all_ref_measurement_groups) + + def _all_ref_measurements(): + return itertools.chain.from_iterable(f._all_ref_measurements() + for f in system.flr_data) + self._ref_measurements_by_id = _assign_all_ids(_all_ref_measurements) + + def _all_ref_measurement_lifetimes(): + return itertools.chain.from_iterable(f._all_ref_measurement_lifetimes() + for f in system.flr_data) + self._ref_measurement_lifetimes_by_id = _assign_all_ids(_all_ref_measurement_lifetimes) + + def dump(self, system, writer): + self.dump_ref_measurement_group(system, writer) + self.dump_ref_measurement_group_link(system, writer) + self.dump_ref_measurement(system, writer) + self.dump_ref_measurement_lifetimes(system, writer) + + def dump_ref_measurement_group(self, system, writer): + with writer.loop('_flr_reference_measurement_group', + ['id', 'num_measurements','details']) as l: + for x in self._ref_measurement_groups_by_id: + l.write(id = x._id, + num_measurements = len(x.ref_measurement_list), + details = x.details) + + def dump_ref_measurement_group_link(self, system, writer): + with writer.loop('_flr_reference_measurement_group_link', + ['group_id', 'reference_measurement_id']) as l: + for x in self._ref_measurement_groups_by_id: + for m in x.ref_measurement_list: + l.write(group_id=x._id, + reference_measurement_id = m._id) + + def dump_ref_measurement(self, system, writer): + with writer.loop('_flr_reference_measurement', + ['id', 'reference_sample_probe_id', + 'num_species', 'details']) as l: + for x in self._ref_measurements_by_id: + l.write(id = x._id, + reference_sample_probe_id = x.ref_sample_probe._id, + num_species = len(x.list_of_lifetimes), + details = x.details) + + def dump_ref_measurement_lifetimes(self, system, writer): + with writer.loop('_flr_reference_measurement_lifetime', + ['ordinal_id', 'reference_measurement_id', + 'species_name', 'species_fraction', 'lifetime']) as l: + ordinal = 1 + for x in self._ref_measurements_by_id: + for m in x.list_of_lifetimes: + l.write(ordinal_id = ordinal, + reference_measurement_id = x._id, + species_name = m.species_name, + species_fraction = m.species_fraction, + lifetime = m.lifetime) + ordinal += 1 + + class _FLRAnalysisDumper(Dumper): def finalize(self, system): def all_analyses(): @@ -2363,27 +2460,67 @@ def all_analyses(): self._analyses_by_id = _assign_all_ids(all_analyses) def dump(self, system, writer): + self.dump_fret_analysis_general(system, writer) + self.dump_fret_analysis_intensity(system, writer) + self.dump_fret_analysis_lifetime(system, writer) + + def dump_fret_analysis_general(self, system, writer): with writer.loop('_flr_fret_analysis', - ['id', 'experiment_id', 'sample_probe_id_1', - 'sample_probe_id_2', 'forster_radius_id', - 'calibration_parameters_id', 'method_name', - 'chi_square_reduced', 'dataset_list_id', + ['id', 'experiment_id', 'type', + 'sample_probe_id_1', 'sample_probe_id_2', + 'forster_radius_id', 'dataset_list_id', 'external_file_id', 'software_id']) as l: for x in self._analyses_by_id: l.write(id=x._id, experiment_id=x.experiment._id, + type = x.type, sample_probe_id_1=x.sample_probe_1._id, sample_probe_id_2=x.sample_probe_2._id, forster_radius_id=x.forster_radius._id, - calibration_parameters_id=x.calibration_parameters._id, - method_name=x.method_name, - chi_square_reduced=x.chi_square_reduced, dataset_list_id=x.dataset._id, external_file_id=None if x.external_file is None else x.external_file._id, software_id=None if x.software is None else x.software._id) + def dump_fret_analysis_intensity(self, system, writer): + with writer.loop('_flr_fret_analysis_intensity', + ['ordinal_id', 'analysis_id', + 'calibration_parameters_id', 'donor_only_fraction', + 'chi_square_reduced', 'method_name', 'details']) as l: + ordinal = 1 + for x in self._analyses_by_id: + ## if it is an intensity-based analysis. + if 'intensity' in x.type: + l.write(ordinal_id = ordinal, + analysis_id = x._id, + calibration_parameters_id = None if x.calibration_parameters is None else x.calibration_parameters._id, + donor_only_fraction = x.donor_only_fraction, + chi_square_reduced = x.chi_square_reduced, + method_name = x.method_name, + details = x.details) + ordinal += 1 + + def dump_fret_analysis_lifetime(self, system, writer): + with writer.loop('_flr_fret_analysis_lifetime', + ['ordinal_id', 'analysis_id', + 'reference_measurement_group_id', 'lifetime_fit_model_id', + 'donor_only_fraction', 'chi_square_reduced', + 'method_name', 'details']) as l: + ordinal = 1 + for x in self._analyses_by_id: + ## if it is a lifetime-based analysis + if 'lifetime' in x.type: + l.write(ordinal_id = ordinal, + analysis_id = x._id, + reference_measurement_group_id = x.ref_measurement_group._id, + lifetime_fit_model_id = x.lifetime_fit_model._id, + donor_only_fraction = x.donor_only_fraction, + chi_square_reduced = x.chi_square_reduced, + method_name = x.method_name, + details = x.details) + ordinal += 1 + class _FLRPeakAssignmentDumper(Dumper): def finalize(self, system): @@ -2700,12 +2837,14 @@ def write(fh, systems, format='mmCIF', dumpers=[]): _EnsembleDumper(), _DensityDumper(), _MultiStateDumper(), _OrderedDumper(), - _FLRExperimentDumper(), _FLRExpSettingDumper(), + _FLRExperimentDumper(), _FLRInstSettingDumper(), + _FLR_ExpConditionDumper(), _FLRInstrumentDumper(), _FLREntityAssemblyDumper(), _FLRSampleConditionDumper(), _FLRSampleDumper(), _FLRProbeDumper(), _FLRSampleProbeDetailsDumper(), _FLRPolyProbePositionDumper(), _FLRConjugateDumper(), _FLRForsterRadiusDumper(), _FLRCalibrationParametersDumper(), + _FLRLifetimeFitModelDumper(), _FLRRefMeasurementDumper(), _FLRAnalysisDumper(), _FLRPeakAssignmentDumper(), _FLRDistanceRestraintDumper(), _FLRModelQualityDumper(), _FLRModelDistanceDumper(), _FLRFPSModelingDumper(), diff --git a/modules/core/dependency/python-ihm/ihm/flr.py b/modules/core/dependency/python-ihm/ihm/flr.py index 1706042a71..f757143720 100644 --- a/modules/core/dependency/python-ihm/ihm/flr.py +++ b/modules/core/dependency/python-ihm/ihm/flr.py @@ -244,59 +244,66 @@ class Experiment(object): :param instrument: The instrument. :type instrument: :class:`Instrument` - :param exp_setting: The experimental setting. - :type exp_setting: :class:`ExpSetting` + :param inst_setting: The instrument setting. + :type inst_setting: :class:`InstSetting` + :param exp_condition: The experimental conditions. + :type exp_condition: :class:`ExpCondition` :param sample: The sample. :type sample: :class:`Sample` :param details: Details on the experiment. """ - def __init__(self, instrument=None, exp_setting=None, sample=None, - details=None): + def __init__(self, instrument=None, inst_setting=None, exp_condition=None, + sample=None, details=None): """The Experiment object can either be initiated with empty lists, or with an entry for each of them. In this way, an experiment object is created and filled with one entry. """ self.instrument_list = [] - self.exp_setting_list = [] + self.inst_setting_list = [] + self.exp_condition_list = [] self.sample_list = [] self.details_list = [] - if instrument != None and exp_setting != None and sample != None: - self.add_entry(instrument=instrument, exp_setting=exp_setting, + if instrument != None and inst_setting != None and exp_condition != None and sample != None: + self.add_entry(instrument=instrument, inst_setting=inst_setting, exp_condition=exp_condition, sample=sample, details=details) - def add_entry(self, instrument, exp_setting, sample,details=None): + def add_entry(self, instrument, inst_setting, exp_condition, sample,details=None): """Entries to the experiment object can also be added one by one. """ self.instrument_list.append(instrument) - self.exp_setting_list.append(exp_setting) + self.inst_setting_list.append(inst_setting) + self.exp_condition_list.append(exp_condition) self.sample_list.append(sample) self.details_list.append(details) def get_entry_by_index(self, index): - """Returns the combination of :class:`Instrument`, :class:`ExpSetting`, - :class:`Sample`, and details for a given index. + """Returns the combination of :class:`Instrument`, :class:`InstSetting`, + :class:`ExpCondition`, :class:`Sample`, and details for a given index. """ return (self.instrument_list[index], - self.exp_setting_list[index], self.sample_list[index], + self.inst_setting_list[index], + self.exp_condition_list[index], + self.sample_list[index], self.details_list[index]) def __eq__(self, other): -# return self.__dict__ == other.__dict__ return ((self.instrument_list == other.instrument_list) - and (self.exp_setting_list == other.exp_setting_list) + and (self.inst_setting_list == other.inst_setting_list) + and (self.exp_condition_list == other.exp_condition_list) and (self.sample_list == other.sample_list) and (self.details_list == other.details_list)) - def contains(self, instrument, exp_setting, sample): + def contains(self, instrument, inst_setting, exp_condition, sample): """Checks whether a combination of :class:`Instrument`, - :class:`ExpSetting`, :class:`Sample` is already included in - the experiment object. + :class:`InstSetting`, :class:`ExpCondition`, + :class:`Sample` is already included in the experiment object. """ ## TODO: possibly extend this by the details_list? for i in range(len(self.instrument_list)): if ((instrument == self.instrument_list[i]) - and (exp_setting == self.exp_setting_list[i]) + and (inst_setting == self.inst_setting_list[i]) + and (exp_condition == self.exp_condition_list[i]) and (sample == self.sample_list[i])): return True return False @@ -316,22 +323,35 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ -class ExpSetting(object): - """Description of the experimental settings. +class InstSetting(object): + """Description of the instrument settings. *Currently this is only text, but will be extended in the future.* - :param str details: Description of the experimental settings used for - the measurement (e.g. temperature, or size of observation + :param str details: Description of the instrument settings used for + the measurement (e.g. laser power or size of observation volume in case of confocal measurements). """ - def __init__(self, details=None): self.details = details def __eq__(self, other): return self.__dict__ == other.__dict__ +class ExpCondition(object): + """Description of the experimental conditions. + + * Currently this is only text, but will be extended in the future.* + + :param str details: Description of the experimental conditions (e.g. + the temperature at which the experiment was carried out). + """ + def __init__(self, details=None): + self.details = details + + def __eq__(self,other): + return self.__dict__ == other.__dict__ + class FRETAnalysis(object): """An analysis of FRET data that was performed. @@ -346,12 +366,19 @@ class FRETAnalysis(object): :type sample_probe_2: :class:`SampleProbeDetails` :param forster_radius: The Förster radius object for this FRET analysis. :type forster_radius: :class:`FRETForsterRadius`. + :param str type: The type of the FRET analysis (intensity-based or lifetime-based). :param calibration_parameters: The calibration parameters used for - this analysis. + this analysis (only in case of intensity-based analyses). :type calibration_parameters: :class:`FRETCalibrationParameters` + :param lifetime_fit_model: The fit model used in case of lifetime-based analyses. + :type lifetime_fit_model: :class:`LifetimeFitModel` + :param ref_measurement_group: The group of reference measurements + in case of lifetime-based analyses. + :type ref_measurement_group: :class:`LifetimeRefMeasurementGroup` :param str method_name: The method used for the analysis. :param float chi_square_reduced: The chi-square reduced as a quality measure for the fit. + :param float donor_only_fraction: The donor-only fraction. :param dataset: The dataset used. :type dataset: :class:`ihm.dataset.Dataset` :param external_file: The external file that contains (results of) @@ -361,16 +388,26 @@ class FRETAnalysis(object): """ def __init__(self, experiment, sample_probe_1, sample_probe_2, - forster_radius, calibration_parameters, method_name=None, - chi_square_reduced=None, dataset=None, - external_file=None, software=None): + forster_radius, type, calibration_parameters=None, + lifetime_fit_model = None, + ref_measurement_group = None, + method_name=None, details = None, + chi_square_reduced=None, donor_only_fraction=None, + dataset=None, external_file=None, software=None): + if type not in ['lifetime-based', 'intensity-based', None]: + raise ValueError('FRETAnalysis.type can be \'lifetime-based\' or \'intensity-based\'. The value is %s'%(type)) self.experiment = experiment self.sample_probe_1 = sample_probe_1 self.sample_probe_2 = sample_probe_2 self.forster_radius = forster_radius + self.type = type self.calibration_parameters = calibration_parameters + self.lifetime_fit_model = lifetime_fit_model + self.ref_measurement_group = ref_measurement_group self.method_name = method_name + self.details = details self.chi_square_reduced = chi_square_reduced + self.donor_only_fraction = donor_only_fraction self.dataset = dataset self.external_file = external_file self.software = software @@ -379,6 +416,86 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ +class LifetimeFitModel(object): + """A lifetime-fit model used for lifetime-based analysis. + + :param str name: The name of the fit model. + :param str description: A description of the fit model. + :param external_file: An external file that contains additional information on the fit model. + :param citation: A citation for the fit model. + :type citation: :class:`ihm.Citation` + """ + def __init__(self, name, description, external_file=None, citation=None): + self.name = name + self.description = description + self.external_file = external_file + self.citation = citation + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +class RefMeasurementGroup(object): + """A Group containing reference measurements for lifetime-based analysis. + + :param str details: Details on the Group of reference measurements. + """ + def __init__(self, details=None): + self.details = details + self.ref_measurement_list = [] + self.num_measurements = len(self.ref_measurement_list) + + def add_ref_measurement(self, ref_measurement): + """Add a lifetime reference measurement to a ref_measurement_group.""" + self.ref_measurement_list.append(ref_measurement) + self.num_measurements = len(self.ref_measurement_list) + + def get_info(self): + return self.ref_measurement_list + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +class RefMeasurement(object): + """A reference measurement for lifetime-based analysis. + + :param ref_sample_probe: The combination of sample and probe used for the reference measurement. + :type ref_sample_probe: :class:`SampleProbeDetails` + :param str details: Details on the measurement. + :param list_of_lifetimes: A list of the results from the reference measurement. + :type list_of_lifetimes: List of :class:`RefMeasurementLifetime` + """ + def __init__(self, ref_sample_probe, details=None, list_of_lifetimes=None): + self.ref_sample_probe = ref_sample_probe + self.details = details + self.list_of_lifetimes = list_of_lifetimes if list_of_lifetimes is not None else [] + self.num_species = len(self.list_of_lifetimes) + + def add_lifetime(self, lifetime): + """Add a lifetime to the list_of_lifetimes.""" + self.list_of_lifetimes.append(lifetime) + self.num_species = len(self.list_of_lifetimes) + + def __eq__(self,other): + return self.__dict__ == other.__dict__ + + +class RefMeasurementLifetime(object): + """Lifetime for a species in a reference measurement. + :param float species_fraction: The species-fraction for the respective lifetime. + :param float lifetime: The lifetime (in ns). + :param str species_name: A name for the species. + """ + def __init__(self, species_fraction, lifetime, species_name=None): + self.species_fraction = species_fraction + self.lifetime = lifetime + self.species_name = species_name + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + class FRETDistanceRestraintGroup(object): """A collection of FRET distance restraints that are used together. """ @@ -828,7 +945,8 @@ def __init__(self): ## The following dictionaries are so far only used when reading data self._collection_flr_experiment = {} - self._collection_flr_exp_setting = {} + self._collection_flr_inst_setting = {} + self._collection_flr_exp_condition = {} self._collection_flr_instrument = {} self._collection_flr_entity_assembly = {} self._collection_flr_sample_condition = {} @@ -842,6 +960,10 @@ def __init__(self): self._collection_flr_fret_forster_radius = {} self._collection_flr_fret_calibration_parameters = {} self._collection_flr_fret_analysis = {} + self._collection_flr_lifetime_fit_model = {} + self._collection_flr_ref_measurement_group = {} + self._collection_flr_ref_measurement = {} + self._collection_flr_ref_measurement_lifetime = {} self._collection_flr_peak_assignment = {} self._collection_flr_fret_distance_restraint = {} self._collection_flr_fret_distance_restraint_group = {} @@ -884,13 +1006,40 @@ def _all_forster_radii(self): def _all_calibration_parameters(self): """Yield all FRETCalibrationParameters objects""" for a in self._all_analyses(): - yield a.calibration_parameters + if a.type == 'intensity-based': + yield a.calibration_parameters + + def _all_lifetime_fit_models(self): + """Yield all LifetimeFitModel objects""" + for a in self._all_analyses(): + if a.type == 'lifetime-based': + yield a.lifetime_fit_model + + def _all_ref_measurement_groups(self): + """Yield all RefMeasurementGroup objects""" + for a in self._all_analyses(): + if a.type == 'lifetime-based': + yield a.ref_measurement_group + + def _all_ref_measurements(self): + """Yield all RefMeasurement objects""" + for rg in self._all_ref_measurement_groups(): + for x in rg.ref_measurement_list: + yield x + + def _all_ref_measurement_lifetimes(self): + """Yield all RefMeasurementLifetime objects""" + for r in self._all_ref_measurements(): + for x in r.list_of_lifetimes: + yield x def _all_sample_probe_details(self): """Yield all SampleProbeDetails objects""" for r in self._all_distance_restraints(): yield r.sample_probe_1 yield r.sample_probe_2 + for r in self._all_ref_measurements(): + yield r.ref_sample_probe def _all_samples(self): """Yield all Sample objects""" @@ -907,10 +1056,16 @@ def _all_poly_probe_positions(self): for s in self._all_sample_probe_details(): yield s.poly_probe_position - def _all_exp_settings(self): - """Yield all ExpSetting objects""" + def _all_inst_settings(self): + """Yield all InstSetting objects""" + for e in self._all_experiments(): + for s in e.inst_setting_list: + yield s + + def _all_exp_conditions(self): + """Yield all ExpCondition objects""" for e in self._all_experiments(): - for s in e.exp_setting_list: + for s in e.exp_condition_list: yield s def _all_instruments(self): diff --git a/modules/core/dependency/python-ihm/ihm/location.py b/modules/core/dependency/python-ihm/ihm/location.py index cbfbe505c3..248e229d34 100644 --- a/modules/core/dependency/python-ihm/ihm/location.py +++ b/modules/core/dependency/python-ihm/ihm/location.py @@ -6,6 +6,7 @@ class Location(object): """Identifies the location where a resource can be found. + Do not use this class itself, but one of its subclasses. Typically the resource may be found in a file (either on the local disk or at a DOI) - for this use one of the subclasses of :class:`FileLocation`. Alternatively the resource may be found in diff --git a/modules/core/dependency/python-ihm/ihm/reader.py b/modules/core/dependency/python-ihm/ihm/reader.py index e2b6ba36ed..dca0b0a8b4 100644 --- a/modules/core/dependency/python-ihm/ihm/reader.py +++ b/modules/core/dependency/python-ihm/ihm/reader.py @@ -21,6 +21,13 @@ except ImportError: _format = None + +class OldFileError(Exception): + """Exception raised if a file conforms to too old a version of the + IHM extension dictionary. See :func:`read`.""" + pass + + def _make_new_entity(): """Make a new Entity object""" e = ihm.Entity([]) @@ -560,10 +567,15 @@ def __init__(self, model_class, starting_model_class): #: Mapping from ID to :class:`ihm.flr.FLRData` objects self.flr_data = IDMapper(self.system.flr_data, ihm.flr.FLRData) - #: Mapping from ID to :class:`ihm.flr.ExpSetting` objects - self.flr_exp_settings = _FLRIDMapper('_collection_flr_exp_setting', + #: Mapping from ID to :class:`ihm.flr.InstSetting` objects + self.flr_inst_settings = _FLRIDMapper('_collection_flr_inst_setting', None, self.flr_data, - ihm.flr.ExpSetting) + ihm.flr.InstSetting) + + #: Mapping from ID to :class:`ihm.flr.ExpCondition` objects + self.flr_exp_conditions = _FLRIDMapper('_collection_flr_exp_condition', + None, self.flr_data, + ihm.flr.ExpCondition) #: Mapping from ID to :class:`ihm.flr.Instrument` objects self.flr_instruments = _FLRIDMapper('_collection_flr_instrument', @@ -625,7 +637,31 @@ def __init__(self, model_class, starting_model_class): #: Mapping from ID to :class:`ihm.flr.FRETAnalysis` objects self.flr_fret_analyses = _FLRIDMapper('_collection_flr_fret_analysis', None, self.flr_data, ihm.flr.FRETAnalysis, - *(None,)*10) + *(None,)*9) + + #: Mapping from ID to :class:`ihm.flr.LifetimeFitModel` objects + self.flr_lifetime_fit_models = _FLRIDMapper( + '_collection_flr_lifetime_fit_model', + None, self.flr_data, ihm.flr.LifetimeFitModel, + *(None,)*4) + + #: Mapping from ID to :class:`ihm.flr.RefMeasurementGroup` objects + self.flr_ref_measurement_groups = _FLRIDMapper( + '_collection_flr_ref_measurement_group', + None, self.flr_data, ihm.flr.RefMeasurementGroup, + *(None,)) + + #: Mapping from ID to :class:`ihm.flr.RefMeasurement` objects + self.flr_ref_measurements = _FLRIDMapper( + '_collection_flr_ref_measurement', + None, self.flr_data, ihm.flr.RefMeasurement, + *(None,)*3) + + #: Mapping from ID to :class:`ihm.flr.RefMeasurementLifetime` objects + self.flr_ref_measurement_lifetimes = _FLRIDMapper( + '_collection_flr_ref_measurement_lifetime', + None, self.flr_data, ihm.flr.RefMeasurementLifetime, + *(None,)*3) #: Mapping from ID to :class:`ihm.flr.PeakAssignment` objects self.flr_peak_assignments = _FLRIDMapper( @@ -815,6 +851,24 @@ def __call__(self, title, entry_id): mapkeys={'entry_id': 'id'}) +class _AuditConformHandler(Handler): + category = '_audit_conform' + + def __call__(self, dict_name, dict_version): + # Reject old file versions if we can parse the version + if dict_name == 'ihm-extension.dic': + try: + major, minor = [int(x) for x in dict_version.split('.')] + if (major, minor) < (1, 0): + raise OldFileError( + "This version of python-ihm only supports reading " + "files that conform to version 1.0 or later of the " + "IHM extension dictionary. This file conforms to " + "version %s." % dict_version) + except ValueError: + pass + + class _SoftwareHandler(Handler): category = '_software' @@ -2276,25 +2330,37 @@ class _FLRExperimentHandler(Handler): category = '_flr_experiment' def __call__(self, ordinal_id, id, instrument_id, - exp_setting_id, sample_id, details): + inst_setting_id, exp_condition_id, + sample_id, details): # Get the object or create the object experiment = self.sysr.flr_experiments.get_by_id(id) # Fill the object instrument = self.sysr.flr_instruments.get_by_id(instrument_id) - exp_setting = self.sysr.flr_exp_settings.get_by_id(exp_setting_id) + inst_setting = self.sysr.flr_inst_settings.get_by_id(inst_setting_id) + exp_condition = self.sysr.flr_exp_conditions.get_by_id(exp_condition_id) sample = self.sysr.flr_samples.get_by_id(sample_id) - experiment.add_entry(instrument=instrument, exp_setting=exp_setting, - sample=sample, details=details) + experiment.add_entry(instrument=instrument, inst_setting=inst_setting, + exp_condition=exp_condition, sample=sample, details=details) + + +class _FLRInstSettingHandler(Handler): + category = '_flr_inst_setting' + + def __call__(self, id, details): + # Get the object or create the object + cur_inst_setting = self.sysr.flr_inst_settings.get_by_id(id) + # Set the variables + self.copy_if_present(cur_inst_setting, locals(), keys=('details',)) -class _FLRExpSettingHandler(Handler): - category = '_flr_exp_setting' +class _FLRExpConditionHandler(Handler): + category = '_flr_exp_condition' def __call__(self, id, details): # Get the object or create the object - cur_exp_setting = self.sysr.flr_exp_settings.get_by_id(id) + cur_exp_condition = self.sysr.flr_exp_conditions.get_by_id(id) # Set the variables - self.copy_if_present(cur_exp_setting, locals(), keys=('details',)) + self.copy_if_present(cur_exp_condition, locals(), keys=('details',)) class _FLRInstrumentHandler(Handler): @@ -2475,27 +2541,111 @@ def __call__(self, id, phi_acceptor, alpha, alpha_sd, gg_gr_ratio, beta, class _FLRFretAnalysisHandler(Handler): category = '_flr_fret_analysis' - def __call__(self, id, experiment_id, sample_probe_id_1, sample_probe_id_2, - forster_radius_id, calibration_parameters_id, method_name, - chi_square_reduced, dataset_list_id, external_file_id, - software_id): + def __call__(self, id, experiment_id, type, + sample_probe_id_1, sample_probe_id_2, + forster_radius_id, dataset_list_id, + external_file_id, software_id): f = self.sysr.flr_fret_analyses.get_by_id(id) f.experiment = self.sysr.flr_experiments.get_by_id(experiment_id) + f.type = type f.sample_probe_1 = self.sysr.flr_sample_probe_details.get_by_id( sample_probe_id_1) f.sample_probe_2 = self.sysr.flr_sample_probe_details.get_by_id( sample_probe_id_2) f.forster_radius = self.sysr.flr_fret_forster_radius.get_by_id( forster_radius_id) - f.calibration_parameters \ - = self.sysr.flr_fret_calibration_parameters.get_by_id( - calibration_parameters_id) f.dataset = self.sysr.datasets.get_by_id(dataset_list_id) f.external_file = self.sysr.external_files.get_by_id_or_none( external_file_id) f.software = self.sysr.software.get_by_id_or_none(software_id) + + +class _FLRFretAnalysisIntensityHandler(Handler): + category = '_flr_fret_analysis_intensity' + + def __call__(self, ordinal_id, analysis_id, + calibration_parameters_id, donor_only_fraction, + chi_square_reduced, method_name, details): + f = self.sysr.flr_fret_analyses.get_by_id(analysis_id) + f.type = 'intensity-based' + f.calibration_parameters = \ + self.sysr.flr_fret_calibration_parameters.get_by_id(calibration_parameters_id) + f.donor_only_fraction = self.get_float(donor_only_fraction) + f.chi_square_reduced = self.get_float(chi_square_reduced) f.method_name = method_name + f.details = details + + +class _FLRFretAnalysisLifetimeHandler(Handler): + category = '_flr_fret_analysis_lifetime' + + def __call__(self, ordinal_id, analysis_id, + reference_measurement_group_id, lifetime_fit_model_id, + donor_only_fraction, chi_square_reduced, method_name, details): + f = self.sysr.flr_fret_analyses.get_by_id(analysis_id) + f.type = 'lifetime-based' + f.reference_measurement_group = self.sysr.flr_ref_measurement_groups.get_by_id(reference_measurement_group_id) + f.lifetime_fit_model = self.sysr.flr_lifetime_fit_models.get_by_id(lifetime_fit_model_id) + f.donor_only_fraction = self.get_float(donor_only_fraction) f.chi_square_reduced = self.get_float(chi_square_reduced) + f.method_name = method_name + f.details = details + + +class _FLRLifetimeFitModelHandler(Handler): + category = '_flr_lifetime_fit_model' + + def __call__(self, id, name, description, + external_file_id, citation_id): + f = self.sysr.flr_lifetime_fit_models.get_by_id(id) + f.name = name + f.description = description + f.external_file = \ + self.sysr.external_files.get_by_id_or_none(external_file_id) + f.citation = \ + self.sysr.citations.get_by_id_or_none(citation_id) + + +class _FLRRefMeasurementHandler(Handler): + category = '_flr_reference_measurement' + + def __call__(self, id, reference_sample_probe_id, + num_species, details): + r = self.sysr.flr_ref_measurements.get_by_id(id) + r.ref_sample_probe = self.sysr.flr_sample_probe_details.get_by_id(reference_sample_probe_id) + r.details = details + + +class _FLRRefMeasurementGroupHandler(Handler): + category = '_flr_reference_measurement_group' + + def __call__(self, id, num_measurements, details): + g = self.sysr.flr_ref_measurement_groups.get_by_id(id) + g.details = details + + +class _FLRRefMeasurementGroupLinkHandler(Handler): + category = '_flr_reference_measurement_group_link' + + def __call__(self, group_id, reference_measurement_id): + g = self.sysr.flr_ref_measurement_groups.get_by_id(group_id) + r = self.sysr.flr_ref_measurements.get_by_id(reference_measurement_id) + g.add_ref_measurement(r) + + +class _FLRRefMeasurementLifetimeHandler(Handler): + category = '_flr_reference_measurement_lifetime' + + def __call__(self, ordinal_id, reference_measurement_id, + species_name, species_fraction, lifetime): + l = self.sysr.flr_ref_measurement_lifetimes.get_by_id(ordinal_id) + l.species_name = species_name + l.species_fraction = self.get_float(species_fraction) + l.lifetime = self.get_float(lifetime) + + ## Add the lifetime to the reference measurement + r = self.sysr.flr_ref_measurements.get_by_id(reference_measurement_id) + r.add_lifetime(l) class _FLRPeakAssignmentHandler(Handler): @@ -2681,7 +2831,8 @@ def __call__(self, ordinal_id, fps_modeling_id, mpp_id, def read(fh, model_class=ihm.model.Model, format='mmCIF', handlers=[], warn_unknown_category=False, warn_unknown_keyword=False, read_starting_model_coord=True, - starting_model_class=ihm.startmodel.StartingModel): + starting_model_class=ihm.startmodel.StartingModel, + reject_old_file=False): """Read data from the file handle `fh`. Note that the reader currently expects to see a file compliant @@ -2726,6 +2877,10 @@ def read(fh, model_class=ihm.model.Model, format='mmCIF', handlers=[], is recommended to subclass :class:`ihm.startmodel.StartingModel` and override :meth:`~ihm.startmodel.StartingModel.add_atom` and/or :meth:`~ihm.startmodel.StartingModel.add_seq_dif`. + :param bool reject_old_file: If True, raise an + :exc:`ihm.reader.OldFileError` if the file conforms to an + older version of the dictionary than this library supports + (by default the library will read what it can from the file). :return: A list of :class:`ihm.System` objects. """ systems = [] @@ -2777,7 +2932,8 @@ def read(fh, model_class=ihm.model.Model, format='mmCIF', handlers=[], _CrossLinkListHandler(s), _CrossLinkRestraintHandler(s), _CrossLinkResultHandler(s), _StartingModelSeqDifHandler(s), _OrderedEnsembleHandler(s), _FLRChemDescriptorHandler(s), - _FLRExpSettingHandler(s), + _FLRInstSettingHandler(s), + _FLRExpConditionHandler(s), _FLRInstrumentHandler(s), _FLRSampleConditionHandler(s), _FLREntityAssemblyHandler(s), @@ -2793,6 +2949,13 @@ def read(fh, model_class=ihm.model.Model, format='mmCIF', handlers=[], _FLRFretForsterRadiusHandler(s), _FLRFretCalibrationParametersHandler(s), _FLRFretAnalysisHandler(s), + _FLRFretAnalysisIntensityHandler(s), + _FLRFretAnalysisLifetimeHandler(s), + _FLRLifetimeFitModelHandler(s), + _FLRRefMeasurementHandler(s), + _FLRRefMeasurementGroupHandler(s), + _FLRRefMeasurementGroupLinkHandler(s), + _FLRRefMeasurementLifetimeHandler(s), _FLRPeakAssignmentHandler(s), _FLRFretDistanceRestraintHandler(s), _FLRFretModelQualityHandler(s), @@ -2804,6 +2967,8 @@ def read(fh, model_class=ihm.model.Model, format='mmCIF', handlers=[], _FLRFPSMPPHandler(s), _FLRFPSMPPAtomPositionHandler(s), _FLRFPSMPPModelingHandler(s)] + [h(s) for h in handlers] + if reject_old_file: + hs.append(_AuditConformHandler(s)) if read_starting_model_coord: hs.append(_StartingModelCoordHandler(s)) if uchandler: diff --git a/modules/core/dependency/python-ihm/setup.py b/modules/core/dependency/python-ihm/setup.py index 97b562d0e3..0a140a4455 100755 --- a/modules/core/dependency/python-ihm/setup.py +++ b/modules/core/dependency/python-ihm/setup.py @@ -7,7 +7,7 @@ import sys import os -VERSION = "0.11" +VERSION = "0.12" copy_args = sys.argv[1:] diff --git a/modules/core/dependency/python-ihm/test/test_dumper.py b/modules/core/dependency/python-ihm/test/test_dumper.py index 7d40c4b408..38e8fb50ec 100644 --- a/modules/core/dependency/python-ihm/test/test_dumper.py +++ b/modules/core/dependency/python-ihm/test/test_dumper.py @@ -2942,8 +2942,10 @@ class MockObject(object): cur_entity_assembly.add_entity(entity=cur_entity_2, num_copies=2) cur_instrument = ihm.flr.Instrument(details='My_Instrument') - cur_exp_setting_1 = ihm.flr.ExpSetting(details='My_Exp_setting_1') - cur_exp_setting_2 = ihm.flr.ExpSetting(details='My_Exp_setting_2') + cur_inst_setting_1 = ihm.flr.InstSetting(details='My_Inst_setting_1') + cur_inst_setting_2 = ihm.flr.InstSetting(details='My_Inst_setting_2') + cur_exp_condition_1 = ihm.flr.ExpCondition(details='My_Exp_condition_1') + cur_exp_condition_2 = ihm.flr.ExpCondition(details='My_Exp_condition_2') cur_sample_condition_1 = ihm.flr.SampleCondition( details='My_Sample_condition_1') @@ -2962,13 +2964,27 @@ class MockObject(object): description='Sample_2', details='Details sample 2', solvent_phase='liquid') + ## Reference sample + cur_sample_3 = ihm.flr.Sample(entity_assembly=cur_entity_assembly, + num_of_probes=1, + condition=cur_sample_condition_1, + description='Reference Sample', + details='Details Reference Sample', + solvent_phase='liquid') + cur_experiment = ihm.flr.Experiment() cur_experiment.add_entry(instrument=cur_instrument, - exp_setting=cur_exp_setting_1, + inst_setting=cur_inst_setting_1, + exp_condition=cur_exp_condition_1, sample=cur_sample_1) cur_experiment.add_entry(instrument=cur_instrument, - exp_setting=cur_exp_setting_2, + inst_setting=cur_inst_setting_2, + exp_condition=cur_exp_condition_2, sample=cur_sample_2) + cur_experiment.add_entry(instrument=cur_instrument, + inst_setting=cur_inst_setting_1, + exp_condition=cur_exp_condition_1, + sample=cur_sample_3) ## Probes cur_probe_1 = ihm.flr.Probe() cur_probe_2 = ihm.flr.Probe() @@ -3055,6 +3071,11 @@ class MockObject(object): fluorophore_type='acceptor', poly_probe_position=cur_poly_probe_position_3, description='Acceptor in position2-position3') + cur_sample_probe_details_5 = ihm.flr.SampleProbeDetails(sample=cur_sample_3 , + probe=cur_probe_1, + fluorophore_type='donor', + poly_probe_position=cur_poly_probe_position_1, + description='Donor-only on reference sample') ## Poly_probe_conjugate ## Chem Descriptor ID 5 cur_poly_probe_conjugate_chem_descriptor = ihm.ChemDescriptor(auth_name='Conjugate', @@ -3072,9 +3093,13 @@ class MockObject(object): cur_poly_probe_conjugate_4 = ihm.flr.PolyProbeConjugate(sample_probe=cur_sample_probe_details_4, chem_descriptor=cur_poly_probe_conjugate_chem_descriptor, ambiguous_stoichiometry=False) + cur_poly_probe_conjugate_5 = ihm.flr.PolyProbeConjugate(sample_probe=cur_sample_probe_details_5, + chem_descriptor=cur_poly_probe_conjugate_chem_descriptor, + ambiguous_stoichiometry=False) cur_flr_data.poly_probe_conjugates.extend( (cur_poly_probe_conjugate_1, cur_poly_probe_conjugate_2, - cur_poly_probe_conjugate_3, cur_poly_probe_conjugate_4)) + cur_poly_probe_conjugate_3, cur_poly_probe_conjugate_4, + cur_poly_probe_conjugate_5)) ## Forster_radius cur_forster_radius = ihm.flr.FRETForsterRadius(donor_probe=cur_probe_1, @@ -3087,11 +3112,31 @@ class MockObject(object): phi_acceptor=0.35, alpha=2.4, gg_gr_ratio=0.4, a_b=0.8) cur_fret_calibration_parameters_2 = ihm.flr.FRETCalibrationParameters( phi_acceptor=0.35, alpha=2.4, gg_gr_ratio=0.38, a_b=0.8) - ## Fret_analysis + + ## LifetimeFitModel + cur_lifetime_fit_model = ihm.flr.LifetimeFitModel(name='Lifetime fit model 1', + description='Description of model') + + ## RefMeasurementLifetime + cur_lifetime_1 = ihm.flr.RefMeasurementLifetime(species_fraction=0.6, + lifetime=3.2) + cur_lifetime_2 = ihm.flr.RefMeasurementLifetime(species_fraction=0.4, + lifetime=1.4) + ## RefMeasurement + cur_ref_measurement_1 = ihm.flr.RefMeasurement(ref_sample_probe=cur_sample_probe_details_5, + details='Reference Measurement 1') + cur_ref_measurement_1.add_lifetime(cur_lifetime_1) + cur_ref_measurement_1.add_lifetime(cur_lifetime_2) + ## RefMeasurementGroup + cur_lifetime_ref_measurement_group = ihm.flr.RefMeasurementGroup(details='Reference measurement group 1') + cur_lifetime_ref_measurement_group.add_ref_measurement(cur_ref_measurement_1) + + ## FretAnalysis cur_fret_analysis_1 = ihm.flr.FRETAnalysis(experiment=cur_experiment, sample_probe_1=cur_sample_probe_details_1, sample_probe_2=cur_sample_probe_details_2, forster_radius=cur_forster_radius, + type='intensity-based', calibration_parameters=cur_fret_calibration_parameters_1, method_name='PDA', chi_square_reduced=1.5, @@ -3100,10 +3145,23 @@ class MockObject(object): sample_probe_1=cur_sample_probe_details_3, sample_probe_2=cur_sample_probe_details_4, forster_radius=cur_forster_radius, + type='intensity-based', calibration_parameters=cur_fret_calibration_parameters_2, method_name='PDA', chi_square_reduced=1.8, dataset=dataset_1) + ## lifetime-based FRETAnalysis + cur_fret_analysis_3 = ihm.flr.FRETAnalysis(experiment=cur_experiment, + sample_probe_1=cur_sample_probe_details_1, + sample_probe_2=cur_sample_probe_details_2, + forster_radius=cur_forster_radius, + type='lifetime-based', + lifetime_fit_model=cur_lifetime_fit_model, + ref_measurement_group=cur_lifetime_ref_measurement_group, + method_name='Lifetime fit', + chi_square_reduced=1.6, + dataset=dataset_1) + ## Peak_assignment cur_peak_assignment = ihm.flr.PeakAssignment(method_name='Population', details='Peaks were assigned by population fractions.') @@ -3128,10 +3186,21 @@ class MockObject(object): state=cur_state, population_fraction=0.80, peak_assignment=cur_peak_assignment) + cur_fret_distance_restraint_3 = ihm.flr.FRETDistanceRestraint(sample_probe_1=cur_sample_probe_details_1, + sample_probe_2=cur_sample_probe_details_2, + analysis=cur_fret_analysis_3, + distance=53.5, + distance_error_plus=2.5, + distance_error_minus=2.3, + distance_type='_E', + state=cur_state, + population_fraction=0.80, + peak_assignment=cur_peak_assignment) cur_fret_distance_restraint_group = ihm.flr.FRETDistanceRestraintGroup() cur_fret_distance_restraint_group.add_distance_restraint(cur_fret_distance_restraint_1) cur_fret_distance_restraint_group.add_distance_restraint(cur_fret_distance_restraint_2) + cur_fret_distance_restraint_group.add_distance_restraint(cur_fret_distance_restraint_3) cur_flr_data.distance_restraint_groups.append( cur_fret_distance_restraint_group) @@ -3232,8 +3301,11 @@ class MockObject(object): experiment_dumper = ihm.dumper._FLRExperimentDumper() experiment_dumper.finalize(system) - exp_setting_dumper = ihm.dumper._FLRExpSettingDumper() - exp_setting_dumper.finalize(system) + inst_setting_dumper = ihm.dumper._FLRInstSettingDumper() + inst_setting_dumper.finalize(system) + + exp_condition_dumper = ihm.dumper._FLR_ExpConditionDumper() + exp_condition_dumper.finalize(system) instrument_dumper = ihm.dumper._FLRInstrumentDumper() instrument_dumper.finalize(system) @@ -3265,6 +3337,12 @@ class MockObject(object): parameters_dumper = ihm.dumper._FLRCalibrationParametersDumper() parameters_dumper.finalize(system) + lifetime_fit_model_dumper = ihm.dumper._FLRLifetimeFitModelDumper() + lifetime_fit_model_dumper.finalize(system) + + ref_measurement_dumper = ihm.dumper._FLRRefMeasurementDumper() + ref_measurement_dumper.finalize(system) + analysis_dumper = ihm.dumper._FLRAnalysisDumper() analysis_dumper.finalize(system) @@ -3295,21 +3373,33 @@ class MockObject(object): _flr_experiment.ordinal_id _flr_experiment.id _flr_experiment.instrument_id -_flr_experiment.exp_setting_id +_flr_experiment.inst_setting_id +_flr_experiment.exp_condition_id _flr_experiment.sample_id _flr_experiment.details -1 1 1 1 1 . -2 1 1 2 2 . +1 1 1 1 1 1 . +2 1 1 2 2 2 . +3 1 1 1 1 3 . +# +""") + + out = _get_dumper_output(inst_setting_dumper, system) + self.assertEqual(out, """# +loop_ +_flr_inst_setting.id +_flr_inst_setting.details +1 My_Inst_setting_1 +2 My_Inst_setting_2 # """) - out = _get_dumper_output(exp_setting_dumper, system) + out = _get_dumper_output(exp_condition_dumper, system) self.assertEqual(out, """# loop_ -_flr_exp_setting.id -_flr_exp_setting.details -1 My_Exp_setting_1 -2 My_Exp_setting_2 +_flr_exp_condition.id +_flr_exp_condition.details +1 My_Exp_condition_1 +2 My_Exp_condition_2 # """) @@ -3357,6 +3447,7 @@ class MockObject(object): _flr_sample.solvent_phase 1 1 2 1 Sample_1 'Details sample 1' liquid 2 1 2 2 Sample_2 'Details sample 2' liquid +3 1 1 1 'Reference Sample' 'Details Reference Sample' liquid # """) @@ -3396,6 +3487,7 @@ class MockObject(object): 2 1 2 acceptor 'Acceptor in position1-position3' 2 3 2 1 donor 'Donor in position2-position3' 3 4 2 2 acceptor 'Acceptor in position2-position3' 2 +5 3 1 donor 'Donor-only on reference sample' 1 # """) @@ -3445,6 +3537,7 @@ class MockObject(object): 2 2 5 NO . 3 3 5 NO . 4 4 5 NO . +5 5 5 NO . # """) @@ -3475,6 +3568,52 @@ class MockObject(object): 1 0.350 2.400 . 0.400 . . . 0.800 2 0.350 2.400 . 0.380 . . . 0.800 # +""") + + out = _get_dumper_output(ref_measurement_dumper, system) + self.assertEqual(out, """# +loop_ +_flr_reference_measurement_group.id +_flr_reference_measurement_group.num_measurements +_flr_reference_measurement_group.details +1 1 'Reference measurement group 1' +# +# +loop_ +_flr_reference_measurement_group_link.group_id +_flr_reference_measurement_group_link.reference_measurement_id +1 1 +# +# +loop_ +_flr_reference_measurement.id +_flr_reference_measurement.reference_sample_probe_id +_flr_reference_measurement.num_species +_flr_reference_measurement.details +1 5 2 'Reference Measurement 1' +# +# +loop_ +_flr_reference_measurement_lifetime.ordinal_id +_flr_reference_measurement_lifetime.reference_measurement_id +_flr_reference_measurement_lifetime.species_name +_flr_reference_measurement_lifetime.species_fraction +_flr_reference_measurement_lifetime.lifetime +1 1 . 0.600 3.200 +2 1 . 0.400 1.400 +# +""") + + out = _get_dumper_output(lifetime_fit_model_dumper, system) + self.assertEqual(out, """# +loop_ +_flr_lifetime_fit_model.id +_flr_lifetime_fit_model.name +_flr_lifetime_fit_model.description +_flr_lifetime_fit_model.external_file_id +_flr_lifetime_fit_model.citation_id +1 'Lifetime fit model 1' 'Description of model' . . +# """) out = _get_dumper_output(analysis_dumper, system) @@ -3482,17 +3621,40 @@ class MockObject(object): loop_ _flr_fret_analysis.id _flr_fret_analysis.experiment_id +_flr_fret_analysis.type _flr_fret_analysis.sample_probe_id_1 _flr_fret_analysis.sample_probe_id_2 _flr_fret_analysis.forster_radius_id -_flr_fret_analysis.calibration_parameters_id -_flr_fret_analysis.method_name -_flr_fret_analysis.chi_square_reduced _flr_fret_analysis.dataset_list_id _flr_fret_analysis.external_file_id _flr_fret_analysis.software_id -1 1 1 2 1 1 PDA 1.500 1 . . -2 1 3 4 1 2 PDA 1.800 1 . . +1 1 intensity-based 1 2 1 1 . . +2 1 intensity-based 3 4 1 1 . . +3 1 lifetime-based 1 2 1 1 . . +# +# +loop_ +_flr_fret_analysis_intensity.ordinal_id +_flr_fret_analysis_intensity.analysis_id +_flr_fret_analysis_intensity.calibration_parameters_id +_flr_fret_analysis_intensity.donor_only_fraction +_flr_fret_analysis_intensity.chi_square_reduced +_flr_fret_analysis_intensity.method_name +_flr_fret_analysis_intensity.details +1 1 1 . 1.500 PDA . +2 2 2 . 1.800 PDA . +# +# +loop_ +_flr_fret_analysis_lifetime.ordinal_id +_flr_fret_analysis_lifetime.analysis_id +_flr_fret_analysis_lifetime.reference_measurement_group_id +_flr_fret_analysis_lifetime.lifetime_fit_model_id +_flr_fret_analysis_lifetime.donor_only_fraction +_flr_fret_analysis_lifetime.chi_square_reduced +_flr_fret_analysis_lifetime.method_name +_flr_fret_analysis_lifetime.details +1 3 1 1 . 1.600 'Lifetime fit' . # """) @@ -3524,6 +3686,7 @@ class MockObject(object): _flr_fret_distance_restraint.peak_assignment_id 1 1 1 1 2 1 1 53.500 2.500 2.300 _E 0.800 1 2 2 1 3 4 1 2 49.000 2.000 2.100 _E 0.800 1 +3 3 1 1 2 1 3 53.500 2.500 2.300 _E 0.800 1 # """) diff --git a/modules/core/dependency/python-ihm/test/test_flr.py b/modules/core/dependency/python-ihm/test/test_flr.py index 4dc780d655..e48127ec0f 100644 --- a/modules/core/dependency/python-ihm/test/test_flr.py +++ b/modules/core/dependency/python-ihm/test/test_flr.py @@ -249,94 +249,101 @@ def test_Experiment_Init(self): ## Initialization with only one parameter given should not add an entry e1 = ihm.flr.Experiment(instrument = 'foo') self.assertEqual(len(e1.instrument_list), 0) - self.assertEqual(len(e1.exp_setting_list),0) + self.assertEqual(len(e1.inst_setting_list),0) + self.assertEqual(len(e1.exp_condition_list),0) self.assertEqual(len(e1.sample_list),0) ## Correct initialization should fill the lists e2 = ihm.flr.Experiment(instrument = 'foo', - exp_setting = 'bar', - sample = 'foo2', + inst_setting = 'bar', + exp_condition='foo2', + sample = 'foo3', details = 'bar2') self.assertEqual(len(e2.instrument_list), 1) self.assertEqual(e2.instrument_list[0], 'foo') - self.assertEqual(e2.exp_setting_list[0], 'bar') - self.assertEqual(e2.sample_list[0], 'foo2') + self.assertEqual(e2.inst_setting_list[0], 'bar') + self.assertEqual(e2.exp_condition_list[0],'foo2') + self.assertEqual(e2.sample_list[0], 'foo3') self.assertEqual(e2.details_list[0], 'bar2') ## Initialization without details given should still have an entry in the list e3 = ihm.flr.Experiment(instrument = 'foo', - exp_setting = 'bar', + inst_setting = 'bar', + exp_condition = 'bar2', sample = 'foo2') self.assertEqual(len(e3.details_list),1) self.assertIsNone(e3.details_list[0]) - def test_Experiment_Add_entry(self): """ Test addition of an entry to the experiment. """ ## Adding to an empty Experiment e1 = ihm.flr.Experiment() e1.add_entry(instrument = 'foo', - exp_setting = 'bar', - sample = 'foo2', + inst_setting = 'bar', + exp_condition = 'foo2', + sample = 'foo3', details = 'bar2') self.assertEqual(e1.instrument_list[0],'foo') - self.assertEqual(e1.exp_setting_list[0], 'bar') - self.assertEqual(e1.sample_list[0],'foo2') + self.assertEqual(e1.inst_setting_list[0], 'bar') + self.assertEqual(e1.exp_condition_list[0],'foo2') + self.assertEqual(e1.sample_list[0],'foo3') self.assertEqual(e1.details_list[0], 'bar2') ## adding to an existing Experiment e2 = ihm.flr.Experiment(instrument = 'foo', - exp_setting = 'foo2', - sample = 'foo3', - details = 'foo4') + inst_setting = 'foo2', + exp_condition='foo3', + sample = 'foo4', + details = 'foo5') e2.add_entry(instrument = 'bar', - exp_setting = 'bar2', - sample = 'bar3', - details = 'bar4') + inst_setting = 'bar2', + exp_condition= 'bar3', + sample = 'bar4', + details = 'bar5') self.assertEqual(e2.instrument_list, ['foo','bar']) - self.assertEqual(e2.exp_setting_list, ['foo2','bar2']) - self.assertEqual(e2.sample_list, ['foo3','bar3']) - self.assertEqual(e2.details_list, ['foo4','bar4']) + self.assertEqual(e2.inst_setting_list, ['foo2','bar2']) + self.assertEqual(e2.exp_condition_list, ['foo3', 'bar3']) + self.assertEqual(e2.sample_list, ['foo4','bar4']) + self.assertEqual(e2.details_list, ['foo5','bar5']) def test_Experiment_Get_entry_by_index(self): """ Test access to entries by index. """ e = ihm.flr.Experiment() - e.add_entry(instrument = 'foo', exp_setting = 'foo2', sample = 'foo3', details = 'foo4') - e.add_entry(instrument = 'bar', exp_setting = 'bar2', sample = 'bar3', details = 'bar4') - e.add_entry(instrument = 'foobar', exp_setting = 'foobar2', sample = 'foobar3', details = 'foobar4') + e.add_entry(instrument = 'foo', inst_setting = 'foo2', exp_condition='foo3', sample = 'foo4', details = 'foo5') + e.add_entry(instrument = 'bar', inst_setting = 'bar2', exp_condition='bar3', sample = 'bar4', details = 'bar5') + e.add_entry(instrument = 'foobar', inst_setting = 'foobar2', exp_condition='foobar3', sample = 'foobar4', details = 'foobar5') return_value_index0 = e.get_entry_by_index(0) return_value_index1 = e.get_entry_by_index(1) return_value_index2 = e.get_entry_by_index(2) - self.assertEqual(return_value_index0, ('foo','foo2','foo3','foo4')) - self.assertEqual(return_value_index1, ('bar','bar2','bar3','bar4')) - self.assertEqual(return_value_index2, ('foobar','foobar2','foobar3','foobar4')) + self.assertEqual(return_value_index0, ('foo','foo2','foo3','foo4','foo5')) + self.assertEqual(return_value_index1, ('bar','bar2','bar3','bar4','bar5')) + self.assertEqual(return_value_index2, ('foobar','foobar2','foobar3','foobar4','foobar5')) def test_Experiment_Contains(self): """ Test whether experiment contains a combination of instrument, exp_setting, and sample. """ ## An empty experiment should not contain anything e1 = ihm.flr.Experiment() - self.assertFalse(e1.contains('foo','foo2','foo3')) + self.assertFalse(e1.contains('foo','foo2','foo3','foo4')) ## After addition, the entry should be contained - e1.add_entry(instrument = 'foo', exp_setting='foo2',sample = 'foo3') - e1.add_entry(instrument = 'bar', exp_setting='bar2',sample = 'bar3') - self.assertTrue(e1.contains('foo','foo2','foo3')) + e1.add_entry(instrument = 'foo', inst_setting='foo2', exp_condition='foo3', sample = 'foo4') + e1.add_entry(instrument = 'bar', inst_setting='bar2', exp_condition='bar3', sample = 'bar4') + self.assertTrue(e1.contains('foo','foo2','foo3','foo4')) ## If one of the entries is not contained, then False - self.assertFalse(e1.contains('foo2','foo2','foo4')) - self.assertFalse(e1.contains('foobar','foobar2','foobar3')) + self.assertFalse(e1.contains('foo2','foo2','foo4','foo5')) + self.assertFalse(e1.contains('foobar','foobar2','foobar3','foobar4')) def test_Experiment_Eq(self): """ Test equality and inequality of Experiment objects. """ e_ref = ihm.flr.Experiment() - e_ref.add_entry(instrument = 'foo', exp_setting='foo2',sample = 'foo3') + e_ref.add_entry(instrument = 'foo', inst_setting='foo2', exp_condition = 'foo3', sample = 'foo4') e_equal = ihm.flr.Experiment() - e_equal.add_entry(instrument = 'foo', exp_setting='foo2',sample = 'foo3') + e_equal.add_entry(instrument = 'foo', inst_setting='foo2', exp_condition = 'foo3', sample = 'foo4') e_unequal = ihm.flr.Experiment() - e_unequal.add_entry(instrument = 'bar', exp_setting='bar2',sample = 'bar3') + e_unequal.add_entry(instrument = 'bar', inst_setting='bar2', exp_condition = 'bar3', sample = 'bar4') self.assertTrue(e_ref == e_equal) self.assertFalse(e_ref == e_unequal) self.assertTrue(e_ref != e_unequal) - def test_Instrument_Init(self): """ Test initialization of Instrument. """ i = ihm.flr.Instrument(details = 'foo') @@ -351,16 +358,30 @@ def test_Instrument_Eq(self): self.assertFalse(i_ref == i_unequal) self.assertTrue(i_ref != i_unequal) - def test_exp_setting_init(self): - """ Test initialization of ExpSetting.""" - e = ihm.flr.ExpSetting(details = 'foo') + def test_inst_setting_init(self): + """ Test initialization of InstSetting.""" + e = ihm.flr.InstSetting(details = 'foo') + self.assertEqual(e.details, 'foo') + + def test_inst_setting_eq(self): + """ Test equality and inequality of InstSetting objects.""" + e_ref = ihm.flr.InstSetting(details = 'foo') + e_equal = ihm.flr.InstSetting(details = 'foo') + e_unequal = ihm.flr.InstSetting(details = 'bar') + self.assertTrue(e_ref == e_equal) + self.assertFalse(e_ref == e_unequal) + self.assertTrue(e_ref != e_unequal) + + def test_exp_condition_init(self): + """ Test initialization of ExpCondition.""" + e = ihm.flr.ExpCondition(details = 'foo') self.assertEqual(e.details, 'foo') - def test_exp_setting_eq(self): - """ Test equality and inequality of ExpSetting objects.""" - e_ref = ihm.flr.ExpSetting(details = 'foo') - e_equal = ihm.flr.ExpSetting(details = 'foo') - e_unequal = ihm.flr.ExpSetting(details = 'bar') + def test_exp_condition_eq(self): + """ Test equality and inequality of ExpCondition objects.""" + e_ref = ihm.flr.ExpCondition(details = 'foo') + e_equal = ihm.flr.ExpCondition(details = 'foo') + e_unequal = ihm.flr.ExpCondition(details = 'bar') self.assertTrue(e_ref == e_equal) self.assertFalse(e_ref == e_unequal) self.assertTrue(e_ref != e_unequal) @@ -371,9 +392,13 @@ def test_fret_analysis_init(self): sample_probe_1 = 'this_sample_probe_1', sample_probe_2 = 'this_sample_probe_2', forster_radius = 'this_forster_radius', + type = 'intensity-based', calibration_parameters = 'this_calibration_parameters', + lifetime_fit_model = 'this_lifetime_fit_model', + ref_measurement_group = 'this_ref_measurement_group', method_name = 'this_method_name', chi_square_reduced = 'this_chi_square_reduced', + donor_only_fraction='this_donly_fraction', dataset='this_dataset_list_id', external_file = 'this_external_file', software = 'this_software') @@ -381,22 +406,34 @@ def test_fret_analysis_init(self): self.assertEqual(f.sample_probe_1, 'this_sample_probe_1') self.assertEqual(f.sample_probe_2, 'this_sample_probe_2') self.assertEqual(f.forster_radius, 'this_forster_radius') + self.assertEqual(f.type, 'intensity-based') self.assertEqual(f.calibration_parameters, 'this_calibration_parameters') + self.assertEqual(f.lifetime_fit_model, 'this_lifetime_fit_model') + self.assertEqual(f.ref_measurement_group,'this_ref_measurement_group') self.assertEqual(f.method_name, 'this_method_name') self.assertEqual(f.chi_square_reduced, 'this_chi_square_reduced') + self.assertEqual(f.donor_only_fraction, 'this_donly_fraction') self.assertEqual(f.dataset, 'this_dataset_list_id') self.assertEqual(f.external_file, 'this_external_file') self.assertEqual(f.software, 'this_software') + self.assertRaises(ValueError, + ihm.flr.FRETAnalysis, experiment='this_experiment', + sample_probe_1='this_sample_probe_1', + sample_probe_2='this_sample_probe_2', + forster_radius='this_forster_radius', type='garbage') + def test_fret_analysis_eq(self): """ Test equality and inequality of FRETAnalysis objects. """ f_ref = ihm.flr.FRETAnalysis(experiment = 'this_experiment', sample_probe_1 = 'this_sample_probe_1', sample_probe_2 = 'this_sample_probe_2', forster_radius = 'this_forster_radius', + type = 'intensity-based', calibration_parameters = 'this_calibration_parameters', method_name = 'this_method_name', chi_square_reduced = 'this_chi_square_reduced', + donor_only_fraction='this_donly_fraction', dataset='this_dataset_list_id', external_file = 'this_external_file', software = 'this_software') @@ -404,9 +441,11 @@ def test_fret_analysis_eq(self): sample_probe_1 = 'this_sample_probe_1', sample_probe_2 = 'this_sample_probe_2', forster_radius = 'this_forster_radius', + type='intensity-based', calibration_parameters = 'this_calibration_parameters', method_name = 'this_method_name', chi_square_reduced = 'this_chi_square_reduced', + donor_only_fraction='this_donly_fraction', dataset='this_dataset_list_id', external_file = 'this_external_file', software = 'this_software') @@ -414,12 +453,141 @@ def test_fret_analysis_eq(self): sample_probe_1='foo', sample_probe_2='this_sample_probe_2', forster_radius='this_forster_radius', + type='intensity-based', calibration_parameters='this_calibration_parameters', method_name='this_method_name', chi_square_reduced='this_chi_square_reduced', + donor_only_fraction='this_donly_fraction', dataset='this_dataset_list_id', external_file='this_external_file', software='this_software') + f_unequal_type = ihm.flr.FRETAnalysis(experiment = 'this_experiment', + sample_probe_1 = 'this_sample_probe_1', + sample_probe_2 = 'this_sample_probe_2', + forster_radius = 'this_forster_radius', + type = 'lifetime-based', + calibration_parameters = 'this_calibration_parameters', + method_name = 'this_method_name', + chi_square_reduced = 'this_chi_square_reduced', + donor_only_fraction='this_donly', + dataset='this_dataset_list_id', + external_file = 'this_external_file', + software = 'this_software') + self.assertTrue(f_ref == f_equal) + self.assertFalse(f_ref == f_unequal) + self.assertTrue(f_ref != f_unequal) + self.assertFalse(f_ref == f_unequal_type) + self.assertTrue(f_ref != f_unequal_type) + + def test_lifetime_fit_model_init(self): + """ Test initialization of LifetimeFitModel.""" + f = ihm.flr.LifetimeFitModel(name = 'this_name', + description = 'this_description', + external_file = 'this_ext_file', + citation = 'this_citation') + self.assertEqual(f.name, 'this_name') + self.assertEqual(f.description, 'this_description') + self.assertEqual(f.external_file, 'this_ext_file') + self.assertEqual(f.citation, 'this_citation') + + def test_lifetime_fit_model_eq(self): + """ Test equality and inequality of LifeTimeFitModel objects.""" + f_ref = ihm.flr.LifetimeFitModel(name = 'this_name', description = 'this_desc') + f_equal = ihm.flr.LifetimeFitModel(name = 'this_name', description = 'this_desc') + f_unequal = ihm.flr.LifetimeFitModel(name = 'other_name', description = 'this_desc') + self.assertTrue(f_ref == f_equal) + self.assertFalse(f_ref == f_unequal) + self.assertTrue(f_ref != f_unequal) + + def test_ref_measurement_group_init(self): + """ Test initialization of RefMeasurementGroup.""" + r = ihm.flr.RefMeasurementGroup() + self.assertEqual(r.ref_measurement_list, []) + + def test_ref_measurement_group_add_ref_measurement(self): + """ Test the addition of a RefMeasurement to the group.""" + r = ihm.flr.RefMeasurementGroup() + r.add_ref_measurement('foo') + r.add_ref_measurement('bar') + self.assertEqual(r.ref_measurement_list, ['foo','bar']) + + def test_ref_measurement_group_get_info(self): + """ Test the retrieval of the ref_measurement_list.""" + r = ihm.flr.RefMeasurementGroup() + r.add_ref_measurement('foo') + r.add_ref_measurement('bar') + return_value = r.get_info() + self.assertEqual(return_value, ['foo', 'bar']) + + def test_ref_measurement_group_eq(self): + """ Test equality and inequality of RefMeasurementGroup objects.""" + r_ref = ihm.flr.RefMeasurementGroup() + r_ref.add_ref_measurement('foo') + r_equal = ihm.flr.RefMeasurementGroup() + r_equal.add_ref_measurement('foo') + r_unequal = ihm.flr.RefMeasurementGroup() + r_unequal.add_ref_measurement('foo2') + self.assertTrue(r_ref == r_equal) + self.assertFalse(r_ref == r_unequal) + self.assertTrue(r_ref != r_unequal) + + def test_ref_measurement_init(self): + """ Test initialization of RefMeasurement.""" + r1 = ihm.flr.RefMeasurement(ref_sample_probe = 'this_ref_sample_probe', + details = 'this_details') + self.assertEqual(r1.ref_sample_probe, 'this_ref_sample_probe') + self.assertEqual(r1.details, 'this_details') + self.assertEqual(r1.list_of_lifetimes, []) + + r2 = ihm.flr.RefMeasurement(ref_sample_probe = 'this_ref_sample_probe_2', + details = 'this_details_2', + list_of_lifetimes = ['foo','bar']) + self.assertEqual(r2.ref_sample_probe, 'this_ref_sample_probe_2') + self.assertEqual(r2.details, 'this_details_2') + self.assertEqual(r2.list_of_lifetimes, ['foo','bar']) + + def test_ref_measurement_add_lifetime(self): + """ Test addition of to the list_of_lifetimes.""" + r = ihm.flr.RefMeasurement(ref_sample_probe = 'this_ref_sample_probe', + details = 'this_details') + r.add_lifetime('foo') + r.add_lifetime('bar') + self.assertEqual(r.list_of_lifetimes, ['foo','bar']) + + def test_ref_measurement_eq(self): + """ Test equality and inequality of RefMeasurement objects.""" + r_ref = ihm.flr.RefMeasurement(ref_sample_probe = 'this_ref_sample_probe_1', + details = 'this_details_1') + r_equal = ihm.flr.RefMeasurement(ref_sample_probe='this_ref_sample_probe_1', + details='this_details_1') + r_unequal = ihm.flr.RefMeasurement(ref_sample_probe='this_ref_sample_probe_2', + details='this_details_2') + r_unequal_list = ihm.flr.RefMeasurement(ref_sample_probe = 'this_ref_sample_probe_1', + details = 'this_details_1', + list_of_lifetimes = ['foo']) + self.assertTrue(r_ref == r_equal) + self.assertTrue(r_ref != r_unequal) + self.assertTrue(r_ref != r_unequal_list) + self.assertFalse(r_ref == r_unequal) + self.assertFalse(r_ref == r_unequal_list) + + def test_ref_measurement_lifetime_init(self): + """ Test initialization of RefMeasuremenLifetime objects.""" + f = ihm.flr.RefMeasurementLifetime(species_fraction='this_frac', + lifetime='this_lifetime', + species_name='foo') + self.assertEqual(f.species_fraction, 'this_frac') + self.assertEqual(f.lifetime, 'this_lifetime') + self.assertEqual(f.species_name, 'foo') + + def test_ref_measurement_lifetime_eq(self): + """ Test equality and inequality of RefMeasurementLifetime objects.""" + f_ref = ihm.flr.RefMeasurementLifetime(species_fraction = 'this_frac_1', + lifetime = 'this_lifetime_1') + f_equal = ihm.flr.RefMeasurementLifetime(species_fraction = 'this_frac_1', + lifetime = 'this_lifetime_1') + f_unequal = ihm.flr.RefMeasurementLifetime(species_fraction = 'this_frac_2', + lifetime = 'this_lifetime_1') self.assertTrue(f_ref == f_equal) self.assertFalse(f_ref == f_unequal) self.assertTrue(f_ref != f_unequal) diff --git a/modules/core/dependency/python-ihm/test/test_reader.py b/modules/core/dependency/python-ihm/test/test_reader.py index 647dd25541..8be8fd6734 100644 --- a/modules/core/dependency/python-ihm/test/test_reader.py +++ b/modules/core/dependency/python-ihm/test/test_reader.py @@ -2456,6 +2456,43 @@ def test_read_full_pdbx(self): s, = ihm.reader.read(f) f.close() + def test_old_file_read_default(self): + """Test default handling of old files""" + cif = """ +loop_ +_audit_conform.dict_name +_audit_conform.dict_version +mmcif_pdbx.dic 5.311 +ihm-extension.dic 0.14 +""" + s, = ihm.reader.read(StringIO(cif)) + + def test_old_file_read_fail(self): + """Test failure reading old files""" + cif = """ +loop_ +_audit_conform.dict_name +_audit_conform.dict_version +mmcif_pdbx.dic 5.311 +ihm-extension.dic 0.14 +""" + self.assertRaises(ihm.reader.OldFileError, + ihm.reader.read, StringIO(cif), reject_old_file=True) + + def test_new_file_read_ok(self): + """Test success reading not-old files""" + # File read is OK if version is new enough, or version cannot be parsed + # because it is non-int or has too many elements + for ver in ('1.0', '0.0.4', '0.0a'): + cif = """ +loop_ +_audit_conform.dict_name +_audit_conform.dict_version +mmcif_pdbx.dic 5.311 +ihm-extension.dic %s +""" % ver + s, = ihm.reader.read(StringIO(cif), reject_old_file=True) + def test_warn_unknown_category(self): """Test warnings for unknown categories""" cif = """ @@ -2653,47 +2690,72 @@ def test_flr_experiment_handler(self): _flr_experiment.ordinal_id _flr_experiment.id _flr_experiment.instrument_id -_flr_experiment.exp_setting_id +_flr_experiment.inst_setting_id +_flr_experiment.exp_condition_id _flr_experiment.sample_id _flr_experiment.details -1 1 1 22 42 "exp 1" -2 1 1 2 2 . +1 1 1 12 22 42 "exp 1" +2 1 1 2 2 2 . """) s, = ihm.reader.read(fh) flr, = s.flr_data experiment, = list(flr._collection_flr_experiment.values()) self.assertIsInstance(experiment, ihm.flr.Experiment) self.assertIsInstance(experiment.instrument_list[0], ihm.flr.Instrument) - self.assertIsInstance(experiment.exp_setting_list[0], - ihm.flr.ExpSetting) + self.assertIsInstance(experiment.inst_setting_list[0], + ihm.flr.InstSetting) + self.assertIsInstance(experiment.exp_condition_list[0], + ihm.flr.ExpCondition) self.assertIsInstance(experiment.sample_list[0], ihm.flr.Sample) self.assertEqual([i._id for i in experiment.instrument_list], ['1', '1']) - self.assertEqual([i._id for i in experiment.exp_setting_list], + self.assertEqual([i._id for i in experiment.inst_setting_list], + ['12', '2']) + self.assertEqual([i._id for i in experiment.exp_condition_list], ['22', '2']) self.assertEqual([i._id for i in experiment.sample_list], ['42', '2']) self.assertEqual(experiment.details_list, ["exp 1", None]) - def test_flr_exp_setting_handler(self): - """Test FLRExpSettingHandler""" + def test_flr_inst_setting_handler(self): + """Test FLRInstSettingHandler""" + fh = StringIO(""" +loop_ +_flr_inst_setting.id +_flr_inst_setting.details +1 My_Inst_setting_1 +2 . +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + self.assertEqual(sorted(flr._collection_flr_inst_setting.keys()), + ['1', '2']) + is1 = flr._collection_flr_inst_setting['1'] + self.assertIsInstance(is1, ihm.flr.InstSetting) + self.assertEqual(is1.details, 'My_Inst_setting_1') + is2 = flr._collection_flr_inst_setting['2'] + self.assertIsInstance(is2, ihm.flr.InstSetting) + self.assertIsNone(is2.details) + + def test_flr_exp_condition_handler(self): + """Test FLRExpConditionHandler""" fh = StringIO(""" loop_ -_flr_exp_setting.id -_flr_exp_setting.details -1 My_Exp_setting_1 +_flr_exp_condition.id +_flr_exp_condition.details +1 My_Exp_condition_1 2 . """) s, = ihm.reader.read(fh) flr, = s.flr_data - self.assertEqual(sorted(flr._collection_flr_exp_setting.keys()), + self.assertEqual(sorted(flr._collection_flr_exp_condition.keys()), ['1', '2']) - es1 = flr._collection_flr_exp_setting['1'] - self.assertIsInstance(es1, ihm.flr.ExpSetting) - self.assertEqual(es1.details, 'My_Exp_setting_1') - es2 = flr._collection_flr_exp_setting['2'] - self.assertIsInstance(es2, ihm.flr.ExpSetting) - self.assertIsNone(es2.details) + ec1 = flr._collection_flr_exp_condition['1'] + self.assertIsInstance(ec1, ihm.flr.ExpCondition) + self.assertEqual(ec1.details, 'My_Exp_condition_1') + ec2 = flr._collection_flr_exp_condition['2'] + self.assertIsInstance(ec2, ihm.flr.ExpCondition) + self.assertIsNone(ec2.details) def test_flr_instrument_handler(self): """Test FLRInstrumentHandler""" @@ -3040,7 +3102,7 @@ def test_flr_fret_calibration_parameters_handler(self): s, = ihm.reader.read(fh) flr, = s.flr_data p1 = flr._collection_flr_fret_calibration_parameters['1'] - self.assertAlmostEqual(p1.phi_acceptor, 0.350, places=1) + self.assertAlmostEqual(p1.phi_acceptor, 0.350, places=2) self.assertAlmostEqual(p1.alpha, 2.400, places=1) self.assertAlmostEqual(p1.alpha_sd, 0.1, places=1) self.assertAlmostEqual(p1.gg_gr_ratio, 0.4, places=1) @@ -3055,16 +3117,15 @@ def test_flr_fret_analysis_handler(self): loop_ _flr_fret_analysis.id _flr_fret_analysis.experiment_id +_flr_fret_analysis.type _flr_fret_analysis.sample_probe_id_1 _flr_fret_analysis.sample_probe_id_2 _flr_fret_analysis.forster_radius_id -_flr_fret_analysis.calibration_parameters_id -_flr_fret_analysis.method_name -_flr_fret_analysis.chi_square_reduced _flr_fret_analysis.dataset_list_id _flr_fret_analysis.external_file_id _flr_fret_analysis.software_id -1 8 9 2 11 12 PDA 1.500 18 42 99 +1 8 intensity-based 9 2 11 18 42 99 +2 13 lifetime-based 24 5 19 32 81 98 """) s, = ihm.reader.read(fh) flr, = s.flr_data @@ -3077,17 +3138,210 @@ def test_flr_fret_analysis_handler(self): self.assertEqual(a.sample_probe_2._id, '2') self.assertIsInstance(a.forster_radius, ihm.flr.FRETForsterRadius) self.assertEqual(a.forster_radius._id, '11') - self.assertIsInstance(a.calibration_parameters, - ihm.flr.FRETCalibrationParameters) - self.assertEqual(a.calibration_parameters._id, '12') - self.assertEqual(a.method_name, 'PDA') - self.assertAlmostEqual(a.chi_square_reduced, 1.500, places=1) + self.assertEqual(a.type, 'intensity-based') self.assertIsInstance(a.dataset, ihm.dataset.Dataset) self.assertEqual(a.dataset._id, '18') self.assertIsInstance(a.external_file, ihm.location.Location) self.assertEqual(a.external_file._id, '42') self.assertIsInstance(a.software, ihm.Software) self.assertEqual(a.software._id, '99') + b = flr._collection_flr_fret_analysis['2'] + self.assertIsInstance(b.experiment, ihm.flr.Experiment) + self.assertEqual(b.experiment._id, '13') + self.assertIsInstance(b.sample_probe_1, ihm.flr.SampleProbeDetails) + self.assertEqual(b.sample_probe_1._id, '24') + self.assertIsInstance(b.sample_probe_2, ihm.flr.SampleProbeDetails) + self.assertEqual(b.sample_probe_2._id, '5') + self.assertIsInstance(b.forster_radius, ihm.flr.FRETForsterRadius) + self.assertEqual(b.forster_radius._id, '19') + self.assertEqual(b.type, 'lifetime-based') + self.assertIsInstance(b.dataset, ihm.dataset.Dataset) + self.assertEqual(b.dataset._id, '32') + self.assertIsInstance(b.external_file, ihm.location.Location) + self.assertEqual(b.external_file._id, '81') + self.assertIsInstance(b.software, ihm.Software) + self.assertEqual(b.software._id, '98') + + def test_flr_fret_analysis_intensity_handler(self): + """Test FLRFretAnalysisIntensityHandler""" + fh = StringIO(""" +loop_ +_flr_fret_analysis_intensity.ordinal_id +_flr_fret_analysis_intensity.analysis_id +_flr_fret_analysis_intensity.calibration_parameters_id +_flr_fret_analysis_intensity.donor_only_fraction +_flr_fret_analysis_intensity.chi_square_reduced +_flr_fret_analysis_intensity.method_name +_flr_fret_analysis_intensity.details +2 5 3 0.200 1.400 PDA Details +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + a = flr._collection_flr_fret_analysis['5'] + self.assertEqual(a.type, 'intensity-based') + self.assertIsInstance(a.calibration_parameters, ihm.flr.FRETCalibrationParameters) + self.assertEqual(a.calibration_parameters._id, '3') + self.assertAlmostEqual(a.donor_only_fraction, 0.2, places=1) + self.assertAlmostEqual(a.chi_square_reduced, 1.4, places=1) + self.assertEqual(a.method_name, 'PDA') + self.assertEqual(a.details, 'Details') + + def test_flr_fret_analysis_lifetime_handler(self): + """Test FLRFretAnalysisLifetimeHandler""" + fh = StringIO(""" +loop_ +_flr_fret_analysis_lifetime.ordinal_id +_flr_fret_analysis_lifetime.analysis_id +_flr_fret_analysis_lifetime.reference_measurement_group_id +_flr_fret_analysis_lifetime.lifetime_fit_model_id +_flr_fret_analysis_lifetime.donor_only_fraction +_flr_fret_analysis_lifetime.chi_square_reduced +_flr_fret_analysis_lifetime.method_name +_flr_fret_analysis_lifetime.details +4 2 19 23 0.300 1.500 'Lifetime fit' 'Details on lifetime fit' +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + a = flr._collection_flr_fret_analysis['2'] + self.assertEqual(a.type, 'lifetime-based') + self.assertIsInstance(a.reference_measurement_group, ihm.flr.RefMeasurementGroup) + self.assertEqual(a.reference_measurement_group._id, '19') + self.assertIsInstance(a.lifetime_fit_model, ihm.flr.LifetimeFitModel) + self.assertEqual(a.lifetime_fit_model._id, '23') + self.assertAlmostEqual(a.donor_only_fraction, 0.3, places=1) + self.assertAlmostEqual(a.chi_square_reduced, 1.5, places=1) + self.assertEqual(a.method_name, 'Lifetime fit') + self.assertEqual(a.details, 'Details on lifetime fit') + + def test_flr_lifetime_fit_model_handler(self): + """Test FLRLifetimeFitModelHandler""" + fh = StringIO(""" +loop_ +_flr_lifetime_fit_model.id +_flr_lifetime_fit_model.name +_flr_lifetime_fit_model.description +_flr_lifetime_fit_model.external_file_id +_flr_lifetime_fit_model.citation_id +1 'FitModel 15' 'Description of the fit model' 3 8 +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + f = flr._collection_flr_lifetime_fit_model['1'] + self.assertEqual(f.name, 'FitModel 15') + self.assertEqual(f.description, 'Description of the fit model') + self.assertIsInstance(f.external_file, ihm.location.Location) + self.assertEqual(f.external_file._id, '3') + self.assertIsInstance(f.citation, ihm.Citation) + self.assertEqual(f.citation._id, '8') + + def test_flr_ref_measurement_handler(self): + """Test FLRRefMeasurementHandler""" + fh = StringIO(""" +loop_ +_flr_reference_measurement.id +_flr_reference_measurement.reference_sample_probe_id +_flr_reference_measurement.num_species +_flr_reference_measurement.details +4 9 2 Details1 +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + r = flr._collection_flr_ref_measurement['4'] + self.assertIsInstance(r.ref_sample_probe, ihm.flr.SampleProbeDetails) + self.assertEqual(r.ref_sample_probe._id, '9') + self.assertEqual(r.details, 'Details1') + ## num_species is set automatically when adding lifetimes to the object + self.assertEqual(r.num_species, 0) + r.add_lifetime('1') + r.add_lifetime('2') + self.assertEqual(r.num_species, 2) + + def test_flr_ref_measurement_group_handler(self): + """Test FLRRefMeasurementGroupHandler""" + fh = StringIO(""" +loop_ +_flr_reference_measurement_group.id +_flr_reference_measurement_group.num_measurements +_flr_reference_measurement_group.details +5 3 Details +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + r = flr._collection_flr_ref_measurement_group['5'] + self.assertEqual(r.details, 'Details') + ## num_measurements is set automatically when adding measurements to the object + self.assertEqual(r.num_measurements, 0) + r.add_ref_measurement('1') + self.assertEqual(r.num_measurements, 1) + r.add_ref_measurement('2') + self.assertEqual(r.num_measurements, 2) + + def test_flr_ref_measurement_group_link_handler(self): + """Test FLRRefMeasurementGroupLinkHandler""" + fh = StringIO(""" +loop_ +_flr_reference_measurement_group_link.group_id +_flr_reference_measurement_group_link.reference_measurement_id +3 12 +3 25 +5 19 +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + g1 = flr._collection_flr_ref_measurement_group['3'] + self.assertEqual(g1.num_measurements, 2) + self.assertIsInstance(g1.ref_measurement_list[0], ihm.flr.RefMeasurement) + self.assertEqual(g1.ref_measurement_list[0]._id, '12') + self.assertIsInstance(g1.ref_measurement_list[1], ihm.flr.RefMeasurement) + self.assertEqual(g1.ref_measurement_list[1]._id, '25') + g2 = flr._collection_flr_ref_measurement_group['5'] + self.assertEqual(g2.num_measurements, 1) + self.assertIsInstance(g2.ref_measurement_list[0], ihm.flr.RefMeasurement) + self.assertEqual(g2.ref_measurement_list[0]._id, '19') + + def test_flr_ref_measurement_lifetime_handler(self): + """Test FLRRefMeasurementLifetimeHandler""" + fh = StringIO(""" +loop_ +_flr_reference_measurement_lifetime.ordinal_id +_flr_reference_measurement_lifetime.reference_measurement_id +_flr_reference_measurement_lifetime.species_name +_flr_reference_measurement_lifetime.species_fraction +_flr_reference_measurement_lifetime.lifetime +1 15 species1 0.300 4.100 +2 15 species2 0.700 2.100 +3 12 species1 1.000 3.800 +""") + s, = ihm.reader.read(fh) + flr, = s.flr_data + ## Check the lifetime objects themselves + f1 = flr._collection_flr_ref_measurement_lifetime['1'] + self.assertEqual(f1.species_name, 'species1') + self.assertAlmostEqual(f1.species_fraction, 0.3, places=1) + self.assertAlmostEqual(f1.lifetime, 4.1, places=1) + f2 = flr._collection_flr_ref_measurement_lifetime['2'] + self.assertEqual(f2.species_name, 'species2') + self.assertAlmostEqual(f2.species_fraction, 0.7, places=1) + self.assertAlmostEqual(f2.lifetime, 2.1, places=1) + f3 = flr._collection_flr_ref_measurement_lifetime['3'] + self.assertEqual(f3.species_name, 'species1') + self.assertAlmostEqual(f3.species_fraction, 1.0, places=1) + self.assertAlmostEqual(f3.lifetime, 3.8, places=1) + ## And check the respective reference measurement objects + r1 = flr._collection_flr_ref_measurement['15'] + self.assertIsInstance(r1.list_of_lifetimes[0], ihm.flr.RefMeasurementLifetime) + self.assertEqual(r1.list_of_lifetimes[0].species_name, 'species1') + self.assertAlmostEqual(r1.list_of_lifetimes[0].species_fraction, 0.3, places=1) + self.assertAlmostEqual(r1.list_of_lifetimes[0].lifetime, 4.1, places=1) + self.assertIsInstance(r1.list_of_lifetimes[1], ihm.flr.RefMeasurementLifetime) + self.assertEqual(r1.list_of_lifetimes[1].species_name, 'species2') + self.assertAlmostEqual(r1.list_of_lifetimes[1].species_fraction, 0.7, places=1) + self.assertAlmostEqual(r1.list_of_lifetimes[1].lifetime, 2.1, places=1) + r2 = flr._collection_flr_ref_measurement['12'] + self.assertIsInstance(r2.list_of_lifetimes[0], ihm.flr.RefMeasurementLifetime) + self.assertEqual(r2.list_of_lifetimes[0].species_name, 'species1') + self.assertAlmostEqual(r2.list_of_lifetimes[0].species_fraction, 1.0, places=1) + self.assertAlmostEqual(r2.list_of_lifetimes[0].lifetime, 3.8, places=1) def test_flr_peak_assignment_handler(self): """Test FLRPeakAssignmentHandler"""