diff --git a/modules/conop/doc/conop.rst b/modules/conop/doc/conop.rst index 6e8443558c8345033692c55e3d84c302d200b93d..8eed48919c547cadf4465a44d0d0388176c1e532 100644 --- a/modules/conop/doc/conop.rst +++ b/modules/conop/doc/conop.rst @@ -169,6 +169,14 @@ files to use the rule-based builder. :param residue: must be a valid residue :type residue: mol.ResidueHandle + .. method:: GuessChemClass(residue) + + Guesses the chemical class of the residue based on its atom and + connectivity. + + So far, the method only guesses whether the residue is a peptide. A residue + is a peptide if all the backbone atoms N,CA,C,O are present, have the right + element and are in a suitable orientation to form bonds. .. class:: RuleBasedBuilder diff --git a/modules/gui/pymod/export_plot.cc b/modules/gui/pymod/export_plot.cc index a921472ad6c8134166d507c956f304f6306eff2f..b233efeb145ec8ae98fdf707845657c659a3246c 100644 --- a/modules/gui/pymod/export_plot.cc +++ b/modules/gui/pymod/export_plot.cc @@ -38,6 +38,7 @@ namespace { struct PlotFunctionWrap : PlotFunction, wrapper<PlotFunction> { + IOProfileWrap(PyObject*&, const ost::io::IOProfile&) Real Func(Real val) const { return this->get_override("Func")(val); diff --git a/modules/gui/src/file_loader.cc b/modules/gui/src/file_loader.cc index 84c542de1c0209c059fa678701f112f6dc8b783b..546f989757696761ffb961000d9c32bdad543930 100644 --- a/modules/gui/src/file_loader.cc +++ b/modules/gui/src/file_loader.cc @@ -382,7 +382,7 @@ void FileLoader::RunScript(const QString& filename) void FileLoader::LoadPDB(const QString& filename, const QString& selection) { - io::PDBReader reader(filename.toStdString()); + io::PDBReader reader(filename.toStdString(), io::IOProfile()); QList<mol::EntityHandle> entities; conop::BuilderP builder=conop::Conopology::Instance().GetBuilder("DEFAULT"); while (reader.HasNext()){ diff --git a/modules/io/doc/io.rst b/modules/io/doc/io.rst index b2037b3addfce4c660774d6177e33c261db729a6..141a1434cd0522e476dc605fea0e8c8976ea9cee 100644 --- a/modules/io/doc/io.rst +++ b/modules/io/doc/io.rst @@ -5,6 +5,7 @@ :hidden: formats + profile .. module:: ost.io :synopsis: Input and output of sequences, alignments, structures, images and density maps. @@ -61,6 +62,10 @@ parameters that affect the import. PDB files can be loaded with :func:`~ost.io.LoadPDB`. It offers a tighter control over the exact loading behaviour. + +:doc:`profile` + + .. autofunction:: ost.io.LoadPDB diff --git a/modules/io/doc/profile.rst b/modules/io/doc/profile.rst new file mode 100644 index 0000000000000000000000000000000000000000..c33ce580befc04fd30e78d553381c11b40774659 --- /dev/null +++ b/modules/io/doc/profile.rst @@ -0,0 +1,131 @@ +IO Profiles for entity importer +================================================================================ + +.. currentmodule:: ost.io + +As of version 1.1, OpenStructure introduces IO profiles to fine-tune the +behaviour of the molecule importers. A profile aggregates flags and methods +that affect the import of molecular structures and influence both the behaviour +of :mod:`~ost.conop` and :mod:`~ost.io`. + +Basic usage of IO profiles +-------------------------------------------------------------------------------- + +You are most certainly reading this document because you were having trouble +loading PDB files. In that case, as a first step you will want to set the profile parameter of :func:`LoadPDB`. The profile +parameter can either be the name of a profile or an instance of +:class:`IOProfile`. Both of the following two examples are equivalent: + +.. code-block:: python + + ent=io.LoadPDB('weird.pdb', profile=io.profiles['SLOPPY']) + ent=io.LoadPDB('weird.pdb', profile='SLOPPY') + +Profiles is a dictionary-like object containing all the profiles known to OpenStructure. You can add new ones by inserting them into the dictionary. If you are loading a lot of structures, you may want to set the default profile to avoid having to pass the profile every time you load a structure. This is done by assigning a different profile to ``DEFAULT``: + +.. code-block:: python + + io.profiles['DEFAULT']='SLOPPY' + ent=io.LoadPDB('weird.pdb') + +Again, you can either assign the name of the profile, or the profile itself. If none of the profiles available by default suits your needs, feel free to create one to your liking. + +Available default profiles +-------------------------------------------------------------------------------- + +The following profiles are available by default. For a detailed description of what the different parameters mean, consult the documentation of the :class:`IOProfile` class. + +STRICT + + This profile is the default and is known to work very well with PDB files + coming from the official PDB website. It is equivalent to the following + profile: + + .. code-block:: python + + IOProfile(dialect='PDB', strict_hydrogens=False, quack_mode=False, + fault_tolerant=False, join_spread_atom_records=False, + no_hetatms=False) + +SLOPPY: + + This profile loads essentially everything + + .. code-block:: python + + IOProfile(dialect='PDB', strict_hydrogens=False, quack_mode=True, + fault_tolerant=True, join_spread_atom_records=False, + no_hetatms=False) + +CHARMM: + + This format is the default when importing CHARMM trajectories and turns on the + CHARMM specific compound dictionary. + + .. code-block:: python + + IOProfile(dialect='CHARMM', strict_hydrogens=False, quack_mode=True, + fault_tolerant=True, join_spread_atom_records=True, + no_hetatms=False) + + +The IOProfile Class +-------------------------------------------------------------------------------- + +.. class:: IOProfile(quack_mode=False, dialect='PDB', strict_hydrogens=False, fault_tolerant=False) + + Aggregates flags and methods that control the import of molecular structures. + + .. method:: PostImport(ent) + + Called after the import has completed. Useful to perform post processing of + the imported entity, e.g. to renumber the residues. The default + implementation does nothing. + + .. attribute:: quack_mode + + :type: bool + + Read/write property. When quack_mode is enabled, the chemical class for + unknown residues is guessed based on its atoms and connectivity. Turn this + on, if you are working with non-standard conforming PDB files and are + experiencing problems with the rendering of the backbone trace and/or see + peptidic residues with unknown chemical class. + + .. attribute:: dialect + + :type: str + + The dialect to be used for PDB files. At the moment, this is either CHARMM + or PDB. More will most likely come in the future. By setting the dialect to + CHARMM, the loading is optimized for CHARMM PDB files. This turns on + support for chain names with length up to 4 characters (column 72-76) and + increase the size of the residue name to 4 residues. + + .. attribute:: strict_hydrogens + + :type: bool + + Whether hydrogen names should be strictly checked. It is very common for + PDB files to not follow the correct naming conventions for hydrogen atoms. + That's why by default, the names of the hydrogens are not required to be + correct. Rather, the connectivity is inferred with distance-based checks. By + turning this flag on, the names of the hydrogen atoms are checked against + the names in the database like all other atom types. + + .. attribute:: no_hetatms + + If set to true, HETATM records are ignored during import. + + .. attribute:: fault_tolerant + + :type: bool + + If true, the import will succeed, even if the PDB contains faulty records. + The faulty records will be ignored and import continues as if the records + haven't been present. + + .. attribute:: join_spread_atom_records + + If set to true, atom records belonging to the same residue are joined, even + if they do not appear sequentially in the PDB file. diff --git a/modules/io/pymod/__init__.py b/modules/io/pymod/__init__.py index 6b4d05b816fbda4db340a72bf4faabb46b937e17..30ea9e3a6066ae64678200582dac595196ac90a4 100644 --- a/modules/io/pymod/__init__.py +++ b/modules/io/pymod/__init__.py @@ -21,6 +21,43 @@ import os, tempfile, ftplib, httplib from _io import * from ost import mol, conop +profiles=None + +class IOProfiles: + def __init__(self): + self._dict={} + + def __getitem__(self, key): + return IOProfileRegistry.Instance().Get(key) + + def __setitem__(self, key, value): + if isinstance(value, str): + value=self[value] + IOProfileRegistry.Instance().Set(key, value) + self._dict[key]=value + + def __len__(self): + return len(self._dict) + + def __iter__(self): + return self._dict.__iter__() + +if not profiles: + profiles=IOProfiles() + profiles['STRICT']=IOProfile(dialect='PDB', fault_tolerant=False, + strict_hydrogens=False, quack_mode=False) + profiles['SLOPPY']=IOProfile(dialect='PDB', fault_tolerant=True, + strict_hydrogens=False, quack_mode=True) + profiles['CHARMM']=IOProfile(dialect='CHARMM', fault_tolerant=True, + strict_hydrogens=False, quack_mode=False) + profiles['DEFAULT']='STRICT' + +def _override(val1, val2): + if val2!=None: + return val2 + else: + return val1 + def __GetModelFromPDB(model_id, output_dir, file_pattern='pdb%s.ent.gz'): file_name = file_pattern % model_id file_path = os.path.join(output_dir,file_name) @@ -45,58 +82,70 @@ def __GetModelFromPDB(model_id, output_dir, file_pattern='pdb%s.ent.gz'): return False return os.path.getsize(file_path) > 0 -def LoadPDB(filename, restrict_chains="", no_hetatms=False, - fault_tolerant=False, load_multi=False, - join_spread_atom_records=False, calpha_only=False, - remote=False, dialect='PDB', strict_hydrogens=False): +def LoadPDB(filename, restrict_chains="", no_hetatms=None, + fault_tolerant=None, load_multi=False, quack_mode=None, + join_spread_atom_records=None, calpha_only=None, + profile='DEFAULT', remote=False, dialect=None, + strict_hydrogens=None): """ Load PDB file from disk and returns one or more entities. Several options - allow to customize the exact behaviour of the PDB import. + allow to customize the exact behaviour of the PDB import. For more information + on these options, see :doc:`profile`. Residues are flagged as ligand if they are mentioned in a HET record. :param restrict_chains: If not an empty string, only chains listed in the string will be imported. - :param fault_tolerant: If True, the import will succeed, even if the - PDB contains faulty records. The faulty records will be ignored and import - continues as if the records haven't been present. + :param fault_tolerant: Enable/disable fault-tolerant import. If set, overrides + the value of :attr:`IOProfile.fault_tolerant`. - :param no_hetatms: If set to True, HETATM records will be ignored + :param no_hetatms: If set to True, HETATM records will be ignored. Overrides + the value of :attr:`IOProfile.no_hetatms` :param load_multi: If set to True, a list of entities will be returned instead - of only the first. This is useful when dealing with multi-PDB files. + of only the first. This is useful when dealing with multi-PDB files. - :param join_spread_atom_records: If set to true, atom records belonging to the - same residue are joined, even if they do not appear sequentially in the PDB - file. + :param join_spread_atom_records: If set, overrides the value of + :attr:`IOProfile.join_spread_atom_records`. - :param remote: If set to true, the method tries to load the pdb from the remote - pdb repository www.pdb.org. + :param remote: If set to true, the method tries to load the pdb from the + remote pdb repository www.pdb.org. The filename is then interpreted as the + pdb id. + :rtype: :class:`~ost.mol.EntityHandle` or a list thereof if `load_multi` is True. - :param dialect: Specifies the particular dialect to use. By default, the - official PDB format is used. Alternatively, by setting the dialect to - CHARMM, the loading is optimized for CHARMM PDB files. This turns on - support for chain names with length up to 4 characters (column 72-76) and - increase the size of the residue name to 4 residues. + + :param dialect: Specifies the particular dialect to use. If set, overrides + the value of :attr:`IOProfile.dialect` :type dialect: :class:`str` - :param strict_hydrogens: whether hydrogen names should be strictly checked. - It is very common for PDB files to not follow the correct naming - conventions for hydrogen atoms. That's why by default, the names of the - hydrogens are not required to be correct. Rather, the connectivity is - inferred with distance-based checks. By turning this flag on, the names - of the hydrogen atoms are checked against the names in the database like - all other atom types. + :param strict_hydrogens: If set, overrides the value of + :attr:`IOProfile.strict_hydrogens`. :raises: :exc:`~ost.io.IOException` if the import fails due to an erroneous or inexistent file """ - - if dialect not in ('PDB', 'CHARMM',): + def _override(val1, val2): + if val2!=None: + return val2 + else: + return val1 + if isinstance(profile, str): + prof=profiles[profile].Copy() + else: + prof=profile.Copy() + if dialect not in (None, 'PDB', 'CHARMM',): raise ValueError('dialect must be PDB or CHARMM') + prof.calpha_only=_override(prof.calpha_only, calpha_only) + prof.no_hetatms=_override(prof.no_hetatms, no_hetatms) + prof.dialect=_override(prof.dialect, dialect) + prof.quack_mode=_override(prof.quack_mode, quack_mode) + prof.strict_hydrogens=_override(prof.strict_hydrogens, strict_hydrogens) + prof.fault_tolerant=_override(prof.fault_tolerant, fault_tolerant) + prof.join_spread_atom_records=_override(prof.join_spread_atom_records, + join_spread_atom_records) if remote: output_dir = tempfile.gettempdir() @@ -107,25 +156,13 @@ def LoadPDB(filename, restrict_chains="", no_hetatms=False, conop_inst=conop.Conopology.Instance() builder=conop_inst.GetBuilder("DEFAULT") - if dialect=='PDB': + if prof.dialect=='PDB': builder.dialect=conop.PDB_DIALECT - elif dialect=='CHARMM': + elif prof.dialect=='CHARMM': builder.dialect=conop.CHARMM_DIALECT - builder.strict_hydrogens=strict_hydrogens - reader=PDBReader(filename) - flags=0 - if calpha_only: - flags|=PDB.CALPHA_ONLY - if fault_tolerant: - flags|=PDB.SKIP_FAULTY_RECORDS - if no_hetatms: - flags|=PDB.NO_HETATMS - if join_spread_atom_records: - flags|=PDB.JOIN_SPREAD_ATOM_RECORDS - if dialect=='CHARMM': - flags|=PDB.CHARMM_FORMAT + builder.strict_hydrogens=prof.strict_hydrogens + reader=PDBReader(filename, prof) try: - PDB.PushFlags(PDB.Flags() | flags) if load_multi: ent_list=[] while reader.HasNext(): @@ -135,7 +172,6 @@ def LoadPDB(filename, restrict_chains="", no_hetatms=False, ent_list.append(ent) if len(ent_list)==0: raise IOError("File doesn't contain any entities") - PDB.PopFlags() return ent_list else: ent=mol.CreateEntity() @@ -144,13 +180,11 @@ def LoadPDB(filename, restrict_chains="", no_hetatms=False, conop_inst.ConnectAll(builder, ent, 0) else: raise IOError("File doesn't contain any entities") - PDB.PopFlags() return ent except: - PDB.PopFlags() raise -def SavePDB(models, filename, dialect='PDB'): +def SavePDB(models, filename, dialect=None, pqr=False, profile='DEFAULT'): """ Save entity or list of entities to disk. If a list of entities is supplied the PDB file will be saved as a multi PDB file. Each of the entities is wrapped @@ -162,18 +196,18 @@ def SavePDB(models, filename, dialect='PDB'): """ if not getattr(models, '__len__', None): models=[models] - writer=PDBWriter(filename, dialect=='CHARMM') - try: - if len(models)>1: - PDB.PushFlags(PDB.Flags() |PDB.WRITE_MULTIPLE_MODELS) - else: - PDB.PushFlags(0) - for model in models: - writer.Write(model) - PDB.PopFlags() - except: - PDB.PopFlags() - raise + if isinstance(profile, str): + profile=profiles[profile].Copy() + else: + profile.Copy() + profile.dialect=_override(profile.dialect, dialect) + writer=PDBWriter(filename, profile) + writer.SetIsPQR(pqr) + if len(models)>1: + writer.SetMultiModel(True) + for model in models: + writer.Write(model) + try: from ost import img LoadMap = LoadImage @@ -192,8 +226,9 @@ def LoadImageList (files): LoadMapList=LoadImageList -def LoadCHARMMTraj(crd, dcd_file=None, lazy_load=False, stride=1, - dialect='CHARMM'): +def LoadCHARMMTraj(crd, dcd_file=None, profile='CHARMM', + lazy_load=False, stride=1, + dialect=None): """ Load CHARMM trajectory file. @@ -208,12 +243,15 @@ def LoadCHARMMTraj(crd, dcd_file=None, lazy_load=False, stride=1, :param stride: The spacing of the frames to load. When set to 2, for example, every second frame is loaded from the trajectory. By default, every frame is loaded. - :param dialect: The dialect for the PDB file to use. See :func:`LoadPDB`. + :param dialect: The dialect for the PDB file to use. See :func:`LoadPDB`. If + set, overrides the value of the profile + :param profile: The IO profile to use for loading the PDB file. See + :doc:`profile`. """ if not isinstance(crd, mol.EntityHandle): if dcd_file==None: dcd_file='%s.dcd' % os.path.splitext(crd)[0] - crd=LoadPDB(crd, dialect=dialect) + crd=LoadPDB(crd, profile=profile, dialect=dialect) else: if not dcd_file: diff --git a/modules/io/pymod/export_pdb_io.cc b/modules/io/pymod/export_pdb_io.cc index 78b9652f8d3e4f33ddf4c359aa99b56e2fedce23..c146557f914008b64d6052ef16238e53bb014795 100644 --- a/modules/io/pymod/export_pdb_io.cc +++ b/modules/io/pymod/export_pdb_io.cc @@ -19,6 +19,7 @@ #include <boost/python.hpp> using namespace boost::python; +#include <ost/io/mol/io_profile.hh> #include <ost/io/mol/pdb_reader.hh> #include <ost/io/mol/pdb_writer.hh> using namespace ost; @@ -34,32 +35,43 @@ void (PDBWriter::*write_b)(const mol::EntityView&)=&PDBWriter::Write; void export_pdb_io() { - { - scope pdb_scope=class_<PDB>("PDB",no_init) - .def("PushFlags",&PDB::PushFlags) - .staticmethod("PushFlags") - .def("Flags",&PDB::Flags) - .staticmethod("Flags") - .def("PopFlags",&PDB::PopFlags) - .staticmethod("PopFlags") - ; - pdb_scope.attr("NO_HETATMS")=PDB::NO_HETATMS; - pdb_scope.attr("SKIP_FAULTY_RECORDS")=PDB::SKIP_FAULTY_RECORDS; - pdb_scope.attr("WRITE_MULTIPLE_MODELS")=PDB::WRITE_MULTIPLE_MODELS; - pdb_scope.attr("JOIN_SPREAD_ATOM_RECORDS")=PDB::JOIN_SPREAD_ATOM_RECORDS; - pdb_scope.attr("CALPHA_ONLY")=PDB::CALPHA_ONLY; - pdb_scope.attr("CHARMM_FORMAT")=PDB::CHARMM_FORMAT; - } - - class_<PDBReader, boost::noncopyable>("PDBReader", init<String>()) + class_<IOProfile>("IOProfile", + init<String,bool,bool,bool,bool,bool,bool>((arg("dialect")="PDB", + arg("strict_hydrogens")=false, + arg("quack_mode")=false, + arg("fault_tolerant")=false, + arg("join_spread_atom_records")=false, + arg("no_hetatms")=false, + arg("calpha_only")=false))) + .def_readwrite("dialect", &IOProfile::dialect) + .def_readwrite("fault_tolerant", &IOProfile::fault_tolerant) + .def_readwrite("quack_mode", &IOProfile::quack_mode) + .def_readwrite("strict_hydrogens", &IOProfile::strict_hydrogens) + .def_readwrite("no_hetatms", &IOProfile::no_hetatms) + .def_readwrite("calpha_only", &IOProfile::calpha_only) + .def_readwrite("join_spread_atom_records", &IOProfile::join_spread_atom_records) + .def("Copy", &IOProfile::Copy) + .def(self_ns::str(self)) + ; + class_<IOProfileRegistry>("IOProfileRegistry", no_init) + .def("Get", &IOProfileRegistry::Get, + return_value_policy<reference_existing_object>()) + .def("Set", &IOProfileRegistry::Set) + .def("Instance", &IOProfileRegistry::Instance, + return_value_policy<reference_existing_object>()).staticmethod("Instance") + .def("GetDefault", &IOProfileRegistry::GetDefault, + return_value_policy<reference_existing_object>()) + ; + class_<PDBReader, boost::noncopyable>("PDBReader", init<String, const IOProfile&>()) .def("HasNext", &PDBReader::HasNext) .def("Import", &PDBReader::Import, X_import(args("entity", "restrict_chains"))) ; - class_<PDBWriter, boost::noncopyable>("PDBWriter", init<String>()) - .def(init<String, bool>()) + class_<PDBWriter, boost::noncopyable>("PDBWriter", init<String, const IOProfile&>()) .def("Write", write_a) + .def("SetIsPQR", &PDBWriter::SetIsPQR) + .def("IsPQR", &PDBWriter::IsPQR) .def("Write", write_b) ; } diff --git a/modules/io/src/mol/CMakeLists.txt b/modules/io/src/mol/CMakeLists.txt index 8498858451420988e3a4132c6df570b30e35eff0..1201eb58fcff984f0bf0b5887a29f011ed6fd7ac 100644 --- a/modules/io/src/mol/CMakeLists.txt +++ b/modules/io/src/mol/CMakeLists.txt @@ -3,7 +3,6 @@ entity_io_crd_handler.cc entity_io_mae_handler.cc pdb_reader.cc entity_io_pdb_handler.cc -pdb_io.cc pdb_writer.cc entity_io_sdf_handler.cc sdf_reader.cc @@ -13,6 +12,7 @@ load_entity.cc surface_io_msms_handler.cc load_surface.cc chemdict_parser.cc +io_profile.cc dcd_io.cc star_parser.cc PARENT_SCOPE @@ -21,21 +21,21 @@ PARENT_SCOPE set(OST_IO_MOL_HEADERS chemdict_parser.hh star_parser.hh +io_profile.hh dcd_io.hh -entity_io_crd_handler.hh -entity_io_mae_handler.hh -pdb_io.hh -entity_io_handler.hh +entity_io_crd_handler.hh +entity_io_mae_handler.hh +entity_io_handler.hh pdb_reader.hh -entity_io_pdb_handler.hh +entity_io_pdb_handler.hh pdb_writer.hh -entity_io_sdf_handler.hh +entity_io_sdf_handler.hh sdf_reader.hh sdf_writer.hh save_entity.hh -load_entity.hh +load_entity.hh surface_io_handler.hh -load_surface.hh +load_surface.hh surface_io_msms_handler.hh PARENT_SCOPE ) diff --git a/modules/io/src/mol/dcd_io.cc b/modules/io/src/mol/dcd_io.cc index 15e056daff29fbf3f294a8a4f8f809e80b748b6a..9a59b2dbb62317d4e3a5b67e41a8c38b2913cb70 100644 --- a/modules/io/src/mol/dcd_io.cc +++ b/modules/io/src/mol/dcd_io.cc @@ -376,11 +376,11 @@ void write_dcd_hdr(std::ofstream& out, void SaveCHARMMTraj(const mol::CoordGroupHandle& coord_group, const String& pdb_filename, const String& dcd_filename, - unsigned int stepsize) + unsigned int stepsize, const IOProfile& profile) { if(stepsize==0) stepsize=1; if(!pdb_filename.empty()) { - PDBWriter writer(pdb_filename, true); + PDBWriter writer(pdb_filename, profile); writer.Write(coord_group.GetAtomList()); } std::ofstream out(dcd_filename.c_str(), std::ios::binary); diff --git a/modules/io/src/mol/dcd_io.hh b/modules/io/src/mol/dcd_io.hh index 24d189e634e5f7ec0289eb09680f6debf039a45a..217da0bf7ac82b81644ee0ebf53c22cda0f97ae9 100644 --- a/modules/io/src/mol/dcd_io.hh +++ b/modules/io/src/mol/dcd_io.hh @@ -26,9 +26,11 @@ #include <ost/io/module_config.hh> #include <ost/mol/coord_group.hh> - +#include <ost/io/mol/io_profile.hh> namespace ost { namespace io { + + /*! \brief import a CHARMM trajectory in dcd format with an existing entity requires the existing entity and the trajectory file - obviously the atom layout of the entity must match the trajectory file @@ -46,7 +48,8 @@ mol::CoordGroupHandle DLLEXPORT_OST_IO LoadCHARMMTraj(const mol::EntityHandle& e void DLLEXPORT_OST_IO SaveCHARMMTraj(const mol::CoordGroupHandle& coord_group, const String& pdb_filename, const String& dcd_filename, - unsigned int stride=1); + unsigned int stride=1, + const IOProfile& profile=IOProfile()); }} // ns diff --git a/modules/io/src/mol/entity_io_pdb_handler.cc b/modules/io/src/mol/entity_io_pdb_handler.cc index 99b8ea49560a7dbe9ef2ca79ab9dbbc855c94dbe..bd4fe5ee25a84f39af2595eb46c268065757374f 100644 --- a/modules/io/src/mol/entity_io_pdb_handler.cc +++ b/modules/io/src/mol/entity_io_pdb_handler.cc @@ -48,20 +48,17 @@ bool EntityIOPDBHandler::RequiresBuilder() const void EntityIOPDBHandler::Export(const mol::EntityView& ent, const boost::filesystem::path& loc) const { - PDBWriter writer(loc); + PDBWriter writer(loc, IOProfileRegistry::Instance().GetDefault()); if (boost::iequals(boost::filesystem::extension(loc), ".pqr")) { - PDB::PushFlags(PDB::Flags() | PDB::PQR_FORMAT); - writer.Write(ent); - PDB::PopFlags(); - } else { - writer.Write(ent); + writer.SetIsPQR(true); } + writer.Write(ent); } void EntityIOPDBHandler::Import(mol::EntityHandle& ent, std::istream& stream) { - PDBReader reader(stream); + PDBReader reader(stream, IOProfileRegistry::Instance().GetDefault()); if (reader.HasNext()) { reader.Import(ent, ""); } @@ -70,7 +67,7 @@ void EntityIOPDBHandler::Import(mol::EntityHandle& ent, void EntityIOPDBHandler::Export(const mol::EntityView& ent, std::ostream& stream) const { - PDBWriter writer(stream); + PDBWriter writer(stream, IOProfileRegistry::Instance().GetDefault()); writer.Write(ent); } @@ -99,7 +96,7 @@ bool pdb_handler_is_responsible_for(const boost::filesystem::path& loc, void EntityIOPDBHandler::Import(mol::EntityHandle& ent, const boost::filesystem::path& loc) { - PDBReader reader(loc); + PDBReader reader(loc, IOProfileRegistry::Instance().GetDefault()); if (reader.HasNext()) { reader.Import(ent, ""); } diff --git a/modules/io/src/mol/io_profile.cc b/modules/io/src/mol/io_profile.cc new file mode 100644 index 0000000000000000000000000000000000000000..d27f3918f055cc12b754c4b9e84c1a80efaaacc4 --- /dev/null +++ b/modules/io/src/mol/io_profile.cc @@ -0,0 +1,16 @@ +#include "io_profile.hh" + +namespace ost { namespace io { + +IOProfileRegistry& IOProfileRegistry::Instance() +{ + static IOProfileRegistry reg; + return reg; +} + +IOProfileRegistry::IOProfileRegistry() +{ + profiles_["DEFAULT"]=IOProfile(); +} + +}} diff --git a/modules/io/src/mol/io_profile.hh b/modules/io/src/mol/io_profile.hh new file mode 100644 index 0000000000000000000000000000000000000000..0fe9ffc2f7fbda8e76d5364b79f68c7e4574c226 --- /dev/null +++ b/modules/io/src/mol/io_profile.hh @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2010 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ +#ifndef OST_IO_IO_PROFILE_HH +#define OST_IO_IO_PROFILE_HH + +#include <iostream> +#include <map> +#include <ost/mol/entity_handle.hh> +#include <ost/io/module_config.hh> +namespace ost { namespace io { + +struct DLLEXPORT IOProfile { +public: + IOProfile(String d, bool sh, bool qm, bool ft, bool js, bool nh, bool co): + dialect(d), strict_hydrogens(sh), quack_mode(qm), fault_tolerant(ft), + join_spread_atom_records(js), no_hetatms(nh), calpha_only(co) + { } + IOProfile(): dialect("PDB"), strict_hydrogens(true), quack_mode(false), + fault_tolerant(false), join_spread_atom_records(false), no_hetatms(false), + calpha_only(false) + { } + virtual ~IOProfile() { } + + String dialect; + bool strict_hydrogens; + bool quack_mode; + bool fault_tolerant; + bool join_spread_atom_records; + bool no_hetatms; + bool calpha_only; + + IOProfile Copy() + { + return IOProfile(dialect, strict_hydrogens, quack_mode, fault_tolerant, + join_spread_atom_records, no_hetatms, calpha_only); + } + virtual void PostImport(mol::EntityHandle ent) { } +}; + +inline std::ostream& operator<<(std::ostream& stream, const IOProfile& p) +{ + stream << "IOProfile(dialect='" << p.dialect << "', strict_hydrogens=" + << (p.strict_hydrogens ? "True" : "False") << ", quack_mode=" + << (p.quack_mode ? "True" : "False") << ", join_spread_atom_records=" + << (p.join_spread_atom_records ? "True" : "False") << ", no_hetatms=" + << (p.no_hetatms ? "True" : "False") << ", calpha_only=" + << (p.calpha_only ? "True" : "False") << ", fault_tolerant=" + << (p.fault_tolerant ? "True" : "False") << ")"; + return stream; +} + +class DLLEXPORT_OST_IO IOProfileRegistry { +public: + static IOProfileRegistry& Instance(); + + IOProfile& Get(const String& key) + { + return profiles_[key]; + } + + void Set(const String& key, const IOProfile& profile) + { + profiles_[key]=profile; + } + + IOProfile& GetDefault() { return profiles_["DEFAULT"]; } +private: + IOProfileRegistry(); + std::map<String, IOProfile> profiles_; +}; + +}} + +#endif diff --git a/modules/io/src/mol/pdb_io.cc b/modules/io/src/mol/pdb_io.cc deleted file mode 100644 index eba9c8877083eaea23bbb1ad81269269fe8d6d96..0000000000000000000000000000000000000000 --- a/modules/io/src/mol/pdb_io.cc +++ /dev/null @@ -1,41 +0,0 @@ -//------------------------------------------------------------------------------ -// This file is part of the OpenStructure project <www.openstructure.org> -// -// Copyright (C) 2008-2010 by the OpenStructure authors -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License as published by the Free -// Software Foundation; either version 3.0 of the License, or (at your option) -// any later version. -// This library is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -// details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this library; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -//------------------------------------------------------------------------------ -#include "pdb_io.hh" - -namespace ost { namespace io { - - PDB& PDB::Instance() { - static PDB x; - return x; - } - - const unsigned int PDB::SKIP_FAULTY_RECORDS=0x1; - const unsigned int PDB::NO_HETATMS=0x2; - const unsigned int PDB::WRITE_MULTIPLE_MODELS=0x4; - const unsigned int PDB::PQR_FORMAT=0x8; - const unsigned int PDB::JOIN_SPREAD_ATOM_RECORDS=0x10; - const unsigned int PDB::CALPHA_ONLY=0x20; - const unsigned int PDB::CHARMM_FORMAT=0x40; - - void PDB::PushFlags(unsigned int flags) {PDB::Instance().fstack.push(flags);} - unsigned int PDB::Flags() {return PDB::Instance().fstack.empty() ? 0 : PDB::Instance().fstack.top();} - void PDB::PopFlags() {PDB::Instance().fstack.pop();} - - -}} // ns diff --git a/modules/io/src/mol/pdb_io.hh b/modules/io/src/mol/pdb_io.hh deleted file mode 100644 index c4fc19e035f2b6bf02d135facf06091cb1c38250..0000000000000000000000000000000000000000 --- a/modules/io/src/mol/pdb_io.hh +++ /dev/null @@ -1,92 +0,0 @@ -//------------------------------------------------------------------------------ -// This file is part of the OpenStructure project <www.openstructure.org> -// -// Copyright (C) 2008-2010 by the OpenStructure authors -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License as published by the Free -// Software Foundation; either version 3.0 of the License, or (at your option) -// any later version. -// This library is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -// details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this library; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -//------------------------------------------------------------------------------ -#ifndef OST_IO_PDB_IO_HH -#define OST_IO_PDB_IO_HH - -#include <stack> -#include <ost/io/module_config.hh> - -namespace ost { namespace io { - - -/// \brief flags that incluence the behaviour of the PDBReader and PDBWriter -struct DLLEXPORT_OST_IO PDB { - - /// \brief skip faulty records - /// This flag tells the PDB loader to ignore faulty records. By default, - /// faulty records abort import. - static const unsigned int SKIP_FAULTY_RECORDS; - - /// \brief do not import HETATM records - static const unsigned int NO_HETATMS; - - /// \brief enable writing of multiple models - static const unsigned int WRITE_MULTIPLE_MODELS; - - /// \brief enable for PQR - static const unsigned int PQR_FORMAT; - - /// \brief enables parsing of charmm-style PDBs - /// - /// CHARMM files don't use the chain column to mark different chains, but - /// rather put the name in the last columns that is isually used for the atom - /// element an occupancy. Note that it is perfectly possible to parse CHARRM - /// PDB files without this flag, but the mileage may vary as some of the - /// elements are incorrectly assigned. - static const unsigned int CHARMM_FORMAT; - - /// \brief Join atom records into one residue, even if the atom records - /// are not sequential. - /// - /// This is useful in the following case: - /// - /// \verbatim - /// ATOM 43 N AALA P 4 - /// ATOM 45 CA AALA P 4 - /// ATOM 47 C AALA P 4 - /// ATOM 48 O AALA P 4 - /// ATOM 49 N APRO P 5 - /// ATOM 50 CD APRO P 5 - /// ATOM 53 CA APRO P 5 - /// ATOM 55 CB APRO P 5 - /// ATOM 58 CG APRO P 5 - /// ATOM 61 C APRO P 5 - /// ATOM 62 O APRO P 5 - /// ATOM 550 CB AALA P 4 - /// \endverbatim - /// - /// By default, the atom 550 will start a new residue instead of being - /// joined with atoms 43-48 into one residue. - static const unsigned int JOIN_SPREAD_ATOM_RECORDS; - - /// \brief only import C-alpha atoms - static const unsigned int CALPHA_ONLY; - - static void PushFlags(unsigned int flags); - static unsigned int Flags(); - static void PopFlags(); - - static PDB& Instance(); - - std::stack<unsigned int> fstack; -}; - -}} - -#endif diff --git a/modules/io/src/mol/pdb_reader.cc b/modules/io/src/mol/pdb_reader.cc index 6712019c326bba5beceb15318f51170d29d584c3..942aa17b1be2ff43fb997d7c96b37579ccd056f5 100644 --- a/modules/io/src/mol/pdb_reader.cc +++ b/modules/io/src/mol/pdb_reader.cc @@ -61,26 +61,28 @@ mol::ResNum to_res_num(int num, char ins_code) } -PDBReader::PDBReader(std::istream& instream): - infile_(), instream_(instream) +PDBReader::PDBReader(std::istream& instream, const IOProfile& profile): + infile_(), instream_(instream), profile_(profile) { this->Init(boost::filesystem::path("")); } -PDBReader::PDBReader(const String& filename) - : infile_(filename), instream_(infile_) +PDBReader::PDBReader(const String& filename, const IOProfile& profile) + : infile_(filename), instream_(infile_), profile_(profile) { this->Init(boost::filesystem::path(filename)); } -PDBReader::PDBReader(const boost::filesystem::path& loc): - infile_(loc), instream_(infile_) +PDBReader::PDBReader(const boost::filesystem::path& loc, + const IOProfile& profile): + infile_(loc), instream_(infile_), profile_(profile) { this->Init(loc); } void PDBReader::Init(const boost::filesystem::path& loc) { + charmm_style_=profile_.dialect=="CHARMM"; num_model_records_=0; if (boost::iequals(".gz", boost::filesystem::extension(loc))) { in_.push(boost::iostreams::gzip_decompressor()); @@ -108,7 +110,7 @@ bool PDBReader::HasNext() StringRef curr_line(curr_line_.c_str(), curr_line_.length()); if (curr_line.size()>5 && (IEquals(curr_line.substr(0, 6), StringRef("ATOM ", 6)) || - (!(PDB::Flags() & PDB::NO_HETATMS) && + (!profile_.no_hetatms && IEquals(curr_line.substr(0, 6),StringRef("HETATM ", 6))) || IEquals(curr_line.substr(0, 6),StringRef("ANISOU ", 6)) || IEquals(curr_line.substr(0, 6), StringRef("SHEET ", 6)) || @@ -127,7 +129,7 @@ bool PDBReader::HasNext() void PDBReader::Import(mol::EntityHandle& ent, const String& restrict_chains) { - LOG_DEBUG("PDBReader: current flags: " << PDB::Flags()); + LOG_DEBUG("PDBReader: " << profile_); Profile profile_import("PDBReader::Import"); this->ClearState(); @@ -150,7 +152,7 @@ void PDBReader::Import(mol::EntityHandle& ent, this->ParseAndAddAtom(curr_line, line_num_, ent, StringRef("ATOM", 4)); } else if (IEquals(curr_line.substr(0, 6), StringRef("ANISOU", 6))) { - if (!(PDB::Flags() & PDB::CHARMM_FORMAT)) { + if (!charmm_style_) { LOG_TRACE("processing ANISOU entry"); this->ParseAnisou(curr_line, line_num_, ent); } @@ -180,13 +182,13 @@ void PDBReader::Import(mol::EntityHandle& ent, continue; } if (IEquals(curr_line.substr(0, 6), StringRef("HETATM", 6))) { - if (PDB::Flags() & PDB::NO_HETATMS) + if (profile_.no_hetatms) continue; LOG_TRACE("processing HETATM entry"); this->ParseAndAddAtom(curr_line, line_num_, ent, StringRef("HETATM", 6)); } else if (IEquals(curr_line.substr(0, 6), StringRef("HELIX ", 6))) { - if (!(PDB::Flags() & PDB::CHARMM_FORMAT)) { + if (!charmm_style_) { this->ParseHelixEntry(curr_line); } } else if (IEquals(curr_line.substr(0, 6), StringRef("HET ", 6))) { @@ -194,7 +196,7 @@ void PDBReader::Import(mol::EntityHandle& ent, char chain=curr_line[12]; std::pair<bool, int> num=curr_line.substr(13, 4).ltrim().to_int(); if (!num.first) { - if (PDB::Flags() && PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { LOG_WARNING("Invalid HET entry on line " << line_num_); continue; } else { @@ -216,7 +218,7 @@ void PDBReader::Import(mol::EntityHandle& ent, if (num_model_records_<2) { continue; } - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { go_on=false; num_model_records_=1; break; @@ -231,7 +233,7 @@ void PDBReader::Import(mol::EntityHandle& ent, continue; } if (IEquals(curr_line.substr(0, 6), StringRef("SHEET ", 6))) { - if (!(PDB::Flags() & PDB::CHARMM_FORMAT)) { + if (!charmm_style_) { this->ParseStrandEntry(curr_line); } } @@ -313,7 +315,7 @@ void PDBReader::ClearState() bool PDBReader::EnsureLineLength(const StringRef& line, size_t size) { if (line.length()<size) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { return false; } throw IOException(str(format("premature end of line %d") %line_num_)); @@ -330,12 +332,12 @@ bool PDBReader::ParseAtomIdent(const StringRef& line, int line_num, return false; } atom_name=line.substr(12, 4).trim(); - if (PDB::Flags() & PDB::CALPHA_ONLY) { + if (profile_.calpha_only) { if (atom_name!=StringRef("CA", 2)) { return false; } } - if (PDB::Flags() & PDB::CHARMM_FORMAT) { + if (charmm_style_) { if (line.size()>73) { size_t width=std::min(line.size()-72, size_t(4)); chain_name=line.substr(72, width).trim().str(); @@ -350,17 +352,17 @@ bool PDBReader::ParseAtomIdent(const StringRef& line, int line_num, std::pair<bool, int> a_num=line.substr(6, 5).ltrim().to_int(); if (!a_num.first) { - if (!(PDB::Flags() & PDB::SKIP_FAULTY_RECORDS)) { + if (!(profile_.fault_tolerant)) { throw IOException(str(format("invalid atom number on line %d") %line_num)); } LOG_WARNING("invalid atom number on line " << line_num); } alt_loc=line[16]; - res_name=line.substr(17, (PDB::Flags() & PDB::CHARMM_FORMAT) ? 4 : 3).trim(); + res_name=line.substr(17, charmm_style_ ? 4 : 3).trim(); std::pair<bool, int> res_num=line.substr(22, 4).ltrim().to_int();; if (!res_num.first) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { return false; } throw IOException(str(format("invalid res number on line %d") % line_num)); @@ -389,7 +391,7 @@ void PDBReader::ParseAnisou(const StringRef& line, int line_num, for (int i=0;i<6; ++i) { std::pair<bool, int> result=line.substr(29+i*7, 6).ltrim().to_int(); if (!result.first) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { return; } throw IOException(str(format("invalid ANISOU record on line %d")%line_num)); @@ -398,8 +400,7 @@ void PDBReader::ParseAnisou(const StringRef& line, int line_num, } String aname(atom_name.str()); if (!curr_residue_.IsValid()) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS || - PDB::Flags() & PDB::CALPHA_ONLY) { + if (profile_.fault_tolerant || profile_.calpha_only) { return; } const char* fmt_str="invalid ANISOU record for inexistent atom on line %d"; @@ -407,8 +408,7 @@ void PDBReader::ParseAnisou(const StringRef& line, int line_num, } mol::AtomHandle atom=curr_residue_.FindAtom(aname); if (!atom.IsValid()) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS || - PDB::Flags() & PDB::CALPHA_ONLY) { + if (profile_.fault_tolerant || profile_.calpha_only) { return; } const char* fmt_str="invalid ANISOU record for inexistent atom on line %d"; @@ -447,7 +447,7 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, for (int i=0;i<3; ++i) { std::pair<bool, float> result=line.substr(30+i*8, 8).ltrim().to_float(); if (!result.first) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { return; } throw IOException(str(format("invalid coordinate on line %d")%line_num)); @@ -515,15 +515,7 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, } if(update_chain) { - curr_chain_=mol::ChainHandle(); -#if 0 - // TODO: should this depend on JOIN_SPREAD as well? - if (PDB::Flags() & PDB::JOIN_SPREAD_ATOM_RECORDS) { - curr_chain_=ent.FindChain(s_chain); - } -#else - curr_chain_=ent.FindChain(chain_name); -#endif + curr_chain_=ent.FindChain(chain_name); if(!curr_chain_.IsValid()) { LOG_DEBUG("new chain " << chain_name); curr_chain_=editor.InsertChain(chain_name); @@ -533,7 +525,7 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, } if(update_residue) { curr_residue_=mol::ResidueHandle(); - if (PDB::Flags() & PDB::JOIN_SPREAD_ATOM_RECORDS) { + if (profile_.join_spread_atom_records) { curr_residue_=curr_chain_.FindResidue(res_num); } if (!curr_residue_.IsValid()) { @@ -592,7 +584,7 @@ void PDBReader::ParseHelixEntry(const StringRef& line) std::pair<bool, int> start_num=line.substr(21, 4).ltrim().to_int(); std::pair<bool, int> end_num=line.substr(33, 4).ltrim().to_int(); if (!start_num.first || !end_num.first) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { return; } throw IOException(str(format("invalid helix entry on line %d") % line_num_)); @@ -617,7 +609,7 @@ void PDBReader::ParseStrandEntry(const StringRef& line) std::pair<bool, int> start_num=line.substr(22, 4).ltrim().to_int(); std::pair<bool, int> end_num=line.substr(33, 4).ltrim().to_int(); if (!start_num.first || !end_num.first) { - if (PDB::Flags() & PDB::SKIP_FAULTY_RECORDS) { + if (profile_.fault_tolerant) { return; } throw IOException(str(format("invalid strand entry on line %d")%line_num_)); diff --git a/modules/io/src/mol/pdb_reader.hh b/modules/io/src/mol/pdb_reader.hh index f18258a73b03c3dfb6ea2b92ebace31154d89075..71ecde7edc41fb5218a18125394e435d003a94eb 100644 --- a/modules/io/src/mol/pdb_reader.hh +++ b/modules/io/src/mol/pdb_reader.hh @@ -28,8 +28,7 @@ #include <ost/mol/mol.hh> #include <ost/mol/xcs_editor.hh> #include <ost/io/module_config.hh> -#include <ost/io/mol/pdb_io.hh> - +#include <ost/io/mol/io_profile.hh> namespace ost { namespace io { class DLLEXPORT_OST_IO PDBReader { @@ -46,9 +45,9 @@ class DLLEXPORT_OST_IO PDBReader { typedef std::vector<HSEntry> HSList; typedef std::vector<HetEntry> HetList; public: - PDBReader(const String& filename); - PDBReader(const boost::filesystem::path& loc); - PDBReader(std::istream& instream); + PDBReader(const String& filename, const IOProfile& profile); + PDBReader(const boost::filesystem::path& loc, const IOProfile& profile); + PDBReader(std::istream& instream, const IOProfile& profile); bool HasNext(); @@ -92,6 +91,8 @@ private: // file (i.e. pdb formatted file with charges in occupacy // column, and radii in b-factor column) bool is_pqr_; + IOProfile profile_; + bool charmm_style_; }; }} diff --git a/modules/io/src/mol/pdb_writer.cc b/modules/io/src/mol/pdb_writer.cc index e38545430ec656b1b8216b428684ac7c4d72a198..bcdd204ba09c8ee1bff9d1abe46da1503bef25f0 100644 --- a/modules/io/src/mol/pdb_writer.cc +++ b/modules/io/src/mol/pdb_writer.cc @@ -319,37 +319,41 @@ struct ForcePOSIX { } -PDBWriter::PDBWriter(std::ostream& stream, bool charmm_style): - outfile_(), outstream_(stream), mol_count_(0), line_(80), - charmm_style_(charmm_style) +PDBWriter::PDBWriter(std::ostream& stream, const IOProfile& profile): + outfile_(), outstream_(stream), mol_count_(0), line_(80), + multi_model_(false), charmm_style_(profile.dialect=="CHARMM"), is_pqr_(false), + profile_(profile) { } -PDBWriter::PDBWriter(const boost::filesystem::path& filename, bool charmm_style): +PDBWriter::PDBWriter(const boost::filesystem::path& filename, + const IOProfile& profile): outfile_(filename.file_string().c_str()), outstream_(outfile_), - mol_count_(0), line_(80), charmm_style_(charmm_style) + mol_count_(0), line_(80), multi_model_(false), + charmm_style_(profile.dialect=="CHARMM"), is_pqr_(false), + profile_(profile) {} -PDBWriter::PDBWriter(const String& filename, bool charmm_style): - outfile_(filename.c_str()), outstream_(outfile_), mol_count_(0), line_(80), - charmm_style_(charmm_style) +PDBWriter::PDBWriter(const String& filename, const IOProfile& profile): + outfile_(filename.c_str()), outstream_(outfile_), mol_count_(0), line_(80), + multi_model_(false), charmm_style_(profile.dialect=="CHARMM"), + is_pqr_(false), profile_(profile) {} void PDBWriter::WriteModelLeader() { ++mol_count_; - if (PDB::Flags() & PDB::WRITE_MULTIPLE_MODELS) { + if (multi_model_) { outstream_ << "MODEL " << mol_count_ << std::endl; } else if (mol_count_>1) { - throw IOException("Please enable the PDB::WRITE_MULTIPLE_MODELS flag to " - "write multiple models"); + throw IOException("Trying to write several models into one file with "); } } void PDBWriter::WriteModelTrailer() { - if (PDB::Flags() & PDB::WRITE_MULTIPLE_MODELS) { + if (multi_model_) { outstream_ << "ENDMDL" << std::endl; } } @@ -357,12 +361,10 @@ void PDBWriter::WriteModelTrailer() template <typename H> void PDBWriter::WriteModel(H ent) { - ForcePOSIX posix = ForcePOSIX(); + ForcePOSIX posix; this->WriteModelLeader(); PDBWriterImpl writer(outstream_,line_, atom_indices_, charmm_style_); - if (PDB::Flags() & PDB::PQR_FORMAT) { - writer.SetIsPQR(true); - } + writer.SetIsPQR(is_pqr_); ent.Apply(writer); PDBConectWriterImpl con_writer(outstream_,atom_indices_); ent.Apply(con_writer); @@ -387,8 +389,7 @@ void PDBWriter::Write(const mol::AtomHandleList& atoms) mol::ChainHandle last_chain; for (mol::AtomHandleList::const_iterator i=atoms.begin(), e=atoms.end(); i!=e; ++i, ++counter) { - write_atom(outstream_, line_, *i, counter, - (PDB::Flags() & PDB::PQR_FORMAT) != 0, charmm_style_); + write_atom(outstream_, line_, *i, counter, is_pqr_, charmm_style_); } this->WriteModelTrailer(); } diff --git a/modules/io/src/mol/pdb_writer.hh b/modules/io/src/mol/pdb_writer.hh index c8965721507173a49598e0a3f97e7e5639929a73..2ec823fcbd57ce76c9c83e9604f8c582063a18e2 100644 --- a/modules/io/src/mol/pdb_writer.hh +++ b/modules/io/src/mol/pdb_writer.hh @@ -32,18 +32,21 @@ #include <ost/io/module_config.hh> #include <ost/io/formatted_line.hh> - - -#include "pdb_io.hh" +#include <ost/io/mol/io_profile.hh> namespace ost { namespace io { class DLLEXPORT_OST_IO PDBWriter { public: - PDBWriter(const String& filename, bool charmm_style=false); - PDBWriter(const boost::filesystem::path& filename, bool charmm_style=false); - PDBWriter(std::ostream& outstream, bool charmm_style=false); - + PDBWriter(const String& filename, + const IOProfile& profile); + PDBWriter(const boost::filesystem::path& filename, + const IOProfile& profile); + PDBWriter(std::ostream& outstream, const IOProfile& profile); + void SetWriteMultiModel(bool flag) { multi_model_=flag; } + bool GetWriteMultiModel() const { return multi_model_; } + void SetIsPQR(bool flag) { is_pqr_=flag; } + bool IsPQR() const { return is_pqr_; } void Write(const mol::EntityView& ent); void Write(const mol::EntityHandle& ent); @@ -61,7 +64,10 @@ private: int mol_count_; std::map<long, int> atom_indices_; FormattedLine line_; + bool multi_model_; bool charmm_style_; + bool is_pqr_; + IOProfile profile_; }; }} diff --git a/modules/io/tests/test_io_pdb.cc b/modules/io/tests/test_io_pdb.cc index 5f8f6598137f6f4a1d451c67a894cf40a44cb370..139b109664716ce30db6b4496d43d0f531c35e7a 100644 --- a/modules/io/tests/test_io_pdb.cc +++ b/modules/io/tests/test_io_pdb.cc @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(test_pdb_import_handler) BOOST_AUTO_TEST_CASE(atom_record) { String fname("testfiles/pdb/atom.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_REQUIRE_EQUAL(ent.GetChainCount(), 2); @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(atom_record) BOOST_AUTO_TEST_CASE(end_record) { String fname("testfiles/pdb/end.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_CHECK_EQUAL(ent.GetAtomCount(), 1); @@ -96,12 +96,13 @@ BOOST_AUTO_TEST_CASE(end_record) BOOST_AUTO_TEST_CASE(join_spread_records_on) { String fname("testfiles/pdb/join-spread-records.pdb"); - PDBReader reader(fname); + IOProfile profile; + profile.join_spread_atom_records=true; + PDBReader reader(fname, profile); mol::EntityHandle ent=mol::CreateEntity(); - PDB::PushFlags(PDB::JOIN_SPREAD_ATOM_RECORDS); + reader.Import(ent); - PDB::PopFlags(); BOOST_CHECK_EQUAL(ent.GetResidueCount(), 2); mol::ResidueHandle res1=ent.FindResidue("A", mol::ResNum(1)); BOOST_CHECK(res1.IsValid()); @@ -117,7 +118,7 @@ BOOST_AUTO_TEST_CASE(join_spread_records_on) BOOST_AUTO_TEST_CASE(join_spread_records_off) { String fname("testfiles/pdb/join-spread-records.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_CHECK_EQUAL(ent.GetResidueCount(), 3); @@ -134,11 +135,11 @@ BOOST_AUTO_TEST_CASE(join_spread_records_off) BOOST_AUTO_TEST_CASE(calpha_only_import_on) { String fname("testfiles/pdb/calpha.pdb"); - PDBReader reader(fname); - PDB::PushFlags(PDB::CALPHA_ONLY); + IOProfile profile; + profile.calpha_only=true; + PDBReader reader(fname, profile); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); - PDB::PopFlags(); BOOST_CHECK_EQUAL(ent.GetResidueCount(), 2); BOOST_CHECK_EQUAL(ent.GetAtomCount(), 2); } @@ -146,7 +147,7 @@ BOOST_AUTO_TEST_CASE(calpha_only_import_on) BOOST_AUTO_TEST_CASE(het_import) { String fname("testfiles/pdb/het.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_CHECK_EQUAL(ent.Select("ligand=true").GetResidueCount(), 1); @@ -155,7 +156,7 @@ BOOST_AUTO_TEST_CASE(het_import) BOOST_AUTO_TEST_CASE(calpha_only_import_off) { String fname("testfiles/pdb/calpha.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_CHECK_EQUAL(ent.GetResidueCount(), 2); @@ -165,7 +166,7 @@ BOOST_AUTO_TEST_CASE(calpha_only_import_off) BOOST_AUTO_TEST_CASE(anisou_record) { String fname("testfiles/pdb/anisou.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_REQUIRE(ent.GetAtomCount()==1); @@ -186,7 +187,7 @@ BOOST_AUTO_TEST_CASE(anisou_record) BOOST_AUTO_TEST_CASE(only_66_cols) { String fname("testfiles/pdb/short.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); } @@ -194,7 +195,7 @@ BOOST_AUTO_TEST_CASE(only_66_cols) BOOST_AUTO_TEST_CASE(no_endmdl_record) { String fname("testfiles/pdb/model.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); BOOST_CHECK_THROW(reader.Import(ent), IOException); } @@ -202,7 +203,7 @@ BOOST_AUTO_TEST_CASE(no_endmdl_record) BOOST_AUTO_TEST_CASE(deuterium_import) { String fname("testfiles/pdb/val-with-deuterium.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); // we use conopology to mark amino acids as peptide-linking. @@ -216,18 +217,19 @@ BOOST_AUTO_TEST_CASE(deuterium_import) BOOST_AUTO_TEST_CASE(faulty_lines) { String fname("testfiles/pdb/faulty.pdb"); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); BOOST_CHECK_THROW(reader.Import(ent), IOException); - - PDB::PushFlags(PDB::SKIP_FAULTY_RECORDS); - reader.Import(ent); + IOProfile profile; + profile.fault_tolerant=true; + PDBReader reader2(fname, profile); + reader2.Import(ent); } BOOST_AUTO_TEST_CASE(write_atom) { std::stringstream out; - PDBWriter writer(out); + PDBWriter writer(out, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); mol::XCSEditor edi=ent.EditXCS(); @@ -248,7 +250,7 @@ BOOST_AUTO_TEST_CASE(write_atom) BOOST_AUTO_TEST_CASE(write_hetatom) { std::stringstream out; - PDBWriter writer(out); + PDBWriter writer(out, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); mol::XCSEditor edi=ent.EditXCS(); @@ -271,8 +273,9 @@ BOOST_AUTO_TEST_CASE(write_hetatom) BOOST_AUTO_TEST_CASE(no_endmdl_record_fault_tolerant) { String fname("testfiles/pdb/model.pdb"); - PDBReader reader(fname); - PDB::PushFlags(PDB::SKIP_FAULTY_RECORDS); + IOProfile profile; + profile.fault_tolerant=true; + PDBReader reader(fname, profile); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); BOOST_CHECK_EQUAL(ent.GetChainCount(), 1); @@ -283,7 +286,6 @@ BOOST_AUTO_TEST_CASE(no_endmdl_record_fault_tolerant) BOOST_CHECK_EQUAL(ent.GetChainCount(), 1); BOOST_CHECK_EQUAL(ent.GetResidueCount(), 1); BOOST_CHECK_EQUAL(ent.GetAtomCount(), 2); - PDB::PopFlags(); } BOOST_AUTO_TEST_CASE(alt_loc_import_export) @@ -292,8 +294,8 @@ BOOST_AUTO_TEST_CASE(alt_loc_import_export) // this scope is required to force the writer stream to be closed before // opening the file again in compare_files. Avoids a race condition. { - PDBReader reader(fname); - PDBWriter writer(String("testfiles/pdb/alt-loc-out.pdb")); + PDBReader reader(fname, IOProfile()); + PDBWriter writer(String("testfiles/pdb/alt-loc-out.pdb"), IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); @@ -309,8 +311,8 @@ BOOST_AUTO_TEST_CASE(write_ter) // this scope is required to force the writer stream to be closed before // opening the file again in compare_files. Avoids a race condition. { - PDBReader reader(fname); - PDBWriter writer(String("testfiles/pdb/ter-out.pdb")); + PDBReader reader(fname, IOProfile()); + PDBWriter writer(String("testfiles/pdb/ter-out.pdb"), IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); @@ -330,8 +332,8 @@ BOOST_AUTO_TEST_CASE(write_ter2) // this scope is required to force the writer stream to be closed before // opening the file again in compare_files. Avoids a race condition. { - PDBReader reader(fname); - PDBWriter writer(String("testfiles/pdb/ter2-out.pdb")); + PDBReader reader(fname, IOProfile()); + PDBWriter writer(String("testfiles/pdb/ter2-out.pdb"), IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); @@ -351,8 +353,8 @@ BOOST_AUTO_TEST_CASE(write_ter3) // this scope is required to force the writer stream to be closed before // opening the file again in compare_files. Avoids a race condition. { - PDBReader reader(fname); - PDBWriter writer(String("testfiles/pdb/ter3-out.pdb")); + PDBReader reader(fname, IOProfile()); + PDBWriter writer(String("testfiles/pdb/ter3-out.pdb"), IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); @@ -371,8 +373,8 @@ BOOST_AUTO_TEST_CASE(write_conect) // this scope is required to force the writer stream to be closed before // opening the file again in compare_files. Avoids a race condition. { - PDBReader reader(String("testfiles/pdb/conect.pdb")); - PDBWriter writer(String("testfiles/pdb/conect-out.pdb")); + PDBReader reader(String("testfiles/pdb/conect.pdb"), IOProfile()); + PDBWriter writer(String("testfiles/pdb/conect-out.pdb"), IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); reader.Import(ent); conop::Conopology& conop_inst=conop::Conopology::Instance(); @@ -389,17 +391,17 @@ BOOST_AUTO_TEST_CASE(alt_loc_tf) // this scope is required to force the writer stream to be closed before // opening the file again in compare_files. Avoids a race condition. mol::EntityHandle ent=mol::CreateEntity(); - PDBReader reader(fname); + PDBReader reader(fname, IOProfile()); reader.Import(ent); String out_name("testfiles/pdb/alt-loc-tf-out.pdb"); { - PDBWriter writer(out_name); + PDBWriter writer(out_name, IOProfile()); geom::Mat4 shift; shift.PasteTranslation(geom::Vec3(10,20,30)); ent.EditXCS().ApplyTransform(shift); writer.Write(ent); } - PDBReader r2(out_name); + PDBReader r2(out_name, IOProfile()); mol::EntityHandle ent2=mol::CreateEntity(); r2.Import(ent2); mol::ResidueHandle res1=ent2.FindResidue("A", mol::ResNum(1)); @@ -411,7 +413,7 @@ BOOST_AUTO_TEST_CASE(alt_loc_tf) BOOST_AUTO_TEST_CASE(res_name_too_long) { std::stringstream out; - PDBWriter writer(out); + PDBWriter writer(out, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); mol::XCSEditor edi=ent.EditXCS(); @@ -424,7 +426,7 @@ BOOST_AUTO_TEST_CASE(res_name_too_long) BOOST_AUTO_TEST_CASE(chain_name_too_long) { std::stringstream out; - PDBWriter writer(out); + PDBWriter writer(out, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); mol::XCSEditor edi=ent.EditXCS(); @@ -437,7 +439,7 @@ BOOST_AUTO_TEST_CASE(chain_name_too_long) BOOST_AUTO_TEST_CASE(atom_name_too_long) { std::stringstream out; - PDBWriter writer(out); + PDBWriter writer(out, IOProfile()); mol::EntityHandle ent=mol::CreateEntity(); mol::XCSEditor edi=ent.EditXCS(); diff --git a/modules/mol/alg/src/ldt.cc b/modules/mol/alg/src/ldt.cc index 7a676b00ae74eceea3fa4cac96b7b18575896590..4b08ed8cce82359f0af0adc6ce17cba3f7b02aad 100644 --- a/modules/mol/alg/src/ldt.cc +++ b/modules/mol/alg/src/ldt.cc @@ -25,10 +25,10 @@ using namespace ost; using namespace ost::io; using namespace ost::mol; -EntityHandle load(const String& file) +EntityHandle load(const String& file, const IOProfile& profile) { try { - PDBReader reader(file); + PDBReader reader(file, profile); if (reader.HasNext()) { EntityHandle ent=CreateEntity(); reader.Import(ent); @@ -56,18 +56,18 @@ void usage() int main (int argc, char* const *argv) { + IOProfile profile; // parse options String sel; - int flags=0; bool filter_clashes=false; char ch=0; while((ch=getopt(argc, argv, "ftcs:"))!=-1) { switch (ch) { case 'c': - flags|=PDB::CALPHA_ONLY; + profile.calpha_only=true; break; case 't': - flags|=PDB::SKIP_FAULTY_RECORDS; + profile.fault_tolerant=true; break; case 'f': filter_clashes=true; @@ -87,18 +87,17 @@ int main (int argc, char* const *argv) usage(); return -1; } - PDB::PushFlags(flags); String ref_file=argv[argc-1]; - EntityHandle ref=load(ref_file); + EntityHandle ref=load(ref_file, profile); if (!ref) { return -1; } EntityView ref_view=ref.Select(sel); for (int i=0; i<argc-1; ++i) { - EntityHandle model=load(argv[i]); + EntityHandle model=load(argv[i], profile); if (!model) { - if (!(flags&PDB::SKIP_FAULTY_RECORDS)) { + if (!profile.fault_tolerant) { return -1; } continue;