-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Version 0.1.3.6. Added PDB writer (#40)
* added writer for PDB * more * Version 0.1.3.6. Added PDB writer * fixes * flip
- Loading branch information
1 parent
2d67bf7
commit e53b634
Showing
11 changed files
with
13,316 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
name: cobot-io | ||
version: 0.1.3.5 | ||
version: 0.1.3.6 | ||
github: "biocad/cobot-io" | ||
license: BSD3 | ||
category: Bio | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
|
||
module Bio.PDB.Writer | ||
( pdbToFile | ||
, pdbToText | ||
) where | ||
|
||
|
||
import Bio.PDB.Type (Atom (..), Chain, Model, PDB (..)) | ||
import Control.Monad.IO.Class (MonadIO, liftIO) | ||
import Data.Text (Text) | ||
import qualified Data.Text as T (cons, drop, init, intercalate, | ||
last, length, pack, replicate, | ||
singleton, splitAt, take) | ||
import qualified Data.Text.IO as TIO (writeFile) | ||
import Data.Vector (Vector) | ||
import qualified Data.Vector as V (all, foldl', fromList, last, | ||
length, toList, zip) | ||
import Text.Printf (printf) | ||
|
||
-- | Writes 'PDB' to the given path. | ||
-- | ||
pdbToFile :: MonadIO m => PDB -> FilePath -> m () | ||
pdbToFile pdb = liftIO . flip TIO.writeFile (pdbToText pdb) | ||
|
||
-- | Converts 'PDB' to 'Text'. | ||
-- | ||
pdbToText :: PDB -> Text | ||
pdbToText PDB{..} = (<> newLine <> toPDBLine end) | ||
$ T.intercalate newLine . V.toList . fmap (modelToText separateModels) | ||
$ V.zip models $ V.fromList [1 ..] | ||
where | ||
separateModels = V.length models > 1 | ||
|
||
end :: Text | ||
end = "END " | ||
|
||
type TerAtom = Atom | ||
|
||
modelToText :: Bool -> (Model, Int) -> Text | ||
modelToText separateModels (pdbModel, modelInd) = modelPrefix <> atomsT <> modelSuffix | ||
where | ||
atomsT = T.intercalate newLine . V.toList . fmap atomOrTer . withTers $ pdbModel | ||
|
||
modelPrefix | separateModels = toPDBLine (mdl <> spaceText 4 <> prependToS 4 modelInd) <> newLine | ||
| otherwise = "" | ||
|
||
modelSuffix | separateModels = newLine <> toPDBLine endmdl | ||
| otherwise = "" | ||
|
||
mdl :: Text | ||
mdl = "MODEL " | ||
|
||
endmdl :: Text | ||
endmdl = "ENDMDL " | ||
|
||
withTers :: Vector Chain -> Vector (Either Atom TerAtom) | ||
withTers = snd . V.foldl' addTer (0, mempty) | ||
where | ||
addTer :: (Int, Vector (Either Atom TerAtom)) -> Chain -> (Int, Vector (Either Atom TerAtom)) | ||
addTer (add, res) cur = if V.all (isHetatm . atomResName) cur then (add, newRes) else withTer | ||
where | ||
ter = addSerial (add + 1) $ V.last cur | ||
newRes = res <> fmap (Left . addSerial add) cur | ||
|
||
withTer = (add + 1, newRes <> pure (Right ter)) | ||
|
||
addSerial :: Int -> Atom -> Atom | ||
addSerial i at@Atom{..} = at { atomSerial = atomSerial + i } | ||
|
||
atomOrTer :: Either Atom TerAtom -> Text | ||
atomOrTer = either atomToText terAtomToText | ||
|
||
terAtomToText :: Atom -> Text | ||
terAtomToText at = toPDBLine $ pref <> spaceText 6 <> suf | ||
where | ||
t = (ter <>) . T.take 21 . T.drop 6 $ atomToText at | ||
(pref, suf) = T.drop 6 <$> T.splitAt 11 t | ||
|
||
ter :: Text | ||
ter = "TER " | ||
|
||
atomToText :: Atom -> Text | ||
atomToText Atom{..} = res | ||
where | ||
recordName | isHetatm atomResName = hetatm | ||
| otherwise = atm | ||
|
||
serial = prependToS 5 atomSerial | ||
name = (\t -> if T.last t == space then T.cons space $ T.init t else t) $ appendTo 4 atomName | ||
altLoc = T.singleton atomAltLoc | ||
resName = prependTo 3 atomResName | ||
chainID = T.singleton atomChainID | ||
resSeq = prependToS 4 atomResSeq | ||
iCode = T.singleton atomICode | ||
x = prependTo 8 $ printFloat 3 atomX | ||
y = prependTo 8 $ printFloat 3 atomY | ||
z = prependTo 8 $ printFloat 3 atomZ | ||
occupancy = prependTo 6 $ printFloat 2 atomOccupancy | ||
tempFactor = prependTo 6 $ printFloat 2 atomTempFactor | ||
element = prependTo 2 atomElement | ||
|
||
charge | atomCharge /= zeroCharge = prependTo 2 atomCharge | ||
| otherwise = spaceText 2 | ||
|
||
res = recordName <> serial <> spaceText 1 <> name <> altLoc | ||
<> resName <> spaceText 1 <> chainID <> resSeq <> iCode <> spaceText 3 | ||
<> x <> y <> z <> occupancy <> tempFactor <> spaceText 10 | ||
<> element <> charge | ||
|
||
atm :: Text | ||
atm = "ATOM " | ||
|
||
hetatm :: Text | ||
hetatm = "HETATM" | ||
|
||
zeroCharge :: Text | ||
zeroCharge = "0" | ||
|
||
printFloat :: Int -> Float -> Text | ||
printFloat after f = T.pack $ printf "%.*f" after f | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Utility functions. | ||
-------------------------------------------------------------------------------- | ||
|
||
isHetatm :: Text -> Bool | ||
isHetatm = (`notElem` canonicalAminoAcids) | ||
where | ||
canonicalAminoAcids = [ "ACE", "ALA", "ARG", "ASN", "ASP", "CYS", "GLU", "GLN" | ||
, "GLY", "HIS", "HID", "HIE", "HIP", "ILE", "LEU", "LYS", "LYN" | ||
, "MET", "NMA", "PHE", "PRO", "SER", "THR", "TRP", "TYR", "VAL" | ||
] | ||
|
||
toPDBLine :: Text -> Text | ||
toPDBLine = appendTo 80 | ||
|
||
prependToS :: Show a => Int -> a -> Text | ||
prependToS i = prependTo i . T.pack . show | ||
|
||
prependTo :: Int -> Text -> Text | ||
prependTo i t = spaceText (i - T.length t) <> t | ||
|
||
appendTo :: Int -> Text -> Text | ||
appendTo i t = t <> spaceText (i - T.length t) | ||
|
||
newLine :: Text | ||
newLine = "\n" | ||
|
||
spaceText :: Int -> Text | ||
spaceText = flip T.replicate " " | ||
|
||
space :: Char | ||
space = ' ' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.