Source code for ccdc.csp.database

#
# This code is Copyright (C) 2020 The Cambridge Crystallographic Data Centre
# (CCDC) of 12 Union Road, Cambridge CB2 1EZ, UK and a proprietary work of CCDC.
# This code may not be used, reproduced, translated, modified, disassembled or
# copied, except in accordance with a valid licence agreement with CCDC and may
# not be disclosed or redistributed in any form, either in whole or in part, to
# any third party. All copies of this code made in accordance with a valid
# licence agreement as referred to above must contain this copyright notice.
#
# No representations, warranties, or liabilities are expressed or implied in the
# supply of this code by CCDC, its servants or agents, except where such
# exclusion or limitation is prohibited, void or unenforceable under governing
# law.
#
'''
The main class of the :mod:`ccdc.csp.database` module is :class:`ccdc.csp.database.CspDatabase`.

A :class:`ccdc.csp.database.CspDatabase` represents a database of crystal structure predictions.
The database can hold predictions for many different molecules, with sets of predictions for a
particular molecule being organised into landscapes.

Connecting to CSP databases
---------------------------

A CSP database
actually consists of two distinct parts with different connection methods, this is
a temporary situation which will be replaced with a single database in the future.
The two databases are identified as follows:

1. A crystal structure database file in csdsqlx format, which must be visible in
   the file system on which a CSPC python script is run. This database file will
   be created if it does not already exist. This database file can be tested, and
   its contents examined, by opening it in Mercury.

2. A CSP metadata database which is accessed via a web-service URL. This web-service
   will usually be hosted on a non-public web server that is visible to CSP users. The
   web-service URL will normally consist of a hostname and port number, optionally there
   may be some extra path. This URL should be supplied by your local system
   administrators. The URL can be tested by appending "/api/CSPMetadata" to it and
   opening the result in a web browser. You should see a JSON formatted list as a
   response if the service is working correctly, the list will be empty "[]" if no
   predictions have been added to the database yet.

Retrieving prediction data from a database::

    # open a database
    db = CspDatabase("my_predictions.csdsqlx", "http://my.prediction.metadata.server:2468")

    # get the landscape names held in the database
    print(db.landscape_names)

    # print the energy and calculated density values for the entries in a landscape named "XXV"
    for id in db.prediction_identifiers('XXV'):
        prediction = db.prediction(id)
        print(prediction.classification_energy_relative, prediction.crystal.calculated_density)

Such databases should be populated by the :mod:`utilities.import_csp_landscape.import_csp_landscape`
script.

'''

from ccdc.csp.prediction import Prediction

from ccdc.utilities import _private_importer
with _private_importer() as pi:
    pi.import_ccdc_module('CspLib')
    pi.import_ccdc_module('UtilitiesLib')

[docs]class CspDatabase: '''A database of crystal structure predictions.''' def __init__(self, structure_db_filename, metadata_host_url): '''Open a Crystal Structure Prediction database. :param structure_db_filename: The filename of the structure database where the crystal data is held. :param metadata_host_url: The base url of the server hosting the CSP metadata database. ''' self._db = CspLib.CspDatabase(structure_db_filename, metadata_host_url)
[docs] def prediction(self, identifier: str) -> Prediction: '''Return a Prediction by identifier.''' return Prediction(self._db.entry(UtilitiesLib.DatabaseEntryIdentifier(identifier)))
def _add_prediction(self, prediction: Prediction): self._db.add_entry(prediction._prediction) def _add_similar_structures(self, similar_structures): self._db.add_similar_structures(similar_structures) @property def landscape_names(self): '''Return the names of landscapes in the database.''' return self._db.landscape_identifiers()
[docs] def prediction_identifiers(self, landscape_name : str): '''Return the prediction identifiers in a named landscape in the database.''' return self._db.entry_identifiers(landscape_name)
def _delete_landscape(self, landscape: str): self._db.delete_landscape(landscape)
[docs] def cross_reference(self, observed_structure_db, log_file=None): '''Cross reference this CSP database with a database of observed crystal structures. :param observed_structure_db: a crystal structure database containing experimentally observed structures :param log_file: the name of a log file ''' cr = CspLib.CspCrossReferencer(self._db, observed_structure_db._db) if log_file: cr.set_log_file(log_file) cr.run()
[docs] class LandscapeSummary: '''Summary information about a CSP landscape''' def __init__(self, summary): self._summary = summary @property def lowest_energy_prediction_name(self): '''Return the lowest energy prediction name''' return self._summary.lowest_energy_prediction_name_ @property def total_number_predictions(self): '''Return the total number of predictions''' return self._summary.total_number_predictions_ @property def number_predictions_0K(self): '''Return the number of predictions at temperature 0K''' return self._summary.number_predictions_0K_ @property def number_simulated_T_non_zero(self): '''Return the number of simulated temperatures above 0K''' return self._summary.number_simulated_T_non_zero_ @property def number_exp_matches(self): '''Return the number of predictions with matches against experimentally observed structures''' return self._summary.number_exp_matches_ @property def experimental_refcodes(self): '''Return the experimental refcodes that were matched''' return self._summary.experimental_refcodes_ @property def matching_prediction_names(self): '''Return the prediction names that matched experimentally observed structures''' return self._summary.matching_prediction_names_ @property def spacegroup_frequency(self): '''Return the spacegroup frequency counts for this landscape''' return dict(self._summary.spacegroup_frequency_) @property def z_prime_frequency(self): '''Return the Z' frequency counts for this landscape''' return dict(self._summary.z_prime_frequency_) @property def max_0K_relative_energy(self): '''Return the maximum relative lattice energy at 0K for this landscape''' return self._summary.max_0K_relative_energy_ @property def molecular_shape_descriptor_average_max(self): '''Return the maximum molecular shape descriptor average for this landscape''' return self._summary.molecular_shape_descriptor_average_max_ @property def molecular_shape_descriptor_average_min(self): '''Return the minimum molecular shape descriptor average for this landscape''' return self._summary.molecular_shape_descriptor_average_min_ @property def molecular_shape_descriptor_difference_max(self): '''Return the maximum molecular shape descriptor difference for this landscape''' return self._summary.molecular_shape_descriptor_difference_max_ @property def molecular_shape_descriptor_difference_min(self): '''Return the minimum molecular shape descriptor difference for this landscape''' return self._summary.molecular_shape_descriptor_difference_min_ @property def hbond_inter_frequency(self): '''Return the intermolecular hydrogen bond frequency for this landscape''' return self._summary.hbond_inter_frequency_ @property def hbond_intra_frequency(self): '''Return the intramolecular hydrogen bond frequency for this landscape''' return self._summary.hbond_intra_frequency_ @property def void_volume_max(self): '''Return the maximum void volume, in cubic Angstroms, for this landscape''' return self._summary.void_volume_max_ @property def void_volume_min(self): '''Return the minimum void volume, in cubic Angstroms, for this landscape''' return self._summary.void_volume_min_ @property def packing_coefficient_max(self): '''Return the maximum packing coefficient for this landscape''' return self._summary.packing_coefficient_max_ @property def packing_coefficient_min(self): '''Return the minimum packing coefficient for this landscape''' return self._summary.packing_coefficient_min_
[docs] def landscape_summary(self, landscape_name:str) -> LandscapeSummary: '''Return a summary of the named landscape''' summary = self._db.landscape_summary(landscape_name) return self.LandscapeSummary(summary)