From 340f154ba56950c37615e239bc586151d39aa796 Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 17:28:46 -0300 Subject: [PATCH 1/8] iterate over contributions of residue in single-residue contributions --- docs/src/tools.md | 23 ++++++- src/tools/residue_contributions.jl | 103 +++++++++++++++++++++++++---- 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/docs/src/tools.md b/docs/src/tools.md index ba1ccd79..0359a8cd 100644 --- a/docs/src/tools.md +++ b/docs/src/tools.md @@ -104,7 +104,7 @@ One nice way to visualize the accumulation or depletion of a solvent around a ma Here, one can see that Glycerol accumulates on Asp76 and on the proximity of hydrogen-bonding residues (Serine residues mostly). This figure was obtained by extracting from atomic contributions of the protein the contribution of each residue to the MDDF, coordination numbers or minimum-distance counts. !!! compat - All features described in this section are only available in v2.8.0 or greater. + All features described in this section are only available in v2.10.0 or greater. The computation of the contributions of each residue can be performed with the convenience function `ResidueContributions`, which creates an object containing the contributions of the residues to the mddf (or coordination numbers, or minimum-distance counts), the @@ -173,6 +173,27 @@ rc2 = rc / 15 rc2 = 2 * rc ``` +When the contributions of a single residue are computed, or a single-residue contribution is retrieved from +a `ResidueContributions` object, the indexing and iteration over that object occurs over the contributions of that residue: + +```julia +using ComplexMixtures, PDBTools, Plots +... +result = mddf(trajectory_file, solute, solvent, options) +rc = ResidueContributions(result, select(atoms, "protein")) +rc7 = rc[7] # contributions of residue 7 +# iterate over the contributions of residue 7 +rc7[1] # contribution of the first distance +rc7[end] # contribution of the last distance +``` + +This is particular useful to retrieve the contributions from all residues at a given distance: + +```julia +rc = ResidueContributions(result, select(atoms, "protein")) +rc_last_distance = [ r[end] for r in rc ] +``` + ### Saving and loading a ResidueContributions object The `ResidueContributions` object can be saved and loaded for easier data analysis. In particular, this diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index a812bc65..f5ec3196 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -1,3 +1,8 @@ +# These are type parameters necessary for indexing and iterating properly when a single +# residue contribution is selected, to iterate ver the contributions of such residue. +struct SingleResidueContribution end +struct MultipleResidueContribution end + """ ResidueContributions (data structure) @@ -89,6 +94,29 @@ rc_range = rc[10:50] # slice the residue contributions contourf(rc_range) # plots a contour map of the selected residues ``` +## Single-residue contributions + +When the contributions of a single residue are computed, or a single-residue contribution is retrieved from +a `ResidueContributions` object, the indexing and iteration over that object occurs over the contributions of that residue: + +```julia +using ComplexMixtures, PDBTools, Plots +... +result = mddf(trajectory_file, solute, solvent, options) +rc = ResidueContributions(result, select(atoms, "protein")) +rc7 = rc[7] # contributions of residue 7 +# iterate over the contributions of residue 7 +rc7[1] # contribution of the first distance +rc7[end] # contribution of the last distance +``` + +This is particular useful to retrieve the contributions from all residues at a given distance: + +```julia +rc = ResidueContributions(result, select(atoms, "protein")) +rc_last_distance = [ r[end] for r in rc ] +``` + ## Arithmetic operations ```julia @@ -113,7 +141,7 @@ rc4 = rc2 / 2 Saving and loading was introduced in v2.8.0. """ -@kwdef struct ResidueContributions +@kwdef struct ResidueContributions{N<:Union{SingleResidueContribution, MultipleResidueContribution}} Version::VersionNumber = pkgversion(@__MODULE__) d::Vector{Float64} residue_contributions::Vector{Vector{Float64}} @@ -174,7 +202,8 @@ function ResidueContributions( serial=false, ) - return ResidueContributions(; + N = length(resnums) == 1 ? SingleResidueContribution : MultipleResidueContribution + return ResidueContributions{N}(; d=results.d[idmin:idmax], residue_contributions=[rc[idmin:idmax] for rc in rescontrib], resnums=resnums, @@ -197,23 +226,52 @@ import Base: == rc1.d == rc2.d && rc1.xticks == rc2.xticks && rc1.resnums == rc2.resnums && rc1.residue_contributions == rc2.residue_contributions -function Base.copy(rc::ResidueContributions) - return ResidueContributions( +function Base.copy(rc::RCType) where {RCType<:ResidueContributions} + return RCType( d=copy(rc.d), residue_contributions=[copy(v) for v in rc.residue_contributions], resnums=copy(rc.resnums), xticks=(copy(rc.xticks[1]), copy(rc.xticks[2])), ) end -function Base.getindex(rc::ResidueContributions, r::AbstractRange) - return ResidueContributions( + + +# +# Indexing and iteration of MultipleResidueContribution objects: iterate over residues +# +function Base.getindex(rc::ResidueContributions{MultipleResidueContribution}, r::AbstractRange) + N = length(r) == 1 ? SingleResidueContribution : MultipleResidueContribution + return ResidueContributions{N}( d=rc.d, residue_contributions=[rc.residue_contributions[i] for i in r], resnums=rc.resnums[r], xticks=(rc.xticks[1][r], rc.xticks[2][r]), ) end -Base.getindex(rc::ResidueContributions, i) = rc[i:i] +Base.length(rc::ResidueContributions{MultipleResidueContribution}) = length(rc.residue_contributions) +Base.getindex(rc::ResidueContributions{MultipleResidueContribution}, i) = rc[i:i] +Base.firstindex(rc::ResidueContributions{MultipleResidueContribution}) = 1 +Base.lastindex(rc::ResidueContributions{MultipleResidueContribution}) = length(rc.residue_contributions) +function Base.iterate(rc::ResidueContributions{MultipleResidueContribution}, state=firstindex(rc)) + if state > length(rc) + return nothing + end + return rc[state], state + 1 +end + +# +# Indexing and iteration of SingleResidueContribution objects: iterate over the contributions of a single residue +# +Base.length(rc::ResidueContributions{SingleResidueContribution}) = length(first(rc.residue_contributions)) +Base.getindex(rc::ResidueContributions{SingleResidueContribution}, i) = first(rc.residue_contributions)[i] +Base.firstindex(rc::ResidueContributions{SingleResidueContribution}) = firstindex(first(rc.residue_contributions)) +Base.lastindex(rc::ResidueContributions{SingleResidueContribution}) = length(first(rc.residue_contributions)) +function Base.iterate(rc::ResidueContributions{SingleResidueContribution}, state=firstindex(first(rc.residue_contributions))) + if state > length(first(rc.residue_contributions)) + return nothing + end + return first(rc.residue_contributions)[state], state + 1 +end import Base: -, +, /, * function -(rc1::ResidueContributions, rc2::ResidueContributions) @@ -257,7 +315,9 @@ function *(rc1::ResidueContributions, rc2::ResidueContributions) return rc_new end +# # Arithmetic operations with scalars +# function *(rc::ResidueContributions, x::Real) rc2 = copy(rc) for v in rc2.residue_contributions @@ -306,9 +366,9 @@ end function Base.show(io::IO, ::MIME"text/plain", rc::ResidueContributions) if _testing_show_method[] - print(io, "\n Residue Contributions\n") + print(io, "\n Residue Contributions - $(length(rc)) residues.\n") else - printstyled(io, "\n Residue Contributions\n", bold=true) + printstyled(io, "\n Residue Contributions - $(length(rc)) residues.\n", bold=true) end m = rc.residue_contributions clims, colorscale = _set_clims_and_colorscale!(rc) @@ -410,7 +470,7 @@ function load(filename::String, ::Type{ResidueContributions}) _check_version(filename) rc = try open(filename, "r") do io - JSON3.read(io, ResidueContributions) + JSON3.read(io, ResidueContributions{MultipleResidueContribution}) end catch throw(ArgumentError("""\n @@ -418,7 +478,12 @@ function load(filename::String, ::Type{ResidueContributions}) """)) end - return rc + # Converto to SingleResidueContribution if a single contribution was read + return if length(rc) == 1 + rc[1] + else + rc + end end @testitem "ResidueContribution" begin @@ -473,10 +538,11 @@ end @test_throws ArgumentError load(tmpfile, ResidueContributions) # version issues - rc_future = ResidueContributions(Version=v"1000.0.0", d=rc.d, residue_contributions=rc.residue_contributions, resnums=rc.resnums, xticks=rc.xticks) + N = ComplexMixtures.MultipleResidueContribution + rc_future = ResidueContributions{N}(Version=v"1000.0.0", d=rc.d, residue_contributions=rc.residue_contributions, resnums=rc.resnums, xticks=rc.xticks) save(tmpfile, rc_future) @test_throws ArgumentError load(tmpfile, ResidueContributions) - rc_past = ResidueContributions(Version=v"1.0.0", d=rc.d, residue_contributions=rc.residue_contributions, resnums=rc.resnums, xticks=rc.xticks) + rc_past = ResidueContributions{N}(Version=v"1.0.0", d=rc.d, residue_contributions=rc.residue_contributions, resnums=rc.resnums, xticks=rc.xticks) save(tmpfile, rc_past) @test_throws ArgumentError load(tmpfile, ResidueContributions) @@ -488,6 +554,17 @@ end rc2 = rc[2:10] @test rc2.resnums == 2:10 @test rc2 == ResidueContributions(result, select(atoms, "protein and resnum > 1 and resnum < 11")) + @test firstindex(rc) == 1 + @test lastindex(rc) == 104 + @test length(rc) == 104 + @test firstindex(rc1) == 1 + @test lastindex(rc1) == 101 + @test length(rc1) == 101 + + # Test iterators + @test count(true for r in rc) == 104 + @test count(true for r in rc1) == 101 + @test [ r[end] for r in rc ] == [ r[end] for r in rc.residue_contributions ] # empty plot (just test if the show function does not throw an error) rc2 = copy(rc) From 83442fa6c6f7bcd86f7694297ed440a1eda7d69b Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 20:18:20 -0300 Subject: [PATCH 2/8] fix jdoctest (print number of residues) --- src/tools/residue_contributions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index f5ec3196..944f31a0 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -51,7 +51,7 @@ julia> results = load(data_dir*"/NAMD/Protein_in_Glycerol/protein_glyc.json"); julia> rc = ResidueContributions(results, select(atoms, "protein"); silent=true) - Residue Contributions + Residue Contributions - 274 residues. 3.51 █ █ █ █ █ 3.27 █ █ █ 3.03 █ █ █ █ █ █ █ █ From 916ef384870f1ceb4a440bf07c5dd1ad5f9a0ed0 Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 21:09:59 -0300 Subject: [PATCH 3/8] implement broadcasting residue contribution properties --- src/tools/residue_contributions.jl | 52 ++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index 944f31a0..a6363878 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -82,14 +82,13 @@ contourf(rc) # plots a contour map ## Slicing -Slicing, or indexing, the residue contributions returns a new `ResidueContributions` object with the selected residues: +A slice of the residue contributions returns a new `ResidueContributions` object with the selected residues: ```julia using ComplexMixtures, PDBTools, Plots ... result = mddf(trajectory_file, solute, solvent, options) rc = ResidueContributions(result, select(atoms, "protein")) -rc_7 = rc[7] # contributions of residue 7 rc_range = rc[10:50] # slice the residue contributions contourf(rc_range) # plots a contour map of the selected residues ``` @@ -104,17 +103,23 @@ using ComplexMixtures, PDBTools, Plots ... result = mddf(trajectory_file, solute, solvent, options) rc = ResidueContributions(result, select(atoms, "protein")) -rc7 = rc[7] # contributions of residue 7 -# iterate over the contributions of residue 7 -rc7[1] # contribution of the first distance -rc7[end] # contribution of the last distance +rc60 = rc[60] # contributions of residue 60 only +# index and iterate over the contributions of residue 7 +rc60[1] # contribution at the first distance (equivalent to first(rc60)) +rc60[end] # contribution at the last distance (equivalent to last(rc60)) +count(>(0.01), rc60) # number of distances with contributions greater than 0.01 +findmax(rc60) # index and value of maximum contribution ``` This is particular useful to retrieve the contributions from all residues at a given distance: ```julia rc = ResidueContributions(result, select(atoms, "protein")) -rc_last_distance = [ r[end] for r in rc ] +rc_last_distance = [ c[end] for c in rc ] +# or, equivalently +rc_last_distance = last.(rc) +# index and value of maximum contribution at each distance, for all residues +findmax.(rc60) ``` ## Arithmetic operations @@ -138,7 +143,7 @@ rc4 = rc2 / 2 !!! compat Slicing, indexing, and multiplication and divison by scalars were introduces in v2.7.0. - Saving and loading was introduced in v2.8.0. + Saving and loading was introduced in v2.8.0. Iterators were introduced in v2.10.0. """ @kwdef struct ResidueContributions{N<:Union{SingleResidueContribution, MultipleResidueContribution}} @@ -146,6 +151,7 @@ rc4 = rc2 / 2 d::Vector{Float64} residue_contributions::Vector{Vector{Float64}} resnums::Vector{Int} + resnames::Vector{String} xticks::Tuple{Vector{Int},Vector{String}} end @@ -195,6 +201,7 @@ function ResidueContributions( # Obtain pretty labels for the residues in the x-axis (using PDBTools) resnums = PDBTools.resnum.(residues) + resnames = PDBTools.resname.(residues) xticks = PDBTools.residue_ticks(atoms; first=first(resnums), last=last(resnums), @@ -206,6 +213,7 @@ function ResidueContributions( return ResidueContributions{N}(; d=results.d[idmin:idmax], residue_contributions=[rc[idmin:idmax] for rc in rescontrib], + resnames=resnames, resnums=resnums, xticks=xticks, ) @@ -224,12 +232,13 @@ end import Base: == ==(rc1::ResidueContributions, rc2::ResidueContributions) = rc1.d == rc2.d && rc1.xticks == rc2.xticks && rc1.resnums == rc2.resnums && - rc1.residue_contributions == rc2.residue_contributions + rc1.resnames == rc2.resnames && rc1.residue_contributions == rc2.residue_contributions function Base.copy(rc::RCType) where {RCType<:ResidueContributions} return RCType( d=copy(rc.d), residue_contributions=[copy(v) for v in rc.residue_contributions], + resnames=copy(rc.resnames), resnums=copy(rc.resnums), xticks=(copy(rc.xticks[1]), copy(rc.xticks[2])), ) @@ -244,6 +253,7 @@ function Base.getindex(rc::ResidueContributions{MultipleResidueContribution}, r: return ResidueContributions{N}( d=rc.d, residue_contributions=[rc.residue_contributions[i] for i in r], + resnames=rc.resnames[r], resnums=rc.resnums[r], xticks=(rc.xticks[1][r], rc.xticks[2][r]), ) @@ -252,6 +262,7 @@ Base.length(rc::ResidueContributions{MultipleResidueContribution}) = length(rc.r Base.getindex(rc::ResidueContributions{MultipleResidueContribution}, i) = rc[i:i] Base.firstindex(rc::ResidueContributions{MultipleResidueContribution}) = 1 Base.lastindex(rc::ResidueContributions{MultipleResidueContribution}) = length(rc.residue_contributions) +Base.keys(rc::ResidueContributions{MultipleResidueContribution}) = firstindex(rc):lastindex(rc) function Base.iterate(rc::ResidueContributions{MultipleResidueContribution}, state=firstindex(rc)) if state > length(rc) return nothing @@ -266,12 +277,14 @@ Base.length(rc::ResidueContributions{SingleResidueContribution}) = length(first( Base.getindex(rc::ResidueContributions{SingleResidueContribution}, i) = first(rc.residue_contributions)[i] Base.firstindex(rc::ResidueContributions{SingleResidueContribution}) = firstindex(first(rc.residue_contributions)) Base.lastindex(rc::ResidueContributions{SingleResidueContribution}) = length(first(rc.residue_contributions)) +Base.keys(rc::ResidueContributions{SingleResidueContribution}) = firstindex(rc):lastindex(rc) function Base.iterate(rc::ResidueContributions{SingleResidueContribution}, state=firstindex(first(rc.residue_contributions))) if state > length(first(rc.residue_contributions)) return nothing end return first(rc.residue_contributions)[state], state + 1 end +PDBTools.resname(rc::ResidueContributions{SingleResidueContribution}) = first(rc.resnames) import Base: -, +, /, * function -(rc1::ResidueContributions, rc2::ResidueContributions) @@ -314,6 +327,22 @@ function *(rc1::ResidueContributions, rc2::ResidueContributions) end return rc_new end +function _isless_error() + throw(ArgumentError("""\n + Invalid operation on ResidueContributions object. Perhaps you meant apply the operation + to the residue contributions of a single residue, or to broadcast the operation? + + Examples: Incorrect Correct + findmax(rc) vs. findmax(rc[1]) *or* findmax.(rc) + maximum(rc) vs. maximum(rc[1]) *or() maximum.(rc) + count(>(0.01), rc) vs. count(>(0.01), rc[1]) *or* count.(>(0.01), rc) + + + """)) +end +Base.isless(::Any, ::ResidueContributions) = _isless_error() +Base.isless(::ResidueContributions, ::Any) = _isless_error() +Base.isless(::T, ::T) where {T<:ResidueContributions{SingleResidueContribution}} = _isless_error() # # Arithmetic operations with scalars @@ -565,6 +594,11 @@ end @test count(true for r in rc) == 104 @test count(true for r in rc1) == 101 @test [ r[end] for r in rc ] == [ r[end] for r in rc.residue_contributions ] + @test last.(rc) == [ r[end] for r in rc ] + @test first.(findmax.(rc[3:4])) ≈ [ 0.09254462465788318, 0.037371523587960476 ] + @test last.(findmax.(rc[3:4])) == [ 13, 31] + @test_throws ArgumentError findmax(rc) + @test_throws ArgumentError count(>(0.01), rc) # empty plot (just test if the show function does not throw an error) rc2 = copy(rc) From a4d3a04cfa476742cdf7bf6de865fc9c8282d88c Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 21:10:48 -0300 Subject: [PATCH 4/8] add resname test --- src/tools/residue_contributions.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index a6363878..5feadaf2 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -589,6 +589,7 @@ end @test firstindex(rc1) == 1 @test lastindex(rc1) == 101 @test length(rc1) == 101 + @test resname(rc[60]) == "PRO" # Test iterators @test count(true for r in rc) == 104 From 519844468d74947d945d4bdad4d8255a892d5b36 Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 21:16:43 -0300 Subject: [PATCH 5/8] update docs --- docs/src/tools.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/src/tools.md b/docs/src/tools.md index 0359a8cd..89b2c87d 100644 --- a/docs/src/tools.md +++ b/docs/src/tools.md @@ -191,7 +191,11 @@ This is particular useful to retrieve the contributions from all residues at a g ```julia rc = ResidueContributions(result, select(atoms, "protein")) -rc_last_distance = [ r[end] for r in rc ] +rc_last_distance = [ r[end] for r in rc ] +# or, equivalently +rc_last_distance = last.(rc) +# compute the maximum contribution of each residue: +max_c = maximum.(rc) ``` ### Saving and loading a ResidueContributions object From 3891f0d59fc7b485b0e44053fba702df599c8ef0 Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 21:31:31 -0300 Subject: [PATCH 6/8] remove resname to preserve compatibility and remove method ambiguities --- src/tools/residue_contributions.jl | 31 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index 5feadaf2..a3f59b07 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -151,7 +151,6 @@ rc4 = rc2 / 2 d::Vector{Float64} residue_contributions::Vector{Vector{Float64}} resnums::Vector{Int} - resnames::Vector{String} xticks::Tuple{Vector{Int},Vector{String}} end @@ -201,7 +200,6 @@ function ResidueContributions( # Obtain pretty labels for the residues in the x-axis (using PDBTools) resnums = PDBTools.resnum.(residues) - resnames = PDBTools.resname.(residues) xticks = PDBTools.residue_ticks(atoms; first=first(resnums), last=last(resnums), @@ -213,7 +211,6 @@ function ResidueContributions( return ResidueContributions{N}(; d=results.d[idmin:idmax], residue_contributions=[rc[idmin:idmax] for rc in rescontrib], - resnames=resnames, resnums=resnums, xticks=xticks, ) @@ -232,13 +229,12 @@ end import Base: == ==(rc1::ResidueContributions, rc2::ResidueContributions) = rc1.d == rc2.d && rc1.xticks == rc2.xticks && rc1.resnums == rc2.resnums && - rc1.resnames == rc2.resnames && rc1.residue_contributions == rc2.residue_contributions + rc1.residue_contributions == rc2.residue_contributions function Base.copy(rc::RCType) where {RCType<:ResidueContributions} return RCType( d=copy(rc.d), residue_contributions=[copy(v) for v in rc.residue_contributions], - resnames=copy(rc.resnames), resnums=copy(rc.resnums), xticks=(copy(rc.xticks[1]), copy(rc.xticks[2])), ) @@ -253,7 +249,6 @@ function Base.getindex(rc::ResidueContributions{MultipleResidueContribution}, r: return ResidueContributions{N}( d=rc.d, residue_contributions=[rc.residue_contributions[i] for i in r], - resnames=rc.resnames[r], resnums=rc.resnums[r], xticks=(rc.xticks[1][r], rc.xticks[2][r]), ) @@ -284,7 +279,6 @@ function Base.iterate(rc::ResidueContributions{SingleResidueContribution}, state end return first(rc.residue_contributions)[state], state + 1 end -PDBTools.resname(rc::ResidueContributions{SingleResidueContribution}) = first(rc.resnames) import Base: -, +, /, * function -(rc1::ResidueContributions, rc2::ResidueContributions) @@ -340,9 +334,9 @@ function _isless_error() """)) end -Base.isless(::Any, ::ResidueContributions) = _isless_error() -Base.isless(::ResidueContributions, ::Any) = _isless_error() -Base.isless(::T, ::T) where {T<:ResidueContributions{SingleResidueContribution}} = _isless_error() +Base.isless(::ResidueContributions, ::ResidueContributions) = _isless_error() +Base.isless(::Real, ::ResidueContributions) = _isless_error() +Base.isless(::ResidueContributions, ::Real) = _isless_error() # # Arithmetic operations with scalars @@ -568,10 +562,22 @@ end # version issues N = ComplexMixtures.MultipleResidueContribution - rc_future = ResidueContributions{N}(Version=v"1000.0.0", d=rc.d, residue_contributions=rc.residue_contributions, resnums=rc.resnums, xticks=rc.xticks) + rc_future = ResidueContributions{N}( + Version=v"1000.0.0", + d=rc.d, + residue_contributions=rc.residue_contributions, + resnums=rc.resnums, + xticks=rc.xticks + ) save(tmpfile, rc_future) @test_throws ArgumentError load(tmpfile, ResidueContributions) - rc_past = ResidueContributions{N}(Version=v"1.0.0", d=rc.d, residue_contributions=rc.residue_contributions, resnums=rc.resnums, xticks=rc.xticks) + rc_past = ResidueContributions{N}( + Version=v"1.0.0", + d=rc.d, + residue_contributions=rc.residue_contributions, + resnums=rc.resnums, + xticks=rc.xticks + ) save(tmpfile, rc_past) @test_throws ArgumentError load(tmpfile, ResidueContributions) @@ -589,7 +595,6 @@ end @test firstindex(rc1) == 1 @test lastindex(rc1) == 101 @test length(rc1) == 101 - @test resname(rc[60]) == "PRO" # Test iterators @test count(true for r in rc) == 104 From ff7b53f4a24100c00aaa7857ec751f35ee470e7d Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 21:45:19 -0300 Subject: [PATCH 7/8] use coordination number in test to remove random component --- src/tools/residue_contributions.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index a3f59b07..d97851fc 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -597,12 +597,15 @@ end @test length(rc1) == 101 # Test iterators + rc = ResidueContributions(result, select(atoms, "protein"); type=:coordination_number) + rc1 = rc[1] @test count(true for r in rc) == 104 @test count(true for r in rc1) == 101 @test [ r[end] for r in rc ] == [ r[end] for r in rc.residue_contributions ] @test last.(rc) == [ r[end] for r in rc ] - @test first.(findmax.(rc[3:4])) ≈ [ 0.09254462465788318, 0.037371523587960476 ] - @test last.(findmax.(rc[3:4])) == [ 13, 31] + @test first.(findmax.(rc[3:4])) ≈ [ 8.8, 4.0 ] + @test last.(findmax.(rc[3:4])) == [ 101, 101 ] + @test findfirst.(>=(0.5), rc[3:4]) == [8, 18] @test_throws ArgumentError findmax(rc) @test_throws ArgumentError count(>(0.01), rc) From a1b3fe1273deff567df710d8f9382f5fc13575b7 Mon Sep 17 00:00:00 2001 From: Leandro Martinez Date: Tue, 26 Nov 2024 22:03:32 -0300 Subject: [PATCH 8/8] test reading file with single residue, and test the missed isless method --- src/tools/residue_contributions.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tools/residue_contributions.jl b/src/tools/residue_contributions.jl index d97851fc..e8e82591 100644 --- a/src/tools/residue_contributions.jl +++ b/src/tools/residue_contributions.jl @@ -388,10 +388,12 @@ function _set_clims_and_colorscale!(rc::ResidueContributions; clims=nothing, col end function Base.show(io::IO, ::MIME"text/plain", rc::ResidueContributions) + n_residues = length(rc.residue_contributions) + plural = n_residues == 1 ? "" : "s" if _testing_show_method[] - print(io, "\n Residue Contributions - $(length(rc)) residues.\n") + print(io, "\n Residue Contributions - $n_residues residue$plural.\n") else - printstyled(io, "\n Residue Contributions - $(length(rc)) residues.\n", bold=true) + printstyled(io, "\n Residue Contributions - $n_residues residue$plural.\n", bold=true) end m = rc.residue_contributions clims, colorscale = _set_clims_and_colorscale!(rc) @@ -502,7 +504,7 @@ function load(filename::String, ::Type{ResidueContributions}) """)) end # Converto to SingleResidueContribution if a single contribution was read - return if length(rc) == 1 + return if length(rc.residue_contributions) == 1 rc[1] else rc @@ -550,10 +552,16 @@ end rcc.residue_contributions[104] # save and load + # a full set of residue contributions tmpfile = tempname() * ".json" save(tmpfile, rc) rc_load = load(tmpfile, ResidueContributions) @test rc == rc_load + # a single residue contribution + save(tmpfile, rc[1]) + rc_load = load(tmpfile, ResidueContributions) + @test rc[1] == rc_load + tmpfile = tempname() open(tmpfile, "w") do io println(io, """{"Version":"$(pkgversion(@__MODULE__))"}""") @@ -608,6 +616,7 @@ end @test findfirst.(>=(0.5), rc[3:4]) == [8, 18] @test_throws ArgumentError findmax(rc) @test_throws ArgumentError count(>(0.01), rc) + @test_throws ArgumentError count(<(0.01), rc) # empty plot (just test if the show function does not throw an error) rc2 = copy(rc)