From 171ca99ad3535551893afa477e7874be5e744fb6 Mon Sep 17 00:00:00 2001 From: "Maarten L. Hekkelman" Date: Tue, 27 Jan 2026 11:39:55 +0100 Subject: [PATCH] Require boost 1.84 or higher for numpy 2 Fix interface to be more compliant with standard concepts --- .clang-format | 22 ++++ .clang-tidy | 18 ++++ .github/workflows/cmake-multi-platform.yml | 2 +- libdssp/include/dssp.hpp | 102 ++++++++++-------- libdssp/src/dssp-io.cpp | 52 +++++---- libdssp/src/dssp.cpp | 118 +++++++++++---------- python-module/CMakeLists.txt | 4 +- python-module/dssp-python-plugin.cpp | 9 +- test/unit-test-dssp.cpp | 19 ++-- 9 files changed, 198 insertions(+), 148 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c605dc2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,22 @@ +BasedOnStyle: LLVM +UseTab: AlignWithSpaces +IndentWidth: 4 +TabWidth: 4 +BreakBeforeBraces: Allman +ColumnLimit: 0 +NamespaceIndentation: Inner +FixNamespaceComments: true +AccessModifierOffset: -2 +AllowShortCaseLabelsOnASingleLine: true +IndentCaseLabels: true +BreakConstructorInitializers: BeforeComma +BraceWrapping: + BeforeLambdaBody: false +AlignAfterOpenBracket: DontAlign +Cpp11BracedListStyle: false +IncludeBlocks: Regroup +LambdaBodyIndentation: Signature +AllowShortLambdasOnASingleLine: Inline +EmptyLineBeforeAccessModifier: LogicalBlock +IndentPPDirectives: AfterHash +PPIndentWidth: 1 diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..6d11f06 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,18 @@ +Checks: '-*, + bugprone-*, + -bugprone-easily-swappable-parameters, + cert-*, + modernize*, + -modernize-use-trailing-return-type, + -modernize-avoid-c-arrays, + -modernize-use-designated-initializers, + performance + ' + +# HeaderFilterRegex: '.*' +ExcludeHeaderFilterRegex: 'Eigen|Eigen/Eigenvalues|eigen3/Eigen/Eigenvalues|sqlite3.h' +CheckOptions: + - key: bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion + value: false + - key: bugprone-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion + value: false diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 7aa9f44..05798f2 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -39,7 +39,7 @@ jobs: - name: Install Catch2 Ubuntu if: matrix.os == 'ubuntu-latest' run: > - sudo apt-get update && sudo apt-get install catch2 libpython3-all-dev libboost1.83-all-dev + sudo apt-get update && sudo apt-get install catch2 libpython3-all-dev libboost1.84-all-dev - name: setup python uses: actions/setup-python@v5 diff --git a/libdssp/include/dssp.hpp b/libdssp/include/dssp.hpp index ea68eae..06784be 100644 --- a/libdssp/include/dssp.hpp +++ b/libdssp/include/dssp.hpp @@ -32,6 +32,8 @@ #include #include +#include +#include class dssp { @@ -105,7 +107,7 @@ class dssp dssp(const dssp &) = delete; dssp &operator=(const dssp &) = delete; - statistics get_statistics() const; + [[nodiscard]] statistics get_statistics() const; class iterator; using res_iterator = typename std::vector::iterator; @@ -120,37 +122,37 @@ class dssp residue_info &operator=(const residue_info &rhs) = default; explicit operator bool() const { return not empty(); } - bool empty() const { return m_impl == nullptr; } + [[nodiscard]] bool empty() const { return m_impl == nullptr; } - std::string asym_id() const; - int seq_id() const; - std::string alt_id() const; - std::string compound_id() const; - char compound_letter() const; // Single letter for residue compound type, or 'X' in case it is not known + [[nodiscard]] std::string asym_id() const; + [[nodiscard]] int seq_id() const; + [[nodiscard]] std::string alt_id() const; + [[nodiscard]] std::string compound_id() const; + [[nodiscard]] char compound_letter() const; // Single letter for residue compound type, or 'X' in case it is not known - std::string auth_asym_id() const; - int auth_seq_id() const; + [[nodiscard]] std::string auth_asym_id() const; + [[nodiscard]] int auth_seq_id() const; - std::string pdb_strand_id() const; - int pdb_seq_num() const; - std::string pdb_ins_code() const; + [[nodiscard]] std::string pdb_strand_id() const; + [[nodiscard]] int pdb_seq_num() const; + [[nodiscard]] std::string pdb_ins_code() const; - std::optional alpha() const; - std::optional kappa() const; - std::optional phi() const; - std::optional psi() const; - std::optional tco() const; - std::optional omega() const; + [[nodiscard]] std::optional alpha() const; + [[nodiscard]] std::optional kappa() const; + [[nodiscard]] std::optional phi() const; + [[nodiscard]] std::optional psi() const; + [[nodiscard]] std::optional tco() const; + [[nodiscard]] std::optional omega() const; - bool is_pre_pro() const; - bool is_cis() const { return std::abs(omega().value_or(360)) < 30.0f; } + [[nodiscard]] bool is_pre_pro() const; + [[nodiscard]] bool is_cis() const { return std::abs(omega().value_or(360)) < 30.0f; } - float chiral_volume() const; + [[nodiscard]] float chiral_volume() const; - std::size_t nr_of_chis() const; - float chi(std::size_t index) const; + [[nodiscard]] std::size_t nr_of_chis() const; + [[nodiscard]] float chi(std::size_t index) const; - std::vector chis() const + [[nodiscard]] std::vector chis() const { std::vector result; for (size_t i = 0; i < nr_of_chis(); ++i) @@ -158,34 +160,34 @@ class dssp return result; } - std::tuple ca_location() const; + [[nodiscard]] std::tuple ca_location() const; - chain_break_type chain_break() const; + [[nodiscard]] chain_break_type chain_break() const; /// \brief the internal number in DSSP - int nr() const; + [[nodiscard]] int nr() const; - structure_type type() const; + [[nodiscard]] structure_type type() const; - int ssBridgeNr() const; + [[nodiscard]] int ssBridgeNr() const; - helix_position_type helix(helix_type helixType) const; + [[nodiscard]] helix_position_type helix(helix_type helixType) const; - bool is_alpha_helix_end_before_start() const; + [[nodiscard]] bool is_alpha_helix_end_before_start() const; - bool bend() const; + [[nodiscard]] bool bend() const; - double accessibility() const; + [[nodiscard]] double accessibility() const; /// \brief returns resinfo, ladder and parallel - std::tuple bridge_partner(int i) const; + [[nodiscard]] std::tuple bridge_partner(int i) const; - int sheet() const; - int strand() const; + [[nodiscard]] int sheet() const; + [[nodiscard]] int strand() const; /// \brief return resinfo and the energy of the bond - std::tuple acceptor(int i) const; - std::tuple donor(int i) const; + [[nodiscard]] std::tuple acceptor(int i) const; + [[nodiscard]] std::tuple donor(int i) const; /// \brief Simple compare equals bool operator==(const residue_info &rhs) const @@ -196,7 +198,7 @@ class dssp /// \brief Returns \result true if there is a bond between two residues friend bool test_bond(residue_info const &a, residue_info const &b); - residue_info next() const; + [[nodiscard]] residue_info next() const; private: residue_info(residue *res) @@ -211,17 +213,18 @@ class dssp { public: using iterator_category = std::bidirectional_iterator_tag; - using value_type = residue_info; + using value_type = const residue_info; using difference_type = std::ptrdiff_t; using pointer = value_type *; using reference = value_type &; + iterator() = default; iterator(const iterator &i) = default; iterator(residue *res); iterator &operator=(const iterator &i) = default; - reference operator*() { return m_current; } - pointer operator->() { return &m_current; } + reference operator*() const { return m_current; } + pointer operator->() const { return &m_current; } iterator &operator++(); iterator operator++(int) @@ -246,17 +249,22 @@ class dssp residue_info m_current; }; + static_assert(std::input_iterator); + using value_type = residue_info; // To access residue info by key, i.e. LabelAsymID and LabelSeqID using key_type = std::tuple; - iterator begin() const; - iterator end() const; + [[nodiscard]] iterator begin() const; + [[nodiscard]] iterator end() const; + + [[nodiscard]] iterator cbegin() const; + [[nodiscard]] iterator cend() const; residue_info operator[](const key_type &key) const; - bool empty() const { return begin() == end(); } + [[nodiscard]] bool empty() const { return begin() == end(); } // -------------------------------------------------------------------- // Writing out the data, either in legacy format... @@ -276,8 +284,10 @@ class dssp AUTHOR }; - std::string get_pdb_header_line(pdb_record_type pdb_record) const; + [[nodiscard]] std::string get_pdb_header_line(pdb_record_type pdb_record) const; private: struct DSSP_impl *m_impl; }; + +static_assert(std::ranges::input_range); \ No newline at end of file diff --git a/libdssp/src/dssp-io.cpp b/libdssp/src/dssp-io.cpp index 16d9a85..818529e 100644 --- a/libdssp/src/dssp-io.cpp +++ b/libdssp/src/dssp-io.cpp @@ -25,14 +25,13 @@ */ #include "dssp-io.hpp" + #include "revision.hpp" +#include #include #include - -#include -#include -#include +#include #include // -------------------------------------------------------------------- @@ -121,20 +120,20 @@ std::string ResidueToDSSPLine(const dssp::residue_info &info) if (acceptor) { auto d = acceptor.nr() - info.nr(); - NHO[i] = cif::format("{:d},{:3.1f}", d, acceptorE); + NHO[i] = std::format("{:d},{:3.1f}", d, acceptorE); } if (donor) { auto d = donor.nr() - info.nr(); - ONH[i] = cif::format("{:d},{:3.1f}", d, donorE); + ONH[i] = std::format("{:d},{:3.1f}", d, donorE); } } // auto ca = residue.atomByID("CA"); auto const &[cax, cay, caz] = residue.ca_location(); - return cif::format("{:5d}{:5d}{:1.1s}{:1.1s} {:1c} {:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:4d}{:4d}{:1c}{:4.0f} {:>11s}{:>11s}{:>11s}{:>11s} {:6.3f}{:6.1f}{:6.1f}{:6.1f}{:6.1f} {:6.1f} {:6.1f} {:6.1f}", + return std::format("{:5d}{:5d}{:1.1s}{:1.1s} {:1c} {:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:1c}{:4d}{:4d}{:1c}{:4.0f} {:>11s}{:>11s}{:>11s}{:>11s} {:6.3f}{:6.1f}{:6.1f}{:6.1f}{:6.1f} {:6.1f} {:6.1f} {:6.1f}", info.nr(), residue.pdb_seq_num(), residue.pdb_ins_code(), residue.pdb_strand_id(), code, ss, helix[3], helix[0], helix[1], helix[2], bend, chirality, bridgelabel[0], bridgelabel[1], bp[0], bp[1], sheet, floor(info.accessibility() + 0.5), @@ -167,40 +166,40 @@ void writeDSSP(const dssp &dssp, std::ostream &os) << dssp.get_pdb_header_line(dssp::pdb_record_type::SOURCE) << '.' << std::endl << dssp.get_pdb_header_line(dssp::pdb_record_type::AUTHOR) << '.' << std::endl; - os << cif::format("{:5d}{:3d}{:3d}{:3d}{:3d} TOTAL NUMBER OF RESIDUES, NUMBER OF CHAINS, NUMBER OF SS-BRIDGES(TOTAL,INTRACHAIN,INTERCHAIN) .", + os << std::format("{:5d}{:3d}{:3d}{:3d}{:3d} TOTAL NUMBER OF RESIDUES, NUMBER OF CHAINS, NUMBER OF SS-BRIDGES(TOTAL,INTRACHAIN,INTERCHAIN) .", stats.count.residues, stats.count.chains, stats.count.SS_bridges, stats.count.intra_chain_SS_bridges, (stats.count.SS_bridges - stats.count.intra_chain_SS_bridges)) << std::endl; - os << cif::format("{:8.1f} ACCESSIBLE SURFACE OF PROTEIN (ANGSTROM**2) .", stats.accessible_surface) << std::endl; + os << std::format("{:8.1f} ACCESSIBLE SURFACE OF PROTEIN (ANGSTROM**2) .", stats.accessible_surface) << std::endl; // hydrogenbond summary - os << cif::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS OF TYPE O(I)-->H-N(J) , SAME NUMBER PER 100 RESIDUES .", stats.count.H_bonds, (stats.count.H_bonds * 100.0 / stats.count.residues)) << std::endl; + os << std::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS OF TYPE O(I)-->H-N(J) , SAME NUMBER PER 100 RESIDUES .", stats.count.H_bonds, (stats.count.H_bonds * 100.0 / stats.count.residues)) << std::endl; - os << cif::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS IN PARALLEL BRIDGES, SAME NUMBER PER 100 RESIDUES .", stats.count.H_bonds_in_parallel_bridges, (stats.count.H_bonds_in_parallel_bridges * 100.0 / stats.count.residues)) << std::endl; + os << std::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS IN PARALLEL BRIDGES, SAME NUMBER PER 100 RESIDUES .", stats.count.H_bonds_in_parallel_bridges, (stats.count.H_bonds_in_parallel_bridges * 100.0 / stats.count.residues)) << std::endl; - os << cif::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS IN ANTIPARALLEL BRIDGES, SAME NUMBER PER 100 RESIDUES .", stats.count.H_bonds_in_antiparallel_bridges, (stats.count.H_bonds_in_antiparallel_bridges * 100.0 / stats.count.residues)) << std::endl; + os << std::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS IN ANTIPARALLEL BRIDGES, SAME NUMBER PER 100 RESIDUES .", stats.count.H_bonds_in_antiparallel_bridges, (stats.count.H_bonds_in_antiparallel_bridges * 100.0 / stats.count.residues)) << std::endl; for (int k = 0; k < 11; ++k) - os << cif::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS OF TYPE O(I)-->H-N(I{:1c}{:1d}), SAME NUMBER PER 100 RESIDUES .", stats.count.H_Bonds_per_distance[k], (stats.count.H_Bonds_per_distance[k] * 100.0 / stats.count.residues), (k - 5 < 0 ? '-' : '+'), abs(k - 5)) << std::endl; + os << std::format("{:5d}{:5.1f} TOTAL NUMBER OF HYDROGEN BONDS OF TYPE O(I)-->H-N(I{:1c}{:1d}), SAME NUMBER PER 100 RESIDUES .", stats.count.H_Bonds_per_distance[k], (stats.count.H_Bonds_per_distance[k] * 100.0 / stats.count.residues), (k - 5 < 0 ? '-' : '+'), abs(k - 5)) << std::endl; // histograms... os << " 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 *** HISTOGRAMS OF *** ." << std::endl; for (auto hi : stats.histogram.residues_per_alpha_helix) - os << cif::format("{:3d}", hi); + os << std::format("{:3d}", hi); os << " RESIDUES PER ALPHA HELIX ." << std::endl; for (auto hi : stats.histogram.parallel_bridges_per_ladder) - os << cif::format("{:3d}", hi); + os << std::format("{:3d}", hi); os << " PARALLEL BRIDGES PER LADDER ." << std::endl; for (auto hi : stats.histogram.antiparallel_bridges_per_ladder) - os << cif::format("{:3d}", hi); + os << std::format("{:3d}", hi); os << " ANTIPARALLEL BRIDGES PER LADDER ." << std::endl; for (auto hi : stats.histogram.ladders_per_sheet) - os << cif::format("{:3d}", hi); + os << std::format("{:3d}", hi); os << " LADDERS PER SHEET ." << std::endl; // per residue information @@ -214,7 +213,7 @@ void writeDSSP(const dssp &dssp, std::ostream &os) // can be the transition to a different chain, or missing residues in the current chain if (ri.nr() != last + 1) - os << cif::format("{:5d} !{:1c} 0 0 0 0, 0.0 0, 0.0 0, 0.0 0, 0.0 0.000 360.0 360.0 360.0 360.0 0.0 0.0 0.0", + os << std::format("{:5d} !{:1c} 0 0 0 0, 0.0 0, 0.0 0, 0.0 0, 0.0 0.000 360.0 360.0 360.0 360.0 0.0 0.0 0.0", (last + 1), (ri.chain_break() == dssp::chain_break_type::NewChain ? '*' : ' ')) << std::endl; @@ -329,9 +328,8 @@ void writeSheets(cif::datablock &db, const dssp &dssp) // clean up old info first for (auto sheet_cat : { "struct_sheet", "struct_sheet_order", "struct_sheet_range", "struct_sheet_hbond", "pdbx_struct_sheet_hbond" }) - db.erase(remove_if(db.begin(), db.end(), [sheet_cat](const cif::category &cat) - { return cat.name() == sheet_cat; }), - db.end()); + std::erase_if(db, [sheet_cat](const cif::category &cat) + { return cat.name() == sheet_cat; }); // create a list of strands, based on the SS info in DSSP. Store sheet number along with the strand. @@ -370,7 +368,7 @@ void writeSheets(cif::datablock &db, const dssp &dssp) std::string strandID = cif::cif_id_for_number(strand.front().strand() - 1); - std::sort(strand.begin(), strand.end(), [](dssp::residue_info const &a, dssp::residue_info const &b) + std::ranges::sort(strand, [](dssp::residue_info const &a, dssp::residue_info const &b) { return a.nr() < b.nr(); }); auto &beg = strand.front(); @@ -458,7 +456,7 @@ void writeLadders(cif::datablock &db, const dssp &dssp) } } - std::sort(ladders.begin(), ladders.end()); + std::ranges::sort(ladders, std::less<>()); auto &dssp_struct_ladder = db["dssp_struct_ladder"]; @@ -746,7 +744,7 @@ void annotateDSSP(cif::datablock &db, const dssp &dssp, bool writeOther, bool wr if (audit_conform.empty()) { auto &cf = cif::validator_factory::instance(); - cf.get("mmcif_pdbx.dic").fill_audit_conform(audit_conform); + cf.get("mmcif_pdbx.dic")->fill_audit_conform(audit_conform); } audit_conform.erase(cif::key("dict_name") == "dssp-extension.dic"); @@ -771,10 +769,10 @@ void annotateDSSP(cif::datablock &db, const dssp &dssp, bool writeOther, bool wr "dssp_statistics", "dssp_statistics_hbond", "dssp_statistics_histogram", - "dssp_struct_summary" - }) + "dssp_struct_summary" }) { - db.erase(std::remove_if(db.begin(), db.end(), [cat] (cif::category &c) { return c.name() == cat; }), db.end()); + std::erase_if(db, [cat](cif::category &c) + { return c.name() == cat; }); } if (writeNewFormat) diff --git a/libdssp/src/dssp.cpp b/libdssp/src/dssp.cpp index 37e3efa..81a5679 100644 --- a/libdssp/src/dssp.cpp +++ b/libdssp/src/dssp.cpp @@ -30,8 +30,11 @@ #include "dssp-io.hpp" +#include #include #include +#include +#include #include #include @@ -49,7 +52,7 @@ using chain_break_type = dssp::chain_break_type; // -------------------------------------------------------------------- const double - kPI = 3.141592653589793238462643383279502884; + kPI = std::numbers::pi; struct point { @@ -263,7 +266,6 @@ struct dssp::residue : mPDBStrandID(pdb_strand_id) , mPDBSeqNum(pdb_seq_num) , mPDBInsCode(pdb_ins_code) - , mChainBreak(chain_break_type::None) , m_model_nr(model_nr) { // update the box containing all atoms @@ -393,7 +395,7 @@ struct dssp::residue auto pc = mPrev->mC; auto po = mPrev->mO; - float CODistance = static_cast(distance(pc, po)); + auto CODistance = static_cast(distance(pc, po)); mH.mX += (pc.mX - po.mX) / CODistance; mH.mY += (pc.mY - po.mY) / CODistance; @@ -402,7 +404,7 @@ struct dssp::residue } void SetSecondaryStructure(structure_type inSS) { mSecondaryStructure = inSS; } - structure_type GetSecondaryStructure() const { return mSecondaryStructure; } + [[nodiscard]] structure_type GetSecondaryStructure() const { return mSecondaryStructure; } void SetBetaPartner(uint32_t n, residue &inResidue, uint32_t inLadder, bool inParallel) { @@ -413,38 +415,38 @@ struct dssp::residue mBetaPartner[n].parallel = inParallel; } - bridge_partner GetBetaPartner(uint32_t n) const + [[nodiscard]] bridge_partner GetBetaPartner(uint32_t n) const { assert(n == 0 or n == 1); return mBetaPartner[n]; } void SetSheet(uint32_t inSheet) { mSheet = inSheet; } - uint32_t GetSheet() const { return mSheet; } + [[nodiscard]] uint32_t GetSheet() const { return mSheet; } void SetStrand(uint32_t inStrand) { mStrand = inStrand; } - uint32_t GetStrand() const { return mStrand; } + [[nodiscard]] uint32_t GetStrand() const { return mStrand; } - bool IsBend() const { return mBend; } + [[nodiscard]] bool IsBend() const { return mBend; } void SetBend(bool inBend) { mBend = inBend; } - helix_position_type GetHelixFlag(helix_type helixType) const + [[nodiscard]] helix_position_type GetHelixFlag(helix_type helixType) const { - size_t stride = static_cast(helixType); + auto stride = static_cast(helixType); assert(stride < 4); return mHelixFlags[stride]; } - bool IsHelixStart(helix_type helixType) const + [[nodiscard]] bool IsHelixStart(helix_type helixType) const { - size_t stride = static_cast(helixType); + auto stride = static_cast(helixType); assert(stride < 4); return mHelixFlags[stride] == helix_position_type::Start or mHelixFlags[stride] == helix_position_type::StartAndEnd; } void SetHelixFlag(helix_type helixType, helix_position_type inHelixFlag) { - size_t stride = static_cast(helixType); + auto stride = static_cast(helixType); assert(stride < 4); mHelixFlags[stride] = inHelixFlag; } @@ -456,7 +458,7 @@ struct dssp::residue mSSBridgeNr = inBridgeNr; } - uint8_t GetSSBridgeNr() const + [[nodiscard]] uint8_t GetSSBridgeNr() const { if (mType != kCysteine) throw std::runtime_error("Only cysteine residues can form sulphur bridges"); @@ -466,7 +468,7 @@ struct dssp::residue float CalculateSurface(const std::vector &inResidues); float CalculateSurface(const point &inAtom, float inRadius, const std::vector &inNeighbours); - bool AtomIntersectsBox(const point &atom, float inRadius) const + [[nodiscard]] bool AtomIntersectsBox(const point &atom, float inRadius) const { return atom.mX + inRadius >= mBox[0].mX and atom.mX - inRadius <= mBox[1].mX and @@ -572,9 +574,9 @@ class accumulator double radius; double distance; - bool operator<(const candidate &rhs) const + auto operator<=>(const candidate &rhs) const { - return distance < rhs.distance; + return distance <=> rhs.distance; } }; @@ -593,13 +595,13 @@ class accumulator candidate c = { b - a, r * r, distance }; m_x.push_back(c); - push_heap(m_x.begin(), m_x.end()); + std::ranges::push_heap(m_x, std::less<>()); } } void sort() { - sort_heap(m_x.begin(), m_x.end()); + std::ranges::sort_heap(m_x, std::less<>()); } std::vector m_x; @@ -611,9 +613,9 @@ class MSurfaceDots public: static MSurfaceDots &Instance(); - size_t size() const { return mPoints.size(); } + [[nodiscard]] size_t size() const { return mPoints.size(); } const point &operator[](size_t inIx) const { return mPoints[inIx]; } - double weight() const { return mWeight; } + [[nodiscard]] double weight() const { return mWeight; } private: MSurfaceDots(int32_t inN); @@ -634,14 +636,14 @@ MSurfaceDots::MSurfaceDots(int32_t N) { auto P = 2 * N + 1; - const float kGoldenRatio = (1 + std::sqrt(5.0f)) / 2; + const float kGoldenRatio = std::numbers::phi_v; mWeight = (4 * kPI) / P; for (auto i = -N; i <= N; ++i) { float lat = std::asin((2.0f * i) / P); - float lon = static_cast(std::fmod(i, kGoldenRatio) * 2 * kPI / kGoldenRatio); + auto lon = static_cast(std::fmod(i, kGoldenRatio) * 2 * kPI / kGoldenRatio); mPoints.emplace_back(point{ std::sin(lon) * std::cos(lat), std::cos(lon) * std::cos(lat), std::sin(lat) }); } @@ -778,8 +780,8 @@ double CalculateHBondEnergy(residue &inDonor, residue &inAcceptor) void CalculateHBondEnergies(std::vector &inResidues, std::vector> &q) { std::unique_ptr progress; - if (cif::VERBOSE == 0 or cif::VERBOSE == 1) - progress.reset(new cif::progress_bar(q.size(), "calculate hbond energies")); + if (cif::VERBOSE >= 0) + progress = std::make_unique(q.size(), "calculate hbond energies"); for (const auto &[i, j] : q) { @@ -873,8 +875,8 @@ void CalculateBetaSheets(std::vector &inResidues, statistics &stats, st // std::cerr << "calculating beta sheets" << std::endl; std::unique_ptr progress; - if (cif::VERBOSE == 0 or cif::VERBOSE == 1) - progress.reset(new cif::progress_bar(q.size(), "calculate beta sheets")); + if (cif::VERBOSE >= 0) + progress = std::make_unique(q.size(), "calculate beta sheets"); // Calculate Bridges std::vector bridges; @@ -929,7 +931,7 @@ void CalculateBetaSheets(std::vector &inResidues, statistics &stats, st } // extend ladders - std::sort(bridges.begin(), bridges.end()); + std::ranges::sort(bridges, std::less<>()); for (uint32_t i = 0; i < bridges.size(); ++i) { @@ -1069,7 +1071,7 @@ void CalculateBetaSheets(std::vector &inResidues, statistics &stats, st { stats.count.H_bonds_in_parallel_bridges += bridge.i.back() - bridge.i.front() + 2; - std::deque::iterator j = bridge.j.begin(); + auto j = bridge.j.begin(); for (uint32_t i : bridge.i) inResidues[i].SetBetaPartner(betai, inResidues[*j++], bridge.ladder, true); @@ -1081,7 +1083,7 @@ void CalculateBetaSheets(std::vector &inResidues, statistics &stats, st { stats.count.H_bonds_in_antiparallel_bridges += bridge.i.back() - bridge.i.front() + 2; - std::deque::reverse_iterator j = bridge.j.rbegin(); + auto j = bridge.j.rbegin(); for (uint32_t i : bridge.i) inResidues[i].SetBetaPartner(betai, inResidues[*j++], bridge.ladder, false); @@ -1390,7 +1392,7 @@ struct DSSP_impl auto findRes(const std::string &asymID, int seqID) { - return std::find_if(mResidues.begin(), mResidues.end(), [&](auto &r) + return std::ranges::find_if(mResidues, [&](auto &r) { return r.mAsymID == asymID and r.mSeqID == seqID; }); } @@ -1454,9 +1456,8 @@ DSSP_impl::DSSP_impl(const cif::datablock &db, int model_nr, int min_poly_prolin for (auto &residue : mResidues) residue.finish(); - mResidues.erase(std::remove_if(mResidues.begin(), mResidues.end(), [](const dssp::residue &r) - { return not r.mComplete; }), - mResidues.end()); + std::erase_if(mResidues, [](const dssp::residue &r) + { return not r.mComplete; }); mStats.count.chains = 1; chain_break_type brk = chain_break_type::NewChain; @@ -1592,8 +1593,8 @@ void DSSP_impl::calculateSecondaryStructure() cAlphas.emplace_back(r.mCAlpha); std::unique_ptr progress; - if (cif::VERBOSE == 0 or cif::VERBOSE == 1) - progress.reset(new cif::progress_bar((mResidues.size() * (mResidues.size() - 1)) / 2, "calculate distances")); + if (cif::VERBOSE >= 0) + progress = std::make_unique((mResidues.size() * (mResidues.size() - 1)) / 2, "calculate distances"); // Calculate the HBond energies std::vector> near; @@ -1646,7 +1647,7 @@ void DSSP_impl::calculateSecondaryStructure() auto id = r.mAsymID + ':' + std::to_string(r.mSeqID) + '/' + r.mCompoundID; std::cerr << id << std::string(12 - id.length(), ' ') - << char(r.mSecondaryStructure) << ' ' + << static_cast(r.mSecondaryStructure) << ' ' << helix << std::endl; } @@ -1784,8 +1785,8 @@ std::string DSSP_impl::GetPDBHEADERLine() char header[] = "HEADER xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDDDDDDDDD IIII"; - std::copy(keywords.begin(), keywords.end(), header + 10); - std::copy(date.begin(), date.end(), header + 50); + std::ranges::copy(keywords, header + 10); + std::ranges::copy(date, header + 50); std::string id = mDB.name(); if (id.length() < 4) @@ -1793,7 +1794,7 @@ std::string DSSP_impl::GetPDBHEADERLine() else if (id.length() > 4) id.erase(id.begin() + 4, id.end()); - std::copy(id.begin(), id.end(), header + 62); + std::ranges::copy(id, header + 62); return FixStringLength(header); } @@ -1809,45 +1810,45 @@ std::string DSSP_impl::GetPDBCOMPNDLine() for (auto r : mDB["entity"].find("type"_key == "polymer")) { - std::string entityID = r["id"].as(); + auto entityID = r["id"].as(); ++molID; cmpnd.push_back("MOL_ID: " + std::to_string(molID)); - std::string molecule = r["pdbx_description"].as(); + auto molecule = r["pdbx_description"].as(); cmpnd.push_back("MOLECULE: " + molecule); auto poly = mDB["entity_poly"].find("entity_id"_key == entityID); if (not poly.empty()) { - std::string chains = poly.front()["pdbx_strand_id"].as(); + auto chains = poly.front()["pdbx_strand_id"].as(); cif::replace_all(chains, ",", ", "); cmpnd.push_back("CHAIN: " + chains); } - std::string fragment = r["pdbx_fragment"].as(); + auto fragment = r["pdbx_fragment"].as(); if (not fragment.empty()) cmpnd.push_back("FRAGMENT: " + fragment); for (auto sr : mDB["entity_name_com"].find("entity_id"_key == entityID)) { - std::string syn = sr["name"].as(); + auto syn = sr["name"].as(); if (not syn.empty()) cmpnd.push_back("SYNONYM: " + syn); } - std::string mutation = r["pdbx_mutation"].as(); + auto mutation = r["pdbx_mutation"].as(); if (not mutation.empty()) cmpnd.push_back("MUTATION: " + mutation); - std::string ec = r["pdbx_ec"].as(); + auto ec = r["pdbx_ec"].as(); if (not ec.empty()) cmpnd.push_back("EC: " + ec); if (r["src_method"] == "man" or r["src_method"] == "syn") - cmpnd.push_back("ENGINEERED: YES"); + cmpnd.emplace_back("ENGINEERED: YES"); - std::string details = r["details"].as(); + auto details = r["details"].as(); if (not details.empty()) cmpnd.push_back("OTHER_DETAILS: " + details); } @@ -1869,13 +1870,13 @@ std::string DSSP_impl::GetPDBSOURCELine() if (r["type"] != "polymer") continue; - std::string entityID = r["id"].as(); + auto entityID = r["id"].as(); ++molID; source.push_back("MOL_ID: " + std::to_string(molID)); if (r["src_method"] == "syn") - source.push_back("SYNTHETIC: YES"); + source.emplace_back("SYNTHETIC: YES"); auto &gen = mDB["entity_src_gen"]; const std::pair kGenSourceMapping[] = { @@ -1905,7 +1906,7 @@ std::string DSSP_impl::GetPDBSOURCELine() std::string cname, sname; tie(cname, sname) = m; - std::string s = gr[cname].as(); + auto s = gr[cname].as(); if (not s.empty()) source.push_back(sname + ": " + s); } @@ -1930,7 +1931,7 @@ std::string DSSP_impl::GetPDBSOURCELine() std::string cname, sname; tie(cname, sname) = m; - std::string s = nr[cname].as(); + auto s = nr[cname].as(); if (not s.empty()) source.push_back(sname + ": " + s); } @@ -2186,7 +2187,7 @@ std::tuple dssp::residue_info::donor(int i) const dssp::residue_info dssp::residue_info::next() const { - return residue_info(m_impl ? m_impl->mNext : nullptr); + return { m_impl ? m_impl->mNext : nullptr }; } // -------------------------------------------------------------------- @@ -2220,7 +2221,8 @@ dssp::dssp(const cif::datablock &db, int model_nr, int min_poly_proline_stretch, { if (calculateSurfaceAccessibility) { - std::thread t(std::bind(&DSSP_impl::calculateSurface, m_impl)); + std::thread t([this] + { m_impl->calculateSurface(); }); m_impl->calculateSecondaryStructure(); t.join(); } @@ -2235,7 +2237,7 @@ dssp::~dssp() dssp::iterator dssp::begin() const { - return iterator(m_impl->mResidues.empty() ? nullptr : m_impl->mResidues.data()); + return { m_impl->mResidues.empty() ? nullptr : m_impl->mResidues.data() }; } dssp::iterator dssp::end() const @@ -2248,12 +2250,12 @@ dssp::iterator dssp::end() const res += m_impl->mResidues.size(); } - return iterator(res); + return { res }; } dssp::residue_info dssp::operator[](const key_type &key) const { - auto i = std::find_if(begin(), end(), + auto i = std::ranges::find_if(*this, [key](const residue_info &res) { return res.asym_id() == std::get<0>(key) and res.seq_id() == std::get<1>(key); }); diff --git a/python-module/CMakeLists.txt b/python-module/CMakeLists.txt index 12b0a9f..e5588fc 100644 --- a/python-module/CMakeLists.txt +++ b/python-module/CMakeLists.txt @@ -29,7 +29,9 @@ if(CMAKE_VERSION GREATER_EQUAL 3.30) endif() find_package(Python REQUIRED COMPONENTS Interpreter Development) -find_package(Boost 1.83 QUIET COMPONENTS python) + +# Boost 1.84 is required since it contains code required to use numpy 2.x +find_package(Boost 1.84 QUIET COMPONENTS python) if(NOT Boost_FOUND) # boost is a huge project and directly downloading the 'alternate release' diff --git a/python-module/dssp-python-plugin.cpp b/python-module/dssp-python-plugin.cpp index 0ea967e..727a8d5 100644 --- a/python-module/dssp-python-plugin.cpp +++ b/python-module/dssp-python-plugin.cpp @@ -2,6 +2,7 @@ #include #include +#include struct statistics_wrapper { @@ -42,7 +43,7 @@ class dssp_wrapper std::istream is(&buffer); auto f = cif::pdb::read(is); - m_dssp.reset(new dssp(f.front(), model_nr, min_poly_proline_stretch_length, calculateSurfaceAccessibility)); + m_dssp = std::make_shared(f.front(), model_nr, min_poly_proline_stretch_length, calculateSurfaceAccessibility); } dssp_wrapper(const dssp_wrapper &) = default; @@ -83,7 +84,7 @@ class dssp_wrapper iterator(const iterator &i) = default; iterator &operator=(const iterator &i) = default; - reference operator*() { return residue_info_handle(m_current); } + reference operator*() { return {m_current}; } pointer operator->() { return &m_current; } iterator &operator++() @@ -126,12 +127,12 @@ class dssp_wrapper // Custom exceptions struct AttributeError : std::exception { - const char *what() const throw() { return "AttributeError exception"; } + [[nodiscard]] const char *what() const noexcept override { return "AttributeError exception"; } }; struct TypeError : std::exception { - const char *what() const throw() { return "TypeError exception"; } + [[nodiscard]] const char *what() const noexcept override { return "TypeError exception"; } }; // Set python exceptions diff --git a/test/unit-test-dssp.cpp b/test/unit-test-dssp.cpp index a361db4..4329a03 100644 --- a/test/unit-test-dssp.cpp +++ b/test/unit-test-dssp.cpp @@ -24,16 +24,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include - #define CATCH_CONFIG_RUNNER -#include - #include "../libdssp/src/dssp-io.hpp" #include "../src/revision.hpp" #include "dssp.hpp" +#include #include namespace fs = std::filesystem; @@ -56,10 +53,12 @@ cif::file operator""_cf(const char *text, size_t length) // -------------------------------------------------------------------- -std::filesystem::path gTestDir = std::filesystem::current_path(); +std::filesystem::path gTestDir; int main(int argc, char *argv[]) { + gTestDir = std::filesystem::current_path(); + Catch::Session session; // There must be exactly one instance // Build a new parser on top of Catch2's @@ -156,8 +155,6 @@ TEST_CASE("ut_mmcif_2") dssp dssp(f.front(), 1, 3, true); - std::stringstream test; - dssp.annotate(f.front(), true, true); cif::file rf(gTestDir / "1cbs-dssp.cif"); @@ -166,12 +163,12 @@ TEST_CASE("ut_mmcif_2") auto &db2 = rf.front(); db1["software"].erase("name"_key == "dssp"); - db1.erase(find_if(db1.begin(), db1.end(), [](cif::category &cat) - { return cat.name() == "audit_conform"; })); + std::erase_if(db1, [](cif::category &cat) + { return cat.name() == "audit_conform"; }); db2["software"].erase("name"_key == "dssp"); - db2.erase(find_if(db2.begin(), db2.end(), [](cif::category &cat) - { return cat.name() == "audit_conform"; })); + std::erase_if(db2, [](cif::category &cat) + { return cat.name() == "audit_conform"; }); // generate some output on different files: // cif::VERBOSE = 2;