Skip to content

Commit

Permalink
Merge Backboner.Protein module and private ProteinData.jl package, ad…
Browse files Browse the repository at this point in the history
…d properties
  • Loading branch information
AntonOresten committed Aug 17, 2024
1 parent 2c8a1b6 commit 9d02815
Show file tree
Hide file tree
Showing 12 changed files with 460 additions and 5 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 anton083 <[email protected]> and contributors
Copyright (c) 2024 Anton Oresten <[email protected]> and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
21 changes: 18 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
name = "ProteinChains"
uuid = "b8e8f2a5-48d3-44f1-ba0d-c71cb7726ff8"
authors = ["anton083 <[email protected]> and contributors"]
version = "1.0.0-DEV"
authors = ["Anton Oresten <[email protected]> and contributors"]
version = "0.0.1"

[deps]
AssigningSecondaryStructure = "8ed43e74-60fb-4e11-99b9-91deed37aef7"
Backboner = "9ac9c2a2-1cfe-46d3-b3fd-6fa470ea56a7"
BioStructures = "de9282ab-8554-53be-b2d6-f6c222edabfc"

[weakdeps]
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[extensions]
ZygoteExt = "Zygote"

[compat]
julia = "1.6.7"
AssigningSecondaryStructure = "0.4"
Backboner = "0.11"
BioStructures = "4"
Zygote = "0.6"
julia = "1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://MurrellGroup.github.io/ProteinChains.jl/dev/)
[![Build Status](https://github.com/MurrellGroup/ProteinChains.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/MurrellGroup/ProteinChains.jl/actions/workflows/CI.yml?query=branch%3Amain)
[![Coverage](https://codecov.io/gh/MurrellGroup/ProteinChains.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/MurrellGroup/ProteinChains.jl)

This Julia package provides implements the `ProteinChain` type: an efficient representation of protein chains with a simple and intuitive interface to assign and implement arbitrary properties such as secondary structure, backbone bond geometry, knots, and more.

## Installation

```julia
using Pkg
Pkg.add("ProteinChains")
```
24 changes: 24 additions & 0 deletions ext/ZygoteExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module ZygoteExt

using ProteinChains

import Zygote

function idealize!(chain::ProteinChain; ideal::BackboneGeometry=BackboneGeometry())
backbone = Backboner.Backbone(chain.backbone)
T = eltype(backbone.coords)
ideal_lengths = T[ideal.N_Ca_length, ideal.Ca_C_length, ideal.C_N_length]
ideal_angles = T[ideal.N_Ca_C_length, ideal.Ca_C_N_length, ideal.C_N_Ca_length]
new_backbone = Backboner.idealize(backbone, ideal_lengths, ideal_angles)
chain.backbone = new_backbone
return chain
end

function idealize!(structure::ProteinStructure; kwargs...)
for chain in structure
idealize!(chain; kwargs...)
end
return structure
end

end
25 changes: 24 additions & 1 deletion src/ProteinChains.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
module ProteinChains

# Write your package code here.
using Backboner

using BioStructures: BioStructures, PDBFormat, MMCIFFormat

include("properties.jl")

include("chain.jl")
export ProteinChain
export countresidues

include("structure.jl")
export ProteinStructure

include("idealize.jl")
export BackboneGeometry

include("secondary-structure.jl")

include("chain-properties.jl")

include("io.jl")
export readrecord
export readpdb
export readcif

end
38 changes: 38 additions & 0 deletions src/chain-properties.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
function assign_bond_lengths!(chain::ProteinChain)
chain.bond_lengths = Backboner.get_bond_lengths(Backboner.Backbone(chain.backbone))
end

function assign_bond_angles!(chain::ProteinChain)
chain.bond_angles = Backboner.get_bond_angles(Backboner.Backbone(chain.backbone))
end

function assign_torsion_angles!(chain::ProteinChain)
chain.torsion_angles = Backboner.get_torsional_angles(Backboner.Backbone(chain.backbone))
end

function assign_secondary_structure!(chain::ProteinChain, dict::Union{Dict{ProteinChain,Any},Nothing}=nothing)
number_vector = isnothing(dict) ? ASS.assign_secondary_structure(chain) : dict[chain]
chain.secondary_structure = number_vector_to_code_string(number_vector)
end

function assign_standard_residue!(chain::ProteinChain, standard_residue::Matrix{<:Real}=STANDARD_RESIDUE_ANGSTROM)
chain.standard_residue = standard_residue
end

function assign_residue_rotations!(chain::ProteinChain)
frames = Backboner.Frames(Backboner.Backbone(chain.backbone), chain.standard_residue)
chain.residue_rotations = frames.rotations
end

function assign_residue_rotations_quat!(chain::ProteinChain)
frames = Backboner.Frames(Backboner.Backbone(chain.backbone), chain.standard_residue)
chain.residue_rotations_quat = rotation_matrices_to_quaternions(frames.rotations)
end

function assign_residue_translations!(chain::ProteinChain)
chain.residue_translations = Backboner.centroid(chain.backbone; dims=2)
end

function assign_is_knotted!(chain::ProteinChain)
chain.is_knotted = Backboner.is_knotted(Backboner.Backbone(chain.backbone)[2:3:end])
end
44 changes: 44 additions & 0 deletions src/chain.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
mutable struct ProteinChain
id::String
aminoacids::String
backbone::Array{Float64,3}
numbers::Vector{Int}
properties::Dict{Symbol,Any}

function ProteinChain(id::String, aminoacids::String, backbone::Array{Float64,3}, numbers::Vector{Int}, properties::Dict{Symbol,Any})
chain = new(id, aminoacids, backbone, numbers, properties)
countresidues(chain)
@assert all(!in(fieldnames(ProteinChain)), keys(properties))
return chain
end
end

Base.getproperty(chain::ProteinChain, property::Symbol) = _getproperty(chain, property)
Base.setproperty!(chain::ProteinChain, property::Symbol, value) = _setproperty!(chain, property, value)

function countresidues(chain::ProteinChain)
@assert length(chain.aminoacids) == size(chain.backbone, 3) == length(chain.numbers)
return length(chain.aminoacids)
end

function ProteinChain(id::String, aminoacids::String, backbone::Array{Float64,3}, numbers::Vector{Int}; kwargs...)
chain = ProteinChain(id, aminoacids, backbone, numbers, Dict{Symbol,Any}())
!isempty(kwargs) && push!(chain.properties, kwargs...)
return chain
end

Base.summary(chain::ProteinChain) = "$(countresidues(chain))-residue ProteinChain \"$(chain.id)\" with $(length(chain.properties)) properties"

function Base.show(io::IO, ::MIME"text/plain", chain::ProteinChain)
print(io, summary(chain), ":")
printstyled(io, "\n fields:", color=:yellow)
context = IOContext(io, :compact => true, :limit => true)
for fieldname in fieldnames(ProteinChain)[1:end-1]
printfield(context, chain, fieldname)
end
printstyled(io, "\n properties:", color=:yellow)
isempty(chain.properties) && print(io, " (none)")
for property in keys(chain.properties)
printfield(context, chain, property)
end
end
34 changes: 34 additions & 0 deletions src/idealize.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Base.@kwdef struct BackboneGeometry
N_Ca_length = 1.46
Ca_C_length = 1.52
C_N_length = 1.33

N_Ca_C_angle = 1.94
Ca_C_N_angle = 2.03
C_N_Ca_angle = 2.13
end

# TODO: make this align with default BackboneGeometry
const STANDARD_RESIDUE_ANGSTROM = [
-1.066 -0.200 1.266;
0.645 -0.527 -0.118;
0.000 0.000 0.000;
] # N Ca Cs

function append_residue(backbone::Backbone, dihedrals::Vector{<:Real}; ideal::BackboneGeometry=BackboneGeometry())
length(dihedrals) == 3 || throw(ArgumentError("length of `dihedrals` must be 3, as only 3 backbone atoms are introduced."))
lengths = [ideal.C_N_length, ideal.N_Ca_length, ideal.Ca_C_length]
angles = [ideal.Ca_C_N_angle, ideal.C_N_Ca_angle, ideal.N_Ca_C_angle]
append_bonds(backbone, Float64.(lengths), Float64.(angles), Float64.(dihedrals))
end

function prepend_residue(backbone::Backbone, dihedrals::Vector{<:Real}; ideal::BackboneGeometry=BackboneGeometry())
length(dihedrals) == 3 || throw(ArgumentError("length of `dihedrals` must be 3, as only 3 backbone atoms are introduced."))
lengths = [ideal.N_Ca_length, ideal.Ca_C_length, ideal.C_N_length]
angles = [ideal.N_Ca_C_angle, ideal.Ca_C_N_angle, ideal.C_N_Ca_angle]
return prepend_bonds(backbone, Float64.(lengths), Float64.(angles), Float64.(dihedrals))
end

# TODO: use cooldown (and Float64?) to increase precision and accuracy
# See ext/ZygoteExt.jl
function idealize! end
Loading

0 comments on commit 9d02815

Please sign in to comment.