From 1bcd71732e6be3473dabbea23572ce3c4e0f9302 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 21 Aug 2019 15:56:38 -0400 Subject: [PATCH 01/62] DRR - Cpptraj: Start new DSSP action that more closely follows the definitions of Kabsch and Sander --- src/Action_DSSP2.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++ src/Action_DSSP2.h | 102 ++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 src/Action_DSSP2.cpp create mode 100644 src/Action_DSSP2.h diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp new file mode 100644 index 0000000000..fbf0abc77c --- /dev/null +++ b/src/Action_DSSP2.cpp @@ -0,0 +1,171 @@ +#include // std::fill +#include "Action_DSSP2.h" +#include "CpptrajStdio.h" + +// ----- SSres ----------------------------------------------------------------- +Action_DSSP2::SSres::SSres() : + resDataSet_(0), + chirality_(0), + pattern_(NOHBOND), + sstype_(NONE), + idx_(-1), + C_(-1), + O_(-1), + N_(-1), + H_(-1), + CA_(-1), + isSelected_(false) +{ + std::fill(SScount_, SScount_ + NSSTYPE_, 0); +} + +void Action_DSSP2::SSres::Deselect() { + isSelected_ = false; + C_ = -1; + H_ = -1; + N_ = -1; + O_ = -1; + CA_ = -1; +} + +// ----- Action_DSSP2 ---------------------------------------------------------- + +/** From the original Kabsch & Sander 1983 paper. Obtained via + * q1 = 0.42e + * q2 = 0.20e + * f = 332 (approximate conversion factor to get energies in kcal/mol) + * fac = q1*q2*f + */ +const double Action_DSSP2::DSSP_fac_ = 27.888; + +const double Action_DSSP2::DSSP_cut_ = -0.5; + +const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; + +const char* Action_DSSP2::SSchar_[] = { "0", "b", "B", "G", "H", "I", "T", "S" }; + +const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; + +const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"; + +Action_DSSP2::Action_DSSP2() : + debug_(0), + outfile_(0), + dsspFile_(0), + assignout_(0) +{} + +// Action_DSSP2::Help() +void Action_DSSP2::Help() const { + mprintf("\t[] [out ] [] [sumout ]\n" + "\t[assignout ] [totalout ] [ptrajformat]\n" + "\t[namen ] [nameh ] [nameca ]\n" + "\t[namec ] [nameo ]\n" + " Calculate secondary structure content for residues in .\n" + " If sumout not specified, the filename specified by out is used with .sum suffix.\n"); +} + +// Action_DSSP2::Init() +Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int debugIn) +{ + debug_ = debugIn; + //Nframe_ = 0; + // Get keywords + outfile_ = init.DFL().AddDataFile( actionArgs.GetStringKey("out"), actionArgs ); + std::string temp = actionArgs.GetStringKey("sumout"); + if (temp.empty() && outfile_ != 0) + temp = outfile_->DataFilename().Full() + ".sum"; + dsspFile_ = init.DFL().AddDataFile( temp ); + DataFile* totalout = init.DFL().AddDataFile( actionArgs.GetStringKey("totalout"), actionArgs ); + assignout_ = init.DFL().AddCpptrajFile(actionArgs.GetStringKey("assignout"), "SS assignment"); + printString_ = actionArgs.hasKey("ptrajformat"); + temp = actionArgs.GetStringKey("namen"); + if (!temp.empty()) BB_N_ = temp; + temp = actionArgs.GetStringKey("nameh"); + if (!temp.empty()) BB_H_ = temp; + temp = actionArgs.GetStringKey("namec"); + if (!temp.empty()) BB_C_ = temp; + temp = actionArgs.GetStringKey("nameo"); + if (!temp.empty()) BB_O_ = temp; + temp = actionArgs.GetStringKey("nameca"); + if (!temp.empty()) BB_CA_ = temp; + // Get masks + if (Mask_.SetMaskString( actionArgs.GetMaskNext() )) return Action::ERR; + + // Set up the DSSP data set + dsetname_ = actionArgs.GetStringNext(); + // Set default name if none specified + if (dsetname_.empty()) + dsetname_ = init.DSL().GenerateDefaultName("DSSP"); + // Set up Z labels + if (outfile_ != 0) + outfile_->ProcessArgs(SSzlabels_); + // Create data sets for total fraction SS vs time. + for (int i = 0; i < NSSTYPE_; i++) { + totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, SSname_[i])); + if (totalDS_[i] == 0) { + mprinterr("Error: Could not create DSSP total frac v time data set.\n"); + return Action::ERR; + } + // For now dont add 'None' so colors match up. + if (totalout != 0 && i > 0) totalout->AddDataSet( totalDS_[i] ); + } + + mprintf( " SECSTRUCT: Calculating secondary structure using mask [%s]\n",Mask_.MaskString()); + if (outfile_ != 0) + mprintf("\tDumping results to %s\n", outfile_->DataFilename().full()); + if (dsspFile_ != 0) + mprintf("\tSum results to %s\n", dsspFile_->DataFilename().full()); + if (printString_) { + mprintf("\tSS data for each residue will be stored as a string.\n"); + for (int i = 0; i < NSSTYPE_; i++) + mprintf("\t\t%s = %s\n", SSchar_[i], SSname_[i]); + } else { + mprintf("\tSS data for each residue will be stored as integers.\n"); + for (int i = 0; i < NSSTYPE_; i++) + mprintf("\t\t%i = %s\n", i, SSname_[i]); + } + if (assignout_ != 0) + mprintf("\tOverall assigned SS will be written to %s\n", assignout_->Filename().full()); + mprintf("\tBackbone Atom Names: N=[%s] H=[%s] C=[%s] O=[%s] CA=[%s]\n", + *BB_N_, *BB_H_, *BB_C_, *BB_O_, *BB_CA_ ); + mprintf("# Citation: Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure:\n" + "# Pattern Recognition of Hydrogen-Bonded and Geometrical Features.\"\n" + "# Biopolymers (1983), V.22, pp.2577-2637.\n" ); + init.DSL().SetDataSetsPending(true); + Init_ = init; + return Action::OK; +} + +// Action_DSSP2::Setup() +/** Set up secondary structure calculation for all residues selected by the + * mask. A residue is selected if at least one atom in the residue is + * selected by the mask. The coordinate indices (i.e. atom # * 3) for + * the C, O, N, H, and CA atoms are set up if those atoms are present. + * The residue is only initialized if it has not been previously selected + * and set up by a prior call to setup. + */ +Action::RetType Action_DSSP2::Setup(ActionSetup& setup) +{ + // Set up mask for this parm + if ( setup.Top().SetupIntegerMask( Mask_ ) ) return Action::ERR; + if ( Mask_.None() ) { + mprintf("Warning: DSSP: Mask has no atoms.\n"); + return Action::SKIP; + } + // Deselect any existing residues + for (SSarrayType::iterator it = Residues_.begin(); it != Residues_.end(); ++it) + it->Deselect(); + // Set up for each solute residue + Range soluteRes = setup.Top().SoluteResidues(); + mprintf("\tSetting up for %i solute residues.\n", soluteRes.Size()); + if ((unsigned int)soluteRes.Size() > Residues_.size()) + Residues_.resize( soluteRes.Size() ); + +} + +// Action_DSSP2::DoAction() +Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) +{ + +} diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h new file mode 100644 index 0000000000..2c521d8584 --- /dev/null +++ b/src/Action_DSSP2.h @@ -0,0 +1,102 @@ +#ifndef INC_ACTION_DSSP2_H +#define INC_ACTION_DSSP2_H +#include +#include +#include "Action.h" +#include "NameType.h" +#include "AtomMask.h" +/// +/** Based on protein secondary structure definitions given in: + * Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure: + * Pattern Recognition of Hydrogen-Bonded and Geometrical Features. + * Biopolymers (1983), V.22, pp.2577-2637. + * The various secondary structure types that can be assigned (in order + * of decreasing priority) are: + * H - Alpha helix (4) + * B - Single bridge (ladder of length 1) + * E - Beta strand (ladder length > 1) + * G - 3-10 helix (3) + * I - Pi helix (5) + * T - Turn (3, 4, 5) + * S - Bend (Angle i-2, i, i+2 > 70 deg.) + */ +class Action_DSSP2 : public Action { + public: + Action_DSSP2(); + DispatchObject* Alloc() const { return (DispatchObject*)new Action_DSSP2(); } + void Help() const; + private: + Action::RetType Init(ArgList&, ActionInit&, int); + Action::RetType Setup(ActionSetup&); + Action::RetType DoAction(int, ActionFrame&); + void Print() {} + + class ElemHbond; + class SSres; + + /// Secondary structure types + enum SStype { NONE=0, EXTENDED, BRIDGE, H3_10, ALPHA, HPI, TURN, BEND }; + static const int NSSTYPE_ = 8; ///< # of secondary structure types. + static const char DSSP_char_[]; ///< DSSP 1 char names corresponding to SStype + static const char* SSname_[]; ///< Full secondary structure names corresponding to SStype + static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype + static const std::string SSzlabels_; ///< Output graph Z labels corresponding to SStype + + /// Elementary hydrogen bond pattern + enum PatternType { + NOHBOND = 0, ///< No hydrogen bond + TURNBEG, ///< Start of n-Turn, > + TURNEND, ///< End of n-Turn, < + TURNX, ///< Coincidence of TURNBEG and TURNEND, X + TURN3, ///< Inside i to i + 3 turn (T) + TURN4, ///< Inside i to i + 4 turn (T) + TURN5, ///< Inside i to i + 5 turn (T) + BRIDGEPARA, ///< (i-1,j) and (j,i+1) or (j-1,i) and (i,j+1), lower case + BRIDGEANTI ///< (i,j) and (j,i) or (i-1,j+1) and (j-1,i+1), upper case + }; + + static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" + static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol + + typedef std::vector SSarrayType; + + int debug_; ///< Action debug level + SSarrayType Residues_; ///< Hold SS data for all residues. + NameType BB_N_; ///< Protein N atom name ('N') + NameType BB_H_; ///< Protein N-H atom name ('H') + NameType BB_C_; ///< Protein C atom name ('C') + NameType BB_O_; ///< Protein C-O atom name ('O') + NameType BB_CA_; ///< Protein alpha C name ('CA') + AtomMask Mask_; ///< Mask used to determine selected residues. + DataFile* outfile_; ///< Output Data file + DataFile* dsspFile_; ///< Sum output file + CpptrajFile* assignout_; ///< Assignment output file. + std::string dsetname_; ///< DSSP data set name + DataSet* totalDS_[NSSTYPE_]; ///< Hold total SS each frame for each SS type + ActionInit Init_; ///< Hold pointers to master DSL/DFL + bool printString_; ///< If true print 1 char per residue indicating ss type +}; + + +class Action_DSSP2::SSres { + public: + SSres(); + /// Deselect this residue and reset coordinate indices. + void Deselect(); + private: + typedef std::vector HbArrayType; + HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N. + DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. + double chirality_; ///< dihedral CA[i-1, i, i+1, i+2] + int SScount_[NSSTYPE_]; ///< Hold count for each SS type + PatternType pattern_; ///< Assigned hbond pattern for this frame + SStype sstype_; ///< SS assignment for this frame + int idx_; ///< Residue index in topology + int C_; ///< Coord idx of BB carbon + int O_; ///< Coord idx of BB oxygen + int N_; ///< Coord idx of BB nitrogen + int H_; ///< Coord idx of BB hydrogen + int CA_; ///< Coord idx of BB alpha carbon + bool isSelected_; ///< True if calculating SS for this residue. +}; +#endif From 10bcef6b8e429f6c91ac075cfea1e660daec72a0 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 21 Aug 2019 21:12:44 -0400 Subject: [PATCH 02/62] DRR - Cpptraj: Start adding residue setup --- src/Action_DSSP2.cpp | 15 ++++++++++++++- src/Action_DSSP2.h | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index fbf0abc77c..9185446406 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -161,7 +161,20 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) mprintf("\tSetting up for %i solute residues.\n", soluteRes.Size()); if ((unsigned int)soluteRes.Size() > Residues_.size()) Residues_.resize( soluteRes.Size() ); - + SSarrayType::iterator Res = Residues_.begin(); + for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) + if (Res->Idx() != -1) { + // Residue has previously been set up. Check that indices match. + if (Res->Idx() != *ridx) { + mprinterr("Error: Solute residue index %i does not match previously setup\n" + "Error: index %i\n", *ridx, Res->Idx()); + return Action::ERR; + } + } else { + // Set up Residue. + Res->SetIdx( *ridx ); + } + return Action::OK; } // Action_DSSP2::DoAction() diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 2c521d8584..74dc26754c 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -81,6 +81,9 @@ class Action_DSSP2 : public Action { class Action_DSSP2::SSres { public: SSres(); + int Idx() const { return idx_; } + + void SetIdx(int i) { idx_ = i; } /// Deselect this residue and reset coordinate indices. void Deselect(); private: From 1bf547d0659385a661261dc074ffe91033e2b8d6 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 21 Aug 2019 21:45:40 -0400 Subject: [PATCH 03/62] DRR - Cpptraj: Continue residue setup --- src/Action_DSSP2.cpp | 18 ++++++++++++++++-- src/Action_DSSP2.h | 5 +++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 9185446406..36bca29336 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -148,11 +148,12 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de Action::RetType Action_DSSP2::Setup(ActionSetup& setup) { // Set up mask for this parm - if ( setup.Top().SetupIntegerMask( Mask_ ) ) return Action::ERR; + if ( setup.Top().SetupCharMask( Mask_ ) ) return Action::ERR; if ( Mask_.None() ) { mprintf("Warning: DSSP: Mask has no atoms.\n"); return Action::SKIP; } + // Deselect any existing residues for (SSarrayType::iterator it = Residues_.begin(); it != Residues_.end(); ++it) it->Deselect(); @@ -163,6 +164,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) Residues_.resize( soluteRes.Size() ); SSarrayType::iterator Res = Residues_.begin(); for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) + { if (Res->Idx() != -1) { // Residue has previously been set up. Check that indices match. if (Res->Idx() != *ridx) { @@ -171,9 +173,21 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) return Action::ERR; } } else { - // Set up Residue. + // Set up Residue. TODO also molecule index? Res->SetIdx( *ridx ); } + Residue const& thisRes = setup.Top().Res( *ridx ); + // Determine if this residue is selected + if (Mask_.AtomsInCharMask(thisRes.FirstAtom(), thisRes.LastAtom())) { + Res->SetSelected( true ); + Res->SetC( setup.Top().FindAtomInResidue(*ridx, BB_C_) ); + Res->SetO( setup.Top().FindAtomInResidue(*ridx, BB_O_) ); + Res->SetN( setup.Top().FindAtomInResidue(*ridx, BB_N_) ); + Res->SetH( setup.Top().FindAtomInResidue(*ridx, BB_H_) ); + Res->SetCA( setup.Top().FindAtomInResidue(*ridx, BB_CA_) ); + } + } + return Action::OK; } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 74dc26754c..91e03529d6 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -4,7 +4,7 @@ #include #include "Action.h" #include "NameType.h" -#include "AtomMask.h" +#include "CharMask.h" /// /** Based on protein secondary structure definitions given in: * Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure: @@ -67,7 +67,7 @@ class Action_DSSP2 : public Action { NameType BB_C_; ///< Protein C atom name ('C') NameType BB_O_; ///< Protein C-O atom name ('O') NameType BB_CA_; ///< Protein alpha C name ('CA') - AtomMask Mask_; ///< Mask used to determine selected residues. + CharMask Mask_; ///< Mask used to determine selected residues. DataFile* outfile_; ///< Output Data file DataFile* dsspFile_; ///< Sum output file CpptrajFile* assignout_; ///< Assignment output file. @@ -84,6 +84,7 @@ class Action_DSSP2::SSres { int Idx() const { return idx_; } void SetIdx(int i) { idx_ = i; } + void SetSelected(bool b) { isSelected_ = b; } /// Deselect this residue and reset coordinate indices. void Deselect(); private: From 9b4cceb2dcc80bc3fd3088bda3ac94cd9bd10f4a Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 13:54:14 -0400 Subject: [PATCH 04/62] DRR - Cpptraj: Coordinate indices setup/check --- src/Action_DSSP2.cpp | 24 +++++++++++++++++++----- src/Action_DSSP2.h | 12 ++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 36bca29336..b260e5c1c9 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -180,11 +180,25 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) // Determine if this residue is selected if (Mask_.AtomsInCharMask(thisRes.FirstAtom(), thisRes.LastAtom())) { Res->SetSelected( true ); - Res->SetC( setup.Top().FindAtomInResidue(*ridx, BB_C_) ); - Res->SetO( setup.Top().FindAtomInResidue(*ridx, BB_O_) ); - Res->SetN( setup.Top().FindAtomInResidue(*ridx, BB_N_) ); - Res->SetH( setup.Top().FindAtomInResidue(*ridx, BB_H_) ); - Res->SetCA( setup.Top().FindAtomInResidue(*ridx, BB_CA_) ); + // Determine atom indices + for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) + { + if ( setup.Top()[at].Name() == BB_C_ ) Res->SetC( at*3 ); + else if ( setup.Top()[at].Name() == BB_O_ ) Res->SetO( at*3 ); + else if ( setup.Top()[at].Name() == BB_N_ ) Res->SetN( at*3 ); + else if ( setup.Top()[at].Name() == BB_H_ ) Res->SetH( at*3 ); + else if ( setup.Top()[at].Name() == BB_CA_ ) Res->SetCA( at*3 ); + } + // Check if residue is missing atoms + if (Res->MissingAtoms()) { + mprintf("Warning: Res %s is missing atoms", setup.Top().TruncResNameNum( *ridx ).c_str()); + if (Res->C() == -1) mprintf(" %s", *BB_C_); + if (Res->O() == -1) mprintf(" %s", *BB_N_); + if (Res->N() == -1) mprintf(" %s", *BB_O_); + if (Res->H() == -1) mprintf(" %s", *BB_H_); + if (Res->CA() == -1) mprintf(" %s", *BB_CA_); + mprintf("\n"); + } } } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 91e03529d6..06434712d7 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -82,9 +82,21 @@ class Action_DSSP2::SSres { public: SSres(); int Idx() const { return idx_; } + int C() const { return C_; } + int O() const { return O_; } + int N() const { return N_; } + int H() const { return H_; } + int CA() const { return CA_; } + + bool MissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } void SetIdx(int i) { idx_ = i; } void SetSelected(bool b) { isSelected_ = b; } + void SetC(int i) { C_ = i; } + void SetO(int i) { O_ = i; } + void SetN(int i) { N_ = i; } + void SetH(int i) { H_ = i; } + void SetCA(int i) { CA_ = i; } /// Deselect this residue and reset coordinate indices. void Deselect(); private: From da7e67b65e4b3958f77e14707cc5e2efd63c89c9 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 14:01:07 -0400 Subject: [PATCH 05/62] DRR - Cpptraj: Per residue data set setup --- src/Action_DSSP2.cpp | 23 +++++++++++++++++++++-- src/Action_DSSP2.h | 17 ++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index b260e5c1c9..1dce228df3 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1,6 +1,7 @@ #include // std::fill #include "Action_DSSP2.h" #include "CpptrajStdio.h" +#include "DataSet.h" // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : @@ -162,6 +163,12 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) mprintf("\tSetting up for %i solute residues.\n", soluteRes.Size()); if ((unsigned int)soluteRes.Size() > Residues_.size()) Residues_.resize( soluteRes.Size() ); + MetaData md(dsetname_, "res"); + DataSet::DataType dt; + if (printString_) + dt = DataSet::STRING; + else + dt = DataSet::INTEGER; SSarrayType::iterator Res = Residues_.begin(); for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) { @@ -190,7 +197,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) else if ( setup.Top()[at].Name() == BB_CA_ ) Res->SetCA( at*3 ); } // Check if residue is missing atoms - if (Res->MissingAtoms()) { + if (Res->IsMissingAtoms()) { mprintf("Warning: Res %s is missing atoms", setup.Top().TruncResNameNum( *ridx ).c_str()); if (Res->C() == -1) mprintf(" %s", *BB_C_); if (Res->O() == -1) mprintf(" %s", *BB_N_); @@ -199,7 +206,19 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) if (Res->CA() == -1) mprintf(" %s", *BB_CA_); mprintf("\n"); } - } + // Set up DataSet if necessary + if (Res->Dset() == 0) { + md.SetIdx( *ridx+1 ); + md.SetLegend( setup.Top().TruncResNameNum( *ridx ) ); + // Setup DataSet for this residue + Res->SetDset( Init_.DSL().AddSet( dt, md ) ); + if (Res->Dset() == 0) { + mprinterr("Error: Could not allocate DSSP data set for residue %i\n", *ridx+1); + return Action::ERR; + } + if (outfile_ != 0) outfile_->AddDataSet( Res->Dset() ); + } + } // END residue is selected } return Action::OK; diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 06434712d7..3326500a00 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -5,6 +5,7 @@ #include "Action.h" #include "NameType.h" #include "CharMask.h" +class DataSet; /// /** Based on protein secondary structure definitions given in: * Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure: @@ -81,14 +82,15 @@ class Action_DSSP2 : public Action { class Action_DSSP2::SSres { public: SSres(); - int Idx() const { return idx_; } - int C() const { return C_; } - int O() const { return O_; } - int N() const { return N_; } - int H() const { return H_; } - int CA() const { return CA_; } + int Idx() const { return idx_; } + int C() const { return C_; } + int O() const { return O_; } + int N() const { return N_; } + int H() const { return H_; } + int CA() const { return CA_; } + DataSet* Dset() const { return resDataSet_; } - bool MissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } + bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } void SetIdx(int i) { idx_ = i; } void SetSelected(bool b) { isSelected_ = b; } @@ -97,6 +99,7 @@ class Action_DSSP2::SSres { void SetN(int i) { N_ = i; } void SetH(int i) { H_ = i; } void SetCA(int i) { CA_ = i; } + void SetDset(DataSet* d) { resDataSet_ = d; } /// Deselect this residue and reset coordinate indices. void Deselect(); private: From 3f6639d62af84bdaaa6a92a14d4c4de9375ca99a Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 14:08:52 -0400 Subject: [PATCH 06/62] DRR - Cpptraj: Add some debug printout --- src/Action_DSSP2.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 1dce228df3..b1bcfbfec3 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -138,6 +138,13 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de return Action::OK; } +static inline void PrintAtom(Topology const& top, NameType const& name, int idx) { + if (idx > -1) + mprintf(" %s=%s", *name, top.AtomMaskName(idx/3).c_str()); + else + mprintf(" %s=NONE", *name); +} + // Action_DSSP2::Setup() /** Set up secondary structure calculation for all residues selected by the * mask. A residue is selected if at least one atom in the residue is @@ -169,6 +176,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) dt = DataSet::STRING; else dt = DataSet::INTEGER; + unsigned int nResSelected = 0; SSarrayType::iterator Res = Residues_.begin(); for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) { @@ -187,6 +195,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) // Determine if this residue is selected if (Mask_.AtomsInCharMask(thisRes.FirstAtom(), thisRes.LastAtom())) { Res->SetSelected( true ); + ++nResSelected; // Determine atom indices for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) { @@ -220,6 +229,20 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) } } // END residue is selected } + mprintf("\t%u of %zu solute residues selected.\n", nResSelected, soluteRes.Size()); + + // DEBUG - print each residue set up. + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + { + mprintf(" %8i", it->Idx() + 1); + PrintAtom(setup.Top(), BB_C_, it->C()); + PrintAtom(setup.Top(), BB_O_, it->O()); + PrintAtom(setup.Top(), BB_N_, it->N()); + PrintAtom(setup.Top(), BB_H_, it->H()); + PrintAtom(setup.Top(), BB_CA_, it->CA()); + mprintf("\n"); + } + return Action::OK; } From e64d2f69528efe71c4c1c47f7aab531e3575128b Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 14:17:35 -0400 Subject: [PATCH 07/62] DRR - Cpptraj: Incorporate as test command dssp2 --- src/Action_DSSP2.cpp | 11 ++++++++--- src/Command.cpp | 2 ++ src/cpptrajdepend | 3 ++- src/cpptrajfiles | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index b1bcfbfec3..3e4cf89fff 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -51,6 +51,11 @@ const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Para,Anti,3-10,Alpha, Action_DSSP2::Action_DSSP2() : debug_(0), + BB_N_("N"), + BB_H_("H"), + BB_C_("C"), + BB_O_("O"), + BB_CA_("CA"), outfile_(0), dsspFile_(0), assignout_(0) @@ -140,9 +145,9 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de static inline void PrintAtom(Topology const& top, NameType const& name, int idx) { if (idx > -1) - mprintf(" %s=%s", *name, top.AtomMaskName(idx/3).c_str()); + mprintf(" '%s'=%-12s", *name, top.AtomMaskName(idx/3).c_str()); else - mprintf(" %s=NONE", *name); + mprintf(" '%s'=%-12s", *name, "NONE"); } // Action_DSSP2::Setup() @@ -250,5 +255,5 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) // Action_DSSP2::DoAction() Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { - + return Action::ERR; } diff --git a/src/Command.cpp b/src/Command.cpp index e6bafb2193..65a779aa1c 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -141,6 +141,7 @@ #include "Action_XtalSymm.h" #include "Action_Time.h" #include "Action_DihedralRMS.h" +#include "Action_DSSP2.h" // ----- ANALYSIS -------------------------------------------------------------- #include "Analysis_Hist.h" #include "Analysis_Corr.h" @@ -304,6 +305,7 @@ void Command::Init() { Command::AddCmd( new Action_DNAionTracker(), Cmd::ACT, 1, "dnaiontracker" ); // HIDDEN Command::AddCmd( new Action_DistRmsd(), Cmd::ACT, 2, "drms", "drmsd" ); Command::AddCmd( new Action_DSSP(), Cmd::ACT, 2, "dssp", "secstruct" ); + Command::AddCmd( new Action_DSSP2(), Cmd::ACT, 1, "dssp2" ); Command::AddCmd( new Action_Energy(), Cmd::ACT, 1, "energy" ); Command::AddCmd( new Action_Esander(), Cmd::ACT, 1, "esander" ); Command::AddCmd( new Action_FilterByData(), Cmd::ACT, 1, "filter" ); diff --git a/src/cpptrajdepend b/src/cpptrajdepend index 94b9602c77..0254f5b8a3 100644 --- a/src/cpptrajdepend +++ b/src/cpptrajdepend @@ -22,6 +22,7 @@ Action_CreateCrd.o : Action_CreateCrd.cpp Action.h ActionState.h Action_CreateCr Action_CreateReservoir.o : Action_CreateReservoir.cpp Action.h ActionState.h Action_CreateReservoir.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DNAionTracker.o : Action_DNAionTracker.cpp Action.h ActionState.h Action_DNAionTracker.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DSSP.o : Action_DSSP.cpp Action.h ActionState.h Action_DSSP.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h +Action_DSSP2.o : Action_DSSP2.cpp Action.h ActionState.h Action_DSSP2.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Density.o : Action_Density.cpp Action.h ActionState.h Action_Density.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h OnlineVarT.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Diffusion.o : Action_Diffusion.cpp Action.h ActionState.h Action_Diffusion.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Dihedral.o : Action_Dihedral.cpp Action.h ActionState.h Action_Dihedral.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h DataSet_double.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h TorsionRoutines.h Vec3.h @@ -157,7 +158,7 @@ Cluster_ReadInfo.o : Cluster_ReadInfo.cpp ArgList.h ArrayIterator.h AssociatedDa Cmd.o : Cmd.cpp Cmd.h DispatchObject.h CmdInput.o : CmdInput.cpp CmdInput.h StringRoutines.h CmdList.o : CmdList.cpp Cmd.h CmdList.h DispatchObject.h -Command.o : Command.cpp Action.h ActionFrameCounter.h ActionList.h ActionState.h Action_Align.h Action_Angle.h Action_AreaPerMol.h Action_AtomMap.h Action_AtomicCorr.h Action_AtomicFluct.h Action_AutoImage.h Action_Average.h Action_Bounds.h Action_Box.h Action_Center.h Action_Channel.h Action_CheckChirality.h Action_CheckStructure.h Action_Closest.h Action_ClusterDihedral.h Action_Contacts.h Action_CreateCrd.h Action_CreateReservoir.h Action_DNAionTracker.h Action_DSSP.h Action_Density.h Action_Diffusion.h Action_Dihedral.h Action_DihedralRMS.h Action_Dipole.h Action_DistRmsd.h Action_Distance.h Action_Energy.h Action_Esander.h Action_FilterByData.h Action_FixAtomOrder.h Action_FixImagedBonds.h Action_GIST.h Action_Grid.h Action_GridFreeEnergy.h Action_HydrogenBond.h Action_Image.h Action_InfraredSpectrum.h Action_Jcoupling.h Action_LESsplit.h Action_LIE.h Action_LipidOrder.h Action_MakeStructure.h Action_Mask.h Action_Matrix.h Action_MinImage.h Action_Molsurf.h Action_MultiDihedral.h Action_MultiVector.h Action_NAstruct.h Action_NMRrst.h Action_NativeContacts.h Action_OrderParameter.h Action_Outtraj.h Action_PairDist.h Action_Pairwise.h Action_Principal.h Action_Projection.h Action_Pucker.h Action_Radgyr.h Action_Radial.h Action_RandomizeIons.h Action_Remap.h Action_ReplicateCell.h Action_Rmsd.h Action_Rotate.h Action_RunningAvg.h Action_STFC_Diffusion.h Action_Scale.h Action_SetVelocity.h Action_Spam.h Action_Strip.h Action_Surf.h Action_SymmetricRmsd.h Action_Temperature.h Action_Time.h Action_Translate.h Action_Unstrip.h Action_Unwrap.h Action_Vector.h Action_VelocityAutoCorr.h Action_Volmap.h Action_Volume.h Action_Watershell.h Action_XtalSymm.h Analysis.h AnalysisList.h AnalysisState.h Analysis_AmdBias.h Analysis_AutoCorr.h Analysis_Average.h Analysis_Clustering.h Analysis_ConstantPHStats.h Analysis_Corr.h Analysis_CrankShaft.h Analysis_CrdFluct.h Analysis_CrossCorr.h Analysis_CurveFit.h Analysis_Divergence.h Analysis_FFT.h Analysis_HausdorffDistance.h Analysis_Hist.h Analysis_IRED.h Analysis_Integrate.h Analysis_KDE.h Analysis_Lifetime.h Analysis_LowestCurve.h Analysis_Matrix.h Analysis_MeltCurve.h Analysis_Modes.h Analysis_MultiHist.h Analysis_Multicurve.h Analysis_Overlap.h Analysis_PhiPsi.h Analysis_Regression.h Analysis_RemLog.h Analysis_Rms2d.h Analysis_RmsAvgCorr.h Analysis_Rotdif.h Analysis_RunningAvg.h Analysis_Spline.h Analysis_State.h Analysis_Statistics.h Analysis_TI.h Analysis_Timecorr.h Analysis_VectorMath.h Analysis_Wavelet.h ArgList.h Array1D.h ArrayIterator.h AssociatedData.h Atom.h AtomExtra.h AtomMap.h AtomMask.h AxisType.h BaseIOtype.h Box.h BufferedLine.h CharMask.h ClusterDist.h ClusterList.h ClusterMap.h ClusterNode.h ClusterSieve.h Cmd.h CmdInput.h CmdList.h Command.h ComplexArray.h Constraints.h Control.h CoordinateInfo.h Corr.h Cph.h CpptrajFile.h CpptrajState.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_2D.h DataSet_3D.h DataSet_Cmatrix.h DataSet_Coords.h DataSet_Coords_CRD.h DataSet_Coords_REF.h DataSet_GridFlt.h DataSet_Mat3x3.h DataSet_MatrixDbl.h DataSet_MatrixFlt.h DataSet_Mesh.h DataSet_Modes.h DataSet_RemLog.h DataSet_Vector.h DataSet_double.h DataSet_float.h DataSet_integer.h DataSet_integer_mem.h DataSet_pH.h DataSet_string.h Deprecated.h DihedralSearch.h Dimension.h DispatchObject.h DistRoutines.h Energy.h Energy_Sander.h EnsembleIn.h EnsembleOutList.h Ewald.h Exec.h Exec_Analyze.h Exec_Calc.h Exec_CatCrd.h Exec_Change.h Exec_ClusterMap.h Exec_CombineCoords.h Exec_Commands.h Exec_CompareTop.h Exec_CrdAction.h Exec_CrdOut.h Exec_CreateSet.h Exec_DataFile.h Exec_DataFilter.h Exec_DataSetCmd.h Exec_GenerateAmberRst.h Exec_Help.h Exec_LoadCrd.h Exec_LoadTraj.h Exec_ParallelAnalysis.h Exec_ParmBox.h Exec_ParmSolvent.h Exec_ParmStrip.h Exec_ParmWrite.h Exec_PermuteDihedrals.h Exec_Precision.h Exec_PrintData.h Exec_ReadData.h Exec_ReadEnsembleData.h Exec_ReadInput.h Exec_RotateDihedral.h Exec_RunAnalysis.h Exec_ScaleDihedralK.h Exec_SequenceAlign.h Exec_SortEnsembleData.h Exec_SplitCoords.h Exec_System.h Exec_Top.h Exec_Traj.h Exec_UpdateParameters.h Exec_ViewRst.h FileIO.h FileName.h FileTypes.h Frame.h FramePtrArray.h Grid.h GridAction.h GridBin.h HistBin.h Hungarian.h ImageTypes.h ImagedAction.h InputTrajCommon.h MapAtom.h MaskArray.h MaskToken.h Matrix.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h OnlineVarT.h OutputTrajCommon.h PDBfile.h PairList.h Parallel.h ParameterHolders.h ParameterTypes.h PubFFT.h RPNcalc.h Random.h Range.h ReferenceAction.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h ReplicaInfo.h Residue.h Spline.h StructureCheck.h SymbolExporting.h SymmetricRmsdCalc.h TextFormat.h Timer.h Topology.h TrajFrameCounter.h TrajectoryFile.h Trajin.h TrajinList.h TrajoutList.h Trajout_Single.h VariableArray.h Vec3.h molsurf.h +Command.o : Command.cpp Action.h ActionFrameCounter.h ActionList.h ActionState.h Action_Align.h Action_Angle.h Action_AreaPerMol.h Action_AtomMap.h Action_AtomicCorr.h Action_AtomicFluct.h Action_AutoImage.h Action_Average.h Action_Bounds.h Action_Box.h Action_Center.h Action_Channel.h Action_CheckChirality.h Action_CheckStructure.h Action_Closest.h Action_ClusterDihedral.h Action_Contacts.h Action_CreateCrd.h Action_CreateReservoir.h Action_DNAionTracker.h Action_DSSP.h Action_DSSP2.h Action_Density.h Action_Diffusion.h Action_Dihedral.h Action_DihedralRMS.h Action_Dipole.h Action_DistRmsd.h Action_Distance.h Action_Energy.h Action_Esander.h Action_FilterByData.h Action_FixAtomOrder.h Action_FixImagedBonds.h Action_GIST.h Action_Grid.h Action_GridFreeEnergy.h Action_HydrogenBond.h Action_Image.h Action_InfraredSpectrum.h Action_Jcoupling.h Action_LESsplit.h Action_LIE.h Action_LipidOrder.h Action_MakeStructure.h Action_Mask.h Action_Matrix.h Action_MinImage.h Action_Molsurf.h Action_MultiDihedral.h Action_MultiVector.h Action_NAstruct.h Action_NMRrst.h Action_NativeContacts.h Action_OrderParameter.h Action_Outtraj.h Action_PairDist.h Action_Pairwise.h Action_Principal.h Action_Projection.h Action_Pucker.h Action_Radgyr.h Action_Radial.h Action_RandomizeIons.h Action_Remap.h Action_ReplicateCell.h Action_Rmsd.h Action_Rotate.h Action_RunningAvg.h Action_STFC_Diffusion.h Action_Scale.h Action_SetVelocity.h Action_Spam.h Action_Strip.h Action_Surf.h Action_SymmetricRmsd.h Action_Temperature.h Action_Time.h Action_Translate.h Action_Unstrip.h Action_Unwrap.h Action_Vector.h Action_VelocityAutoCorr.h Action_Volmap.h Action_Volume.h Action_Watershell.h Action_XtalSymm.h Analysis.h AnalysisList.h AnalysisState.h Analysis_AmdBias.h Analysis_AutoCorr.h Analysis_Average.h Analysis_Clustering.h Analysis_ConstantPHStats.h Analysis_Corr.h Analysis_CrankShaft.h Analysis_CrdFluct.h Analysis_CrossCorr.h Analysis_CurveFit.h Analysis_Divergence.h Analysis_FFT.h Analysis_HausdorffDistance.h Analysis_Hist.h Analysis_IRED.h Analysis_Integrate.h Analysis_KDE.h Analysis_Lifetime.h Analysis_LowestCurve.h Analysis_Matrix.h Analysis_MeltCurve.h Analysis_Modes.h Analysis_MultiHist.h Analysis_Multicurve.h Analysis_Overlap.h Analysis_PhiPsi.h Analysis_Regression.h Analysis_RemLog.h Analysis_Rms2d.h Analysis_RmsAvgCorr.h Analysis_Rotdif.h Analysis_RunningAvg.h Analysis_Spline.h Analysis_State.h Analysis_Statistics.h Analysis_TI.h Analysis_Timecorr.h Analysis_VectorMath.h Analysis_Wavelet.h ArgList.h Array1D.h ArrayIterator.h AssociatedData.h Atom.h AtomExtra.h AtomMap.h AtomMask.h AxisType.h BaseIOtype.h Box.h BufferedLine.h CharMask.h ClusterDist.h ClusterList.h ClusterMap.h ClusterNode.h ClusterSieve.h Cmd.h CmdInput.h CmdList.h Command.h ComplexArray.h Constraints.h Control.h CoordinateInfo.h Corr.h Cph.h CpptrajFile.h CpptrajState.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_2D.h DataSet_3D.h DataSet_Cmatrix.h DataSet_Coords.h DataSet_Coords_CRD.h DataSet_Coords_REF.h DataSet_GridFlt.h DataSet_Mat3x3.h DataSet_MatrixDbl.h DataSet_MatrixFlt.h DataSet_Mesh.h DataSet_Modes.h DataSet_RemLog.h DataSet_Vector.h DataSet_double.h DataSet_float.h DataSet_integer.h DataSet_integer_mem.h DataSet_pH.h DataSet_string.h Deprecated.h DihedralSearch.h Dimension.h DispatchObject.h DistRoutines.h Energy.h Energy_Sander.h EnsembleIn.h EnsembleOutList.h Ewald.h Exec.h Exec_Analyze.h Exec_Calc.h Exec_CatCrd.h Exec_Change.h Exec_ClusterMap.h Exec_CombineCoords.h Exec_Commands.h Exec_CompareTop.h Exec_CrdAction.h Exec_CrdOut.h Exec_CreateSet.h Exec_DataFile.h Exec_DataFilter.h Exec_DataSetCmd.h Exec_GenerateAmberRst.h Exec_Help.h Exec_LoadCrd.h Exec_LoadTraj.h Exec_ParallelAnalysis.h Exec_ParmBox.h Exec_ParmSolvent.h Exec_ParmStrip.h Exec_ParmWrite.h Exec_PermuteDihedrals.h Exec_Precision.h Exec_PrintData.h Exec_ReadData.h Exec_ReadEnsembleData.h Exec_ReadInput.h Exec_RotateDihedral.h Exec_RunAnalysis.h Exec_ScaleDihedralK.h Exec_SequenceAlign.h Exec_SortEnsembleData.h Exec_SplitCoords.h Exec_System.h Exec_Top.h Exec_Traj.h Exec_UpdateParameters.h Exec_ViewRst.h FileIO.h FileName.h FileTypes.h Frame.h FramePtrArray.h Grid.h GridAction.h GridBin.h HistBin.h Hungarian.h ImageTypes.h ImagedAction.h InputTrajCommon.h MapAtom.h MaskArray.h MaskToken.h Matrix.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h OnlineVarT.h OutputTrajCommon.h PDBfile.h PairList.h Parallel.h ParameterHolders.h ParameterTypes.h PubFFT.h RPNcalc.h Random.h Range.h ReferenceAction.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h ReplicaInfo.h Residue.h Spline.h StructureCheck.h SymbolExporting.h SymmetricRmsdCalc.h TextFormat.h Timer.h Topology.h TrajFrameCounter.h TrajectoryFile.h Trajin.h TrajinList.h TrajoutList.h Trajout_Single.h VariableArray.h Vec3.h molsurf.h ComplexArray.o : ComplexArray.cpp ArrayIterator.h ComplexArray.h Constraints.o : Constraints.cpp ArgList.h Atom.h AtomExtra.h AtomMask.h Box.h CharMask.h Constants.h Constraints.h CoordinateInfo.h CpptrajStdio.h FileName.h Frame.h MaskToken.h Matrix_3x3.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReplicaDimArray.h Residue.h SymbolExporting.h Topology.h Vec3.h Control.o : Control.cpp Action.h ActionList.h ActionState.h Analysis.h AnalysisList.h AnalysisState.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Control.h CoordinateInfo.h CpptrajFile.h CpptrajState.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h EnsembleIn.h EnsembleOutList.h FileIO.h FileName.h FileTypes.h Frame.h FramePtrArray.h InputTrajCommon.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h ReplicaInfo.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h TrajFrameCounter.h Trajin.h TrajinList.h TrajoutList.h VariableArray.h Vec3.h diff --git a/src/cpptrajfiles b/src/cpptrajfiles index c02caaa64e..710b0eed09 100644 --- a/src/cpptrajfiles +++ b/src/cpptrajfiles @@ -26,6 +26,7 @@ COMMON_SOURCES= \ Action_CreateReservoir.cpp \ Action_DNAionTracker.cpp \ Action_DSSP.cpp \ + Action_DSSP2.cpp \ Action_Density.cpp \ Action_Diffusion.cpp \ Action_Dihedral.cpp \ From d30d2fa0d819f8f26a08c0eb071ac87e0d547716 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 14:40:55 -0400 Subject: [PATCH 08/62] DRR - Cpptraj: Add dssp hbond calc --- src/Action_DSSP2.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++- src/Action_DSSP2.h | 22 +++++++++++------ 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 3e4cf89fff..abcaa4858b 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1,7 +1,9 @@ +#include // sqrt #include // std::fill #include "Action_DSSP2.h" #include "CpptrajStdio.h" #include "DataSet.h" +#include "DistRoutines.h" // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : @@ -29,6 +31,12 @@ void Action_DSSP2::SSres::Deselect() { CA_ = -1; } +void Action_DSSP2::SSres::Unassign() { + CO_HN_Hbonds_.clear(); + pattern_ = NOHBOND; + sstype_ = NONE; +} + // ----- Action_DSSP2 ---------------------------------------------------------- /** From the original Kabsch & Sander 1983 paper. Obtained via @@ -255,5 +263,52 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) // Action_DSSP2::DoAction() Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { - return Action::ERR; + int resi; + int Nres = (int)Residues_.size(); + // The first pass is used to determine hydrogen bonding +#ifdef _OPENMP +#pragma omp parallel private(resi) +{ +#pragma omp for +#endif /* _OPENMP */ + for (resi = 0; resi < Nres; resi++) + { + if (Residues_[resi].IsSelected()) + { + SSres& ResCO = Residues_[resi]; + ResCO.Unassign(); + if (ResCO.HasCO()) + { + const double* Cxyz = frm.Frm().CRD( ResCO.C() ); + const double* Oxyz = frm.Frm().CRD( ResCO.O() ); + for (int resj = 0; resj < Nres; resj++) + { + SSres& ResNH = Residues_[resj]; + if (ResNH.IsSelected() && resi != resj && ResNH.HasNH()) + { + const double* Nxyz = frm.Frm().CRD( ResNH.N() ); + const double* Hxyz = frm.Frm().CRD( ResNH.H() ); + + double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); + double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); + double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); + double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); + + double E = DSSP_fac_ * (rON + rCH - rOH - rCN); + if (E < DSSP_cut_) { +# ifdef DSSPDEBUG + mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +# endif + ResCO.AddHbond( ResNH.Idx() ); + } + } // END ResNH selected + } // END inner loop over residues + } // END has CO + } // END ResCO selected + } // END outer loop over residues +#ifdef _OPENMP +} // END pragma omp parallel +#endif + + return Action::OK; } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 3326500a00..3721db1d4b 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -82,15 +82,19 @@ class Action_DSSP2 : public Action { class Action_DSSP2::SSres { public: SSres(); - int Idx() const { return idx_; } - int C() const { return C_; } - int O() const { return O_; } - int N() const { return N_; } - int H() const { return H_; } - int CA() const { return CA_; } - DataSet* Dset() const { return resDataSet_; } + int Idx() const { return idx_; } + bool IsSelected() const { return isSelected_; } + int C() const { return C_; } + int O() const { return O_; } + int N() const { return N_; } + int H() const { return H_; } + int CA() const { return CA_; } + DataSet* Dset() const { return resDataSet_; } bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } + bool HasCO() const { return (C_!=-1 && O_!=-1); } + bool HasNH() const { return (N_!=-1 && H_!=-1); } + bool HasCA() const { return (CA_!=-1); } void SetIdx(int i) { idx_ = i; } void SetSelected(bool b) { isSelected_ = b; } @@ -102,6 +106,10 @@ class Action_DSSP2::SSres { void SetDset(DataSet* d) { resDataSet_ = d; } /// Deselect this residue and reset coordinate indices. void Deselect(); + /// Reset hbonds, pattern and SS type assignments + void Unassign(); + /// Add hbond from this CO to specified NH + void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } private: typedef std::vector HbArrayType; HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N. From a1baab126afbd7514dbb22bf9d22746f1b862b2b Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 14:48:03 -0400 Subject: [PATCH 09/62] DRR - Cpptraj: Debug print of what hbonds are formed --- src/Action_DSSP2.cpp | 7 +++++++ src/Action_DSSP2.h | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index abcaa4858b..3e6e18d40d 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -310,5 +310,12 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) } // END pragma omp parallel #endif + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + { + mprintf("Res %8i", it->Idx()); + for (SSres::const_iterator hb = it->begin(); hb != it->end(); ++hb) + mprintf(" %8i", *hb); + mprintf("\n"); + } return Action::OK; } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 3721db1d4b..fb96248fda 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -80,6 +80,7 @@ class Action_DSSP2 : public Action { class Action_DSSP2::SSres { + typedef std::vector HbArrayType; public: SSres(); int Idx() const { return idx_; } @@ -96,6 +97,10 @@ class Action_DSSP2::SSres { bool HasNH() const { return (N_!=-1 && H_!=-1); } bool HasCA() const { return (CA_!=-1); } + typedef HbArrayType::const_iterator const_iterator; + const_iterator begin() const { return CO_HN_Hbonds_.begin(); } + const_iterator end() const { return CO_HN_Hbonds_.end(); } + void SetIdx(int i) { idx_ = i; } void SetSelected(bool b) { isSelected_ = b; } void SetC(int i) { C_ = i; } @@ -111,7 +116,6 @@ class Action_DSSP2::SSres { /// Add hbond from this CO to specified NH void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } private: - typedef std::vector HbArrayType; HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N. DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. double chirality_; ///< dihedral CA[i-1, i, i+1, i+2] From 8b9295f5d9697c11eba64e34460ea8c9e86861a9 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 22 Aug 2019 15:31:04 -0400 Subject: [PATCH 10/62] DRR - Cpptraj: Fix the offset --- src/Action_DSSP2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 3e6e18d40d..2044b97a09 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -312,9 +312,9 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) { - mprintf("Res %8i", it->Idx()); + mprintf("Res %8i", it->Idx()+1); for (SSres::const_iterator hb = it->begin(); hb != it->end(); ++hb) - mprintf(" %8i", *hb); + mprintf(" %8i", *hb+1); mprintf("\n"); } return Action::OK; From edf0e54f2d4b60286c25abc44b0f558efb084d8f Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 09:07:25 -0400 Subject: [PATCH 11/62] DRR - Cpptraj: Store data that is more in line with the original dssp definitions --- src/Action_DSSP2.cpp | 79 ++++++++++++++++++++++++++++++-------------- src/Action_DSSP2.h | 24 ++++++++++---- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 2044b97a09..62e915a5ed 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -9,7 +9,7 @@ Action_DSSP2::SSres::SSres() : resDataSet_(0), chirality_(0), - pattern_(NOHBOND), +// pattern_(NOHBOND), sstype_(NONE), idx_(-1), C_(-1), @@ -17,6 +17,15 @@ Action_DSSP2::SSres::SSres() : N_(-1), H_(-1), CA_(-1), + bridge1idx_(-1), + bridge2idx_(-1), + resChar_(' '), + TURN3_(' '), + TURN4_(' '), + TURN5_(' '), + BRIDGE1_(' '), + BRIDGE2_(' '), + SHEET_(' '), isSelected_(false) { std::fill(SScount_, SScount_ + NSSTYPE_, 0); @@ -33,8 +42,16 @@ void Action_DSSP2::SSres::Deselect() { void Action_DSSP2::SSres::Unassign() { CO_HN_Hbonds_.clear(); - pattern_ = NOHBOND; +// pattern_ = NOHBOND; sstype_ = NONE; + bridge1idx_ = -1; + bridge2idx_ = -1; + TURN3_ = ' '; + TURN4_ = ' '; + TURN5_ = ' '; + BRIDGE1_ = ' '; + BRIDGE2_ = ' '; + SHEET_ = ' '; } // ----- Action_DSSP2 ---------------------------------------------------------- @@ -203,6 +220,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) } else { // Set up Residue. TODO also molecule index? Res->SetIdx( *ridx ); + Res->SetResChar( setup.Top().Res(*ridx).SingleCharName() ); } Residue const& thisRes = setup.Top().Res( *ridx ); // Determine if this residue is selected @@ -283,25 +301,30 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) const double* Oxyz = frm.Frm().CRD( ResCO.O() ); for (int resj = 0; resj < Nres; resj++) { - SSres& ResNH = Residues_[resj]; - if (ResNH.IsSelected() && resi != resj && ResNH.HasNH()) - { - const double* Nxyz = frm.Frm().CRD( ResNH.N() ); - const double* Hxyz = frm.Frm().CRD( ResNH.H() ); + // Only consider residues spaced more than 2 apart. + int resDelta = resi - resj; + if (resDelta < 0) resDelta = -resDelta; + if (resDelta > 2) { + SSres& ResNH = Residues_[resj]; + if (ResNH.IsSelected() && ResNH.HasNH()) + { + const double* Nxyz = frm.Frm().CRD( ResNH.N() ); + const double* Hxyz = frm.Frm().CRD( ResNH.H() ); - double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); - double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); - double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); - double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); + double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); + double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); + double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); + double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); - double E = DSSP_fac_ * (rON + rCH - rOH - rCN); - if (E < DSSP_cut_) { -# ifdef DSSPDEBUG - mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -# endif - ResCO.AddHbond( ResNH.Idx() ); - } - } // END ResNH selected + double E = DSSP_fac_ * (rON + rCH - rOH - rCN); + if (E < DSSP_cut_) { +# ifdef DSSPDEBUG + mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +# endif + ResCO.AddHbond( resj ); + } + } // END ResNH selected + } // END residues spaced > 2 apart } // END inner loop over residues } // END has CO } // END ResCO selected @@ -309,13 +332,19 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) #ifdef _OPENMP } // END pragma omp parallel #endif - - for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + // Do basic assignment + for (SSarrayType::const_iterator Resi = Residues_.begin(); Resi != Residues_.end(); ++Resi) { - mprintf("Res %8i", it->Idx()+1); - for (SSres::const_iterator hb = it->begin(); hb != it->end(); ++hb) - mprintf(" %8i", *hb+1); - mprintf("\n"); + mprintf("Res %8i %c", Resi->Idx()+1, Resi->ResChar()); // DBG + for (SSres::const_iterator rjidx = Resi->begin(); rjidx != Resi->end(); ++rjidx) + { + SSres& Resj = Residues_[*rjidx]; + mprintf(" %8i", Resj.Idx()+1); // DBG + int resDelta = Resj.Idx() - Resi->Idx(); + if (resDelta < 0) resDelta = -resDelta; + mprintf("(%4i)", resDelta); + } + mprintf("\n"); // DBG } return Action::OK; } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index fb96248fda..91e0883d47 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -44,7 +44,7 @@ class Action_DSSP2 : public Action { static const std::string SSzlabels_; ///< Output graph Z labels corresponding to SStype /// Elementary hydrogen bond pattern - enum PatternType { +/* enum PatternType { NOHBOND = 0, ///< No hydrogen bond TURNBEG, ///< Start of n-Turn, > TURNEND, ///< End of n-Turn, < @@ -54,7 +54,7 @@ class Action_DSSP2 : public Action { TURN5, ///< Inside i to i + 5 turn (T) BRIDGEPARA, ///< (i-1,j) and (j,i+1) or (j-1,i) and (i,j+1), lower case BRIDGEANTI ///< (i,j) and (j,i) or (i-1,j+1) and (j-1,i+1), upper case - }; + };*/ static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol @@ -84,6 +84,7 @@ class Action_DSSP2::SSres { public: SSres(); int Idx() const { return idx_; } + char ResChar() const { return resChar_; } bool IsSelected() const { return isSelected_; } int C() const { return C_; } int O() const { return O_; } @@ -101,7 +102,8 @@ class Action_DSSP2::SSres { const_iterator begin() const { return CO_HN_Hbonds_.begin(); } const_iterator end() const { return CO_HN_Hbonds_.end(); } - void SetIdx(int i) { idx_ = i; } + void SetIdx(int i) { idx_ = i; } + void SetResChar(char c) { resChar_ = c; } void SetSelected(bool b) { isSelected_ = b; } void SetC(int i) { C_ = i; } void SetO(int i) { O_ = i; } @@ -116,11 +118,12 @@ class Action_DSSP2::SSres { /// Add hbond from this CO to specified NH void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } private: - HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N. + HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. - double chirality_; ///< dihedral CA[i-1, i, i+1, i+2] + double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] + double bend_; ///< Angle CA[i-2, i, i+2] int SScount_[NSSTYPE_]; ///< Hold count for each SS type - PatternType pattern_; ///< Assigned hbond pattern for this frame + //PatternType pattern_; ///< Assigned hbond pattern for this frame SStype sstype_; ///< SS assignment for this frame int idx_; ///< Residue index in topology int C_; ///< Coord idx of BB carbon @@ -128,6 +131,15 @@ class Action_DSSP2::SSres { int N_; ///< Coord idx of BB nitrogen int H_; ///< Coord idx of BB hydrogen int CA_; ///< Coord idx of BB alpha carbon + int bridge1idx_; ///< Index in Residues_ of res this is bridged to + int bridge2idx_; ///< Index in Residues_ of res this is bridged to + char resChar_; ///< Single char residue ID + char TURN3_; ///< Character if part of 3 turn + char TURN4_; ///< Character if part of 4 turn + char TURN5_; ///< Character if part of 5 turn + char BRIDGE1_; ///< Bridge 1 character + char BRIDGE2_; ///< Bridge 2 character + char SHEET_; ///< Sheet character bool isSelected_; ///< True if calculating SS for this residue. }; #endif From e186ac3fd3040d445a1c6a4b36d2e15b5420cd44 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 09:56:11 -0400 Subject: [PATCH 12/62] DRR - Cpptraj: Put all the SS chars in a single array. Start turn assignment --- src/Action_DSSP2.cpp | 84 +++++++++++++++++++++++++++++++++++--------- src/Action_DSSP2.h | 46 +++++++++++++----------- 2 files changed, 92 insertions(+), 38 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 62e915a5ed..a9567b55ed 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -20,15 +20,10 @@ Action_DSSP2::SSres::SSres() : bridge1idx_(-1), bridge2idx_(-1), resChar_(' '), - TURN3_(' '), - TURN4_(' '), - TURN5_(' '), - BRIDGE1_(' '), - BRIDGE2_(' '), - SHEET_(' '), isSelected_(false) { - std::fill(SScount_, SScount_ + NSSTYPE_, 0); + std::fill(SScount_, SScount_ + NSSTYPE_, 0); + std::fill(ssChar_, ssChar_ + NSSCHARTYPE_, ' '); } void Action_DSSP2::SSres::Deselect() { @@ -46,12 +41,36 @@ void Action_DSSP2::SSres::Unassign() { sstype_ = NONE; bridge1idx_ = -1; bridge2idx_ = -1; - TURN3_ = ' '; - TURN4_ = ' '; - TURN5_ = ' '; - BRIDGE1_ = ' '; - BRIDGE2_ = ' '; - SHEET_ = ' '; + std::fill(ssChar_, ssChar_ + NSSCHARTYPE_, ' '); +} + +/** Set turn beginning. */ +void Action_DSSP2::SSres::SetBegin(ssCharType typeIn) { + if (ssChar_[typeIn] == '<') + ssChar_[typeIn] = 'X'; + else + ssChar_[typeIn] = '>'; +} + +void Action_DSSP2::SSres::SetEnd(ssCharType typeIn) { + if (ssChar_[typeIn] == '>') + ssChar_[typeIn] = 'X'; + else + ssChar_[typeIn] = '<'; +} + +void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { + // Do not overwrite an existing end character + if (ssChar_[typeIn] == ' ') { + if (typeIn == T3) ssChar_[typeIn] = '3'; + else if (typeIn == T4) ssChar_[typeIn] = '4'; + else if (typeIn == T5) ssChar_[typeIn] = '5'; + } +} + +void Action_DSSP2::SSres::PrintSSchar() const { + mprintf("\t%8i %c %c %c %c\n", idx_, resChar_, + ssChar_[T3], ssChar_[T4], ssChar_[T5]); } // ----- Action_DSSP2 ---------------------------------------------------------- @@ -323,6 +342,10 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) # endif ResCO.AddHbond( resj ); } +# ifdef DSSPDEBUG + else if (resDelta < 6) + mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +# endif } // END ResNH selected } // END residues spaced > 2 apart } // END inner loop over residues @@ -333,18 +356,45 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) } // END pragma omp parallel #endif // Do basic assignment - for (SSarrayType::const_iterator Resi = Residues_.begin(); Resi != Residues_.end(); ++Resi) + for (unsigned int riidx = 0; riidx != Residues_.size(); riidx++) { - mprintf("Res %8i %c", Resi->Idx()+1, Resi->ResChar()); // DBG - for (SSres::const_iterator rjidx = Resi->begin(); rjidx != Resi->end(); ++rjidx) + SSres& Resi = Residues_[riidx]; + mprintf("Res %8i %c", Resi.Idx()+1, Resi.ResChar()); // DBG + for (SSres::const_iterator rjidx = Resi.begin(); rjidx != Resi.end(); ++rjidx) { SSres& Resj = Residues_[*rjidx]; mprintf(" %8i", Resj.Idx()+1); // DBG - int resDelta = Resj.Idx() - Resi->Idx(); + int resDelta = Resj.Idx() - Resi.Idx(); if (resDelta < 0) resDelta = -resDelta; mprintf("(%4i)", resDelta); + if (resDelta == 3) { + // 3-TURN + Residues_[riidx ].SetBegin(T3); + Residues_[riidx+1].SetTurn(T3); + Residues_[riidx+2].SetTurn(T3); + Residues_[riidx+3].SetEnd(T3); + } else if (resDelta == 4) { + // 4-TURN + Residues_[riidx ].SetBegin(T4); + Residues_[riidx+1].SetTurn(T4); + Residues_[riidx+2].SetTurn(T4); + Residues_[riidx+3].SetTurn(T4); + Residues_[riidx+4].SetEnd(T4); + } else if (resDelta == 5) { + // 5-TURN + Residues_[riidx ].SetBegin(T5); + Residues_[riidx+1].SetTurn(T5); + Residues_[riidx+2].SetTurn(T5); + Residues_[riidx+3].SetTurn(T5); + Residues_[riidx+4].SetTurn(T5); + Residues_[riidx+5].SetEnd(T5); + } } mprintf("\n"); // DBG } + + // DEBUG - Print basic assignment + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + it->PrintSSchar(); return Action::OK; } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 91e0883d47..4cbc518f9a 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -56,6 +56,9 @@ class Action_DSSP2 : public Action { BRIDGEANTI ///< (i,j) and (j,i) or (i-1,j+1) and (j-1,i+1), upper case };*/ + enum ssCharType { T3 = 0, T4, T5, B1, B2, S }; + static const int NSSCHARTYPE_ = 6; + static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol @@ -98,10 +101,16 @@ class Action_DSSP2::SSres { bool HasNH() const { return (N_!=-1 && H_!=-1); } bool HasCA() const { return (CA_!=-1); } + void PrintSSchar() const; + typedef HbArrayType::const_iterator const_iterator; const_iterator begin() const { return CO_HN_Hbonds_.begin(); } const_iterator end() const { return CO_HN_Hbonds_.end(); } + void SetBegin(ssCharType); + void SetTurn(ssCharType); + void SetEnd(ssCharType); + void SetIdx(int i) { idx_ = i; } void SetResChar(char c) { resChar_ = c; } void SetSelected(bool b) { isSelected_ = b; } @@ -118,28 +127,23 @@ class Action_DSSP2::SSres { /// Add hbond from this CO to specified NH void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } private: - HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). - DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. - double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] - double bend_; ///< Angle CA[i-2, i, i+2] - int SScount_[NSSTYPE_]; ///< Hold count for each SS type + HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). + DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. + double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] + double bend_; ///< Angle CA[i-2, i, i+2] + int SScount_[NSSTYPE_]; ///< Hold count for each SS type //PatternType pattern_; ///< Assigned hbond pattern for this frame - SStype sstype_; ///< SS assignment for this frame - int idx_; ///< Residue index in topology - int C_; ///< Coord idx of BB carbon - int O_; ///< Coord idx of BB oxygen - int N_; ///< Coord idx of BB nitrogen + SStype sstype_; ///< SS assignment for this frame + int idx_; ///< Residue index in topology + int C_; ///< Coord idx of BB carbon + int O_; ///< Coord idx of BB oxygen + int N_; ///< Coord idx of BB nitrogen int H_; ///< Coord idx of BB hydrogen - int CA_; ///< Coord idx of BB alpha carbon - int bridge1idx_; ///< Index in Residues_ of res this is bridged to - int bridge2idx_; ///< Index in Residues_ of res this is bridged to - char resChar_; ///< Single char residue ID - char TURN3_; ///< Character if part of 3 turn - char TURN4_; ///< Character if part of 4 turn - char TURN5_; ///< Character if part of 5 turn - char BRIDGE1_; ///< Bridge 1 character - char BRIDGE2_; ///< Bridge 2 character - char SHEET_; ///< Sheet character - bool isSelected_; ///< True if calculating SS for this residue. + int CA_; ///< Coord idx of BB alpha carbon + int bridge1idx_; ///< Index in Residues_ of res this is bridged to + int bridge2idx_; ///< Index in Residues_ of res this is bridged to + char resChar_; ///< Single char residue ID + char ssChar_[NSSCHARTYPE_]; ///< Character if part of N turn/bridge/sheet + bool isSelected_; ///< True if calculating SS for this residue. }; #endif From e1d6e51cb5af975e4096905edd39ac79004760cf Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 09:59:30 -0400 Subject: [PATCH 13/62] DRR - Cpptraj: Add offset --- src/Action_DSSP2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index a9567b55ed..ffd3322330 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -69,7 +69,7 @@ void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { } void Action_DSSP2::SSres::PrintSSchar() const { - mprintf("\t%8i %c %c %c %c\n", idx_, resChar_, + mprintf("\t%8i %c %c %c %c\n", idx_+1, resChar_, ssChar_[T3], ssChar_[T4], ssChar_[T5]); } From abef8dfb6bca59345c247f3ee095b97fbd620e6b Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 10:21:11 -0400 Subject: [PATCH 14/62] DRR - Cpptraj: Will use num to refer to things indexed in Topology and idx to refer to things indexed in Residues_ --- src/Action_DSSP2.cpp | 24 +++++++++++++----------- src/Action_DSSP2.h | 6 +++--- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index ffd3322330..dcb43a995a 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -11,7 +11,7 @@ Action_DSSP2::SSres::SSres() : chirality_(0), // pattern_(NOHBOND), sstype_(NONE), - idx_(-1), + num_(-1), C_(-1), O_(-1), N_(-1), @@ -69,7 +69,7 @@ void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { } void Action_DSSP2::SSres::PrintSSchar() const { - mprintf("\t%8i %c %c %c %c\n", idx_+1, resChar_, + mprintf("\t%8i %c %c %c %c\n", num_+1, resChar_, ssChar_[T3], ssChar_[T4], ssChar_[T5]); } @@ -229,16 +229,16 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) SSarrayType::iterator Res = Residues_.begin(); for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) { - if (Res->Idx() != -1) { + if (Res->Num() != -1) { // Residue has previously been set up. Check that indices match. - if (Res->Idx() != *ridx) { + if (Res->Num() != *ridx) { mprinterr("Error: Solute residue index %i does not match previously setup\n" - "Error: index %i\n", *ridx, Res->Idx()); + "Error: index %i\n", *ridx, Res->Num()); return Action::ERR; } } else { // Set up Residue. TODO also molecule index? - Res->SetIdx( *ridx ); + Res->SetNum( *ridx ); Res->SetResChar( setup.Top().Res(*ridx).SingleCharName() ); } Residue const& thisRes = setup.Top().Res( *ridx ); @@ -284,7 +284,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) // DEBUG - print each residue set up. for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) { - mprintf(" %8i", it->Idx() + 1); + mprintf(" %8i", it->Num() + 1); PrintAtom(setup.Top(), BB_C_, it->C()); PrintAtom(setup.Top(), BB_O_, it->O()); PrintAtom(setup.Top(), BB_N_, it->N()); @@ -359,12 +359,13 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) for (unsigned int riidx = 0; riidx != Residues_.size(); riidx++) { SSres& Resi = Residues_[riidx]; - mprintf("Res %8i %c", Resi.Idx()+1, Resi.ResChar()); // DBG + mprintf("Res %8i %c", Resi.Num()+1, Resi.ResChar()); // DBG + // Loop over all residues this residue is h-bonded to looking for turns. for (SSres::const_iterator rjidx = Resi.begin(); rjidx != Resi.end(); ++rjidx) { SSres& Resj = Residues_[*rjidx]; - mprintf(" %8i", Resj.Idx()+1); // DBG - int resDelta = Resj.Idx() - Resi.Idx(); + mprintf(" %8i", Resj.Num()+1); // DBG + int resDelta = Resj.Num() - Resi.Num(); if (resDelta < 0) resDelta = -resDelta; mprintf("(%4i)", resDelta); if (resDelta == 3) { @@ -389,7 +390,8 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) Residues_[riidx+4].SetTurn(T5); Residues_[riidx+5].SetEnd(T5); } - } + } // END loop over hbonded residues + // Look for bridges mprintf("\n"); // DBG } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 4cbc518f9a..988ea15ea1 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -86,7 +86,7 @@ class Action_DSSP2::SSres { typedef std::vector HbArrayType; public: SSres(); - int Idx() const { return idx_; } + int Num() const { return num_; } char ResChar() const { return resChar_; } bool IsSelected() const { return isSelected_; } int C() const { return C_; } @@ -111,7 +111,7 @@ class Action_DSSP2::SSres { void SetTurn(ssCharType); void SetEnd(ssCharType); - void SetIdx(int i) { idx_ = i; } + void SetNum(int i) { num_ = i; } void SetResChar(char c) { resChar_ = c; } void SetSelected(bool b) { isSelected_ = b; } void SetC(int i) { C_ = i; } @@ -134,7 +134,7 @@ class Action_DSSP2::SSres { int SScount_[NSSTYPE_]; ///< Hold count for each SS type //PatternType pattern_; ///< Assigned hbond pattern for this frame SStype sstype_; ///< SS assignment for this frame - int idx_; ///< Residue index in topology + int num_; ///< Residue index in Topology int C_; ///< Coord idx of BB carbon int O_; ///< Coord idx of BB oxygen int N_; ///< Coord idx of BB nitrogen From 51109cf1a358387bfba7fc1fc60a83c53524429b Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 11:09:02 -0400 Subject: [PATCH 15/62] DRR - Cpptraj: Look for previous/next residues --- src/Action_DSSP2.cpp | 33 +++++++++++++++++++++++++++++++-- src/Action_DSSP2.h | 8 +++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index dcb43a995a..9d6b90eea3 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -17,6 +17,8 @@ Action_DSSP2::SSres::SSres() : N_(-1), H_(-1), CA_(-1), + prevIdx_(-1), + nextIdx_(-1), bridge1idx_(-1), bridge2idx_(-1), resChar_(' '), @@ -229,6 +231,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) SSarrayType::iterator Res = Residues_.begin(); for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) { + Residue const& thisRes = setup.Top().Res( *ridx ); if (Res->Num() != -1) { // Residue has previously been set up. Check that indices match. if (Res->Num() != *ridx) { @@ -239,9 +242,34 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) } else { // Set up Residue. TODO also molecule index? Res->SetNum( *ridx ); - Res->SetResChar( setup.Top().Res(*ridx).SingleCharName() ); + Res->SetResChar( thisRes.SingleCharName() ); + // Determine the previous and next residues + int prevresnum = -1; + int nextresnum = -1; + for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) { + if ( setup.Top()[at].Element() != Atom::HYDROGEN ) { + for (Atom::bond_iterator ib = setup.Top()[at].bondbegin(); + ib != setup.Top()[at].bondend(); ++ib) + { + if ( setup.Top()[*ib].ResNum() < *ridx ) { + if (prevresnum != -1) + mprintf("Warning: Multiple previous residues for res %i\n", *ridx+1); + else + prevresnum = setup.Top()[*ib].ResNum(); + } else if ( setup.Top()[*ib].ResNum() > *ridx ) { + if (nextresnum != -1) + mprintf("Warning: Multiple next residues for res %i\n", *ridx+1); + else + nextresnum = setup.Top()[*ib].ResNum(); + } + } + } + } + mprintf("\t %8i < %8i < %8i\n", prevresnum+1, *ridx+1, nextresnum+1); + // Here we assume that residues are sequential! + if (prevresnum > -1) Res->SetPrevIdx( Res-Residues_.begin()-1 ); + if (nextresnum > -1) Res->SetNextIdx( Res-Residues_.begin()+1 ); } - Residue const& thisRes = setup.Top().Res( *ridx ); // Determine if this residue is selected if (Mask_.AtomsInCharMask(thisRes.FirstAtom(), thisRes.LastAtom())) { Res->SetSelected( true ); @@ -290,6 +318,7 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) PrintAtom(setup.Top(), BB_N_, it->N()); PrintAtom(setup.Top(), BB_H_, it->H()); PrintAtom(setup.Top(), BB_CA_, it->CA()); + mprintf(" Prev=%8i Next=%8i", it->PrevIdx(), it->NextIdx()); mprintf("\n"); } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 988ea15ea1..b4810504b7 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -94,6 +94,8 @@ class Action_DSSP2::SSres { int N() const { return N_; } int H() const { return H_; } int CA() const { return CA_; } + int PrevIdx() const { return prevIdx_; } + int NextIdx() const { return nextIdx_; } DataSet* Dset() const { return resDataSet_; } bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } @@ -119,6 +121,8 @@ class Action_DSSP2::SSres { void SetN(int i) { N_ = i; } void SetH(int i) { H_ = i; } void SetCA(int i) { CA_ = i; } + void SetPrevIdx(int i) { prevIdx_ = i; } + void SetNextIdx(int i) { nextIdx_ = i; } void SetDset(DataSet* d) { resDataSet_ = d; } /// Deselect this residue and reset coordinate indices. void Deselect(); @@ -138,8 +142,10 @@ class Action_DSSP2::SSres { int C_; ///< Coord idx of BB carbon int O_; ///< Coord idx of BB oxygen int N_; ///< Coord idx of BB nitrogen - int H_; ///< Coord idx of BB hydrogen + int H_; ///< Coord idx of BB hydrogen int CA_; ///< Coord idx of BB alpha carbon + int prevIdx_; ///< Index in Residues_ of previous residue + int nextIdx_; ///< Index in Residues_ of next residue int bridge1idx_; ///< Index in Residues_ of res this is bridged to int bridge2idx_; ///< Index in Residues_ of res this is bridged to char resChar_; ///< Single char residue ID From b916a365151293a579c95707ef9518347636b2ac Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 12:42:37 -0400 Subject: [PATCH 16/62] DRR - Cpptraj: bridge identification with set. Not sure I like this way... --- src/Action_DSSP2.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 9d6b90eea3..acde74e7c6 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1,5 +1,6 @@ #include // sqrt #include // std::fill +#include #include "Action_DSSP2.h" #include "CpptrajStdio.h" #include "DataSet.h" @@ -384,6 +385,10 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) #ifdef _OPENMP } // END pragma omp parallel #endif + typedef std::pair HbondPairType; + typedef std::set HbondMapType; + /// Map resi (CO) to resj (NH) potential bridge hbonds + HbondMapType bridgeHbonds; // Do basic assignment for (unsigned int riidx = 0; riidx != Residues_.size(); riidx++) { @@ -418,11 +423,50 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) Residues_[riidx+3].SetTurn(T5); Residues_[riidx+4].SetTurn(T5); Residues_[riidx+5].SetEnd(T5); + } else { + // Potential bridge hbond + bridgeHbonds.insert( HbondPairType(Resi.Num(), Resj.Num()) ); } } // END loop over hbonded residues // Look for bridges mprintf("\n"); // DBG } + mprintf("Potential bridge hbonds:\n"); + for (HbondMapType::const_iterator it = bridgeHbonds.begin(); it != bridgeHbonds.end(); ++it) + { + mprintf("\t%8i -- %8i\n", it->first+1, it->second+1); + // Start with the premise that this bond is part of one of the 4 potential bridge patterns. + // Then check if the compliment exists. + // Assume (i-1, j). Check for (j, i+1) PARALLEL + HbondMapType::iterator hb = bridgeHbonds.find( HbondPairType(it->second, it->first+2) ); + if (hb != bridgeHbonds.end()) { + mprintf("\t\t%i PARALLEL with %i\n", it->first+2, it->second+1); + continue; + } + // Assume (j, i+1). Check for (i-1, j) + hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first) ); + if (hb != bridgeHbonds.end()) { + mprintf("\t\t%i PARALLEL with %i\n", it->second+2, it->first+1); + continue; + } + // Assume (j-1, i). Check for (i, j+1) + //hb = bridgeHbonds.find( HbondPairType(it->second, it->first+2) ); + // Assume (i, j+1). Check for (j-1, i) + //hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first) ); + + // Assume (i,j). Look for (j,i) + hb = bridgeHbonds.find( HbondPairType(it->second, it->first) ); + if (hb != bridgeHbonds.end()) { + mprintf("\t\t%i ANTI-PARALLEL with %i\n", it->first+1, it->second+1); + continue; + } + // Assume (i-1, j+1). Look for (j-1, i+1) + hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first+2) ); + if (hb != bridgeHbonds.end()) { + mprintf("\t\t%i ANTI-PARALLEL with %i\n", it->first+2, it->second); + continue; + } + } // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) From 104ee3386d9c4779c568802864489832ebcd0109 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 13:09:45 -0400 Subject: [PATCH 17/62] DRR - Cpptraj: Original way of assigning beta --- src/Action_DSSP2.cpp | 44 +++++++++++++++++++++++++++++++++++++------- src/Action_DSSP2.h | 4 +++- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index acde74e7c6..f604e82750 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1,6 +1,6 @@ #include // sqrt #include // std::fill -#include +//#include // SET #include "Action_DSSP2.h" #include "CpptrajStdio.h" #include "DataSet.h" @@ -327,6 +327,13 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) return Action::OK; } +/** \return true if idx1 is CO-NH bonded to idx2. + */ +bool Action_DSSP2::isBonded(int idx1, int idx2) const { + if (idx1 < 0 || idx2 < 0) return false; + return ( std::find( Residues_[idx1].begin(), Residues_[idx1].end(), idx2 ) != Residues_[idx1].end() ); +} + // Action_DSSP2::DoAction() Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { @@ -385,19 +392,21 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) #ifdef _OPENMP } // END pragma omp parallel #endif +/* // SET typedef std::pair HbondPairType; typedef std::set HbondMapType; /// Map resi (CO) to resj (NH) potential bridge hbonds HbondMapType bridgeHbonds; +*/ // Do basic assignment - for (unsigned int riidx = 0; riidx != Residues_.size(); riidx++) + for (int riidx = 0; riidx != (int)Residues_.size(); riidx++) { - SSres& Resi = Residues_[riidx]; + SSres const& Resi = Residues_[riidx]; mprintf("Res %8i %c", Resi.Num()+1, Resi.ResChar()); // DBG // Loop over all residues this residue is h-bonded to looking for turns. for (SSres::const_iterator rjidx = Resi.begin(); rjidx != Resi.end(); ++rjidx) { - SSres& Resj = Residues_[*rjidx]; + SSres const& Resj = Residues_[*rjidx]; mprintf(" %8i", Resj.Num()+1); // DBG int resDelta = Resj.Num() - Resi.Num(); if (resDelta < 0) resDelta = -resDelta; @@ -423,14 +432,34 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) Residues_[riidx+3].SetTurn(T5); Residues_[riidx+4].SetTurn(T5); Residues_[riidx+5].SetEnd(T5); - } else { + } /*else { // SET // Potential bridge hbond bridgeHbonds.insert( HbondPairType(Resi.Num(), Resj.Num()) ); - } + }*/ } // END loop over hbonded residues - // Look for bridges + // Look for bridge to this res. + for (int rjidx = 0; rjidx != (int)Residues_.size(); rjidx++) + { + SSres const& Resj = Residues_[rjidx]; + // Only consider residues spaced more than 5 apart. + int resDelta = Resj.Num() - Resi.Num(); + if (resDelta < 0) resDelta = -resDelta; + if (resDelta > 5) { + if ( (isBonded(Resi.PrevIdx(), rjidx) && isBonded(rjidx, Resi.NextIdx())) || + (isBonded(Resj.PrevIdx(), riidx) && isBonded(riidx, Resj.NextIdx())) ) + { + mprintf("\tAssigning %i to parallel beta.\n", Resi.Num()+1); + } else if ( (isBonded(riidx, rjidx) && isBonded(rjidx, riidx)) || + (isBonded(Resi.PrevIdx(), Resj.NextIdx()) && isBonded(Resj.PrevIdx(), Resi.NextIdx())) ) + { + mprintf("\tAssigning %i to anti-parallel beta.\n", Resi.Num()+1); + } + } + } + mprintf("\n"); // DBG } +/* // SET mprintf("Potential bridge hbonds:\n"); for (HbondMapType::const_iterator it = bridgeHbonds.begin(); it != bridgeHbonds.end(); ++it) { @@ -467,6 +496,7 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) continue; } } +*/ // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index b4810504b7..a218570d3b 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -32,6 +32,8 @@ class Action_DSSP2 : public Action { Action::RetType DoAction(int, ActionFrame&); void Print() {} + bool isBonded(int,int) const; + class ElemHbond; class SSres; @@ -81,7 +83,6 @@ class Action_DSSP2 : public Action { bool printString_; ///< If true print 1 char per residue indicating ss type }; - class Action_DSSP2::SSres { typedef std::vector HbArrayType; public: @@ -131,6 +132,7 @@ class Action_DSSP2::SSres { /// Add hbond from this CO to specified NH void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } private: + HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] From 773f40c187898b1c548933f0731d50f9f20eb889 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 14:42:23 -0400 Subject: [PATCH 18/62] DRR - Cpptraj: Reenable the new bridge search but leave the old one active for comparison. Start doing strand assignment. --- src/Action_DSSP2.cpp | 93 +++++++++++++++++++++++++++++++++++++------- src/Action_DSSP2.h | 7 ++++ src/cpptrajdepend | 2 +- 3 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index f604e82750..79f2b9511e 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1,10 +1,11 @@ #include // sqrt #include // std::fill -//#include // SET +#include // SET #include "Action_DSSP2.h" #include "CpptrajStdio.h" #include "DataSet.h" #include "DistRoutines.h" +#include "Timer.h" // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : @@ -71,9 +72,32 @@ void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { } } +void Action_DSSP2::SSres::SetBridge(int idx, char bchar) { + if (bridge1idx_ == -1) { + bridge1idx_ = idx; + ssChar_[B1] = bchar; + } else if (bridge2idx_ == -1) { + bridge2idx_ = idx; + ssChar_[B2] = bchar; + } else + mprinterr("Error: Too many bridges for %i (to %i)\n", Num(), idx+1); +} + +bool Action_DSSP2::SSres::IsBridgedWith(int idx2) const { + if (bridge1idx_ == idx2) return true; + if (bridge2idx_ == idx2) return true; + return false; +} + +char Action_DSSP2::SSres::StrandChar() const { + // TODO ever b2? + return ssChar_[B1]; +} + void Action_DSSP2::SSres::PrintSSchar() const { - mprintf("\t%8i %c %c %c %c\n", num_+1, resChar_, - ssChar_[T3], ssChar_[T4], ssChar_[T5]); + mprintf("\t%8i %c %c %c %c %c(%8i) %c(%8i)\n", num_+1, resChar_, + ssChar_[T3], ssChar_[T4], ssChar_[T5], + ssChar_[B1], bridge1idx_+1, ssChar_[B2], bridge2idx_+1); } // ----- Action_DSSP2 ---------------------------------------------------------- @@ -334,6 +358,31 @@ bool Action_DSSP2::isBonded(int idx1, int idx2) const { return ( std::find( Residues_[idx1].begin(), Residues_[idx1].end(), idx2 ) != Residues_[idx1].end() ); } +void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char& currentStrandChar) { + // By convention, always make idx1 the lower one + int idx1, idx2; + if (idx1in < idx2in) { + idx1 = idx1in; + idx2 = idx2in; + } else { + idx1 = idx2in; + idx2 = idx1in; + } + SSres& Resi = Residues_[idx1]; + SSres& Resj = Residues_[idx2]; + // Do not duplicate bridges + if (Resi.IsBridgedWith(idx2)) return; + char prevStrandChar = Residues_[Resi.PrevIdx()].StrandChar(); + if (prevStrandChar == ' ') { + prevStrandChar = currentStrandChar; + currentStrandChar++; + } + if (btypeIn == ANTIPARALLEL) + prevStrandChar = toupper( prevStrandChar ); + Resi.SetBridge( idx2, prevStrandChar ); + Resj.SetBridge( idx1, prevStrandChar ); +} + // Action_DSSP2::DoAction() Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { @@ -392,12 +441,13 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) #ifdef _OPENMP } // END pragma omp parallel #endif -/* // SET + // SET typedef std::pair HbondPairType; typedef std::set HbondMapType; /// Map resi (CO) to resj (NH) potential bridge hbonds HbondMapType bridgeHbonds; -*/ + + Timer t_oldBridge; // Do basic assignment for (int riidx = 0; riidx != (int)Residues_.size(); riidx++) { @@ -432,12 +482,14 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) Residues_[riidx+3].SetTurn(T5); Residues_[riidx+4].SetTurn(T5); Residues_[riidx+5].SetEnd(T5); - } /*else { // SET + } else { // SET // Potential bridge hbond bridgeHbonds.insert( HbondPairType(Resi.Num(), Resj.Num()) ); - }*/ + } } // END loop over hbonded residues + // Look for bridge to this res. + t_oldBridge.Start(); for (int rjidx = 0; rjidx != (int)Residues_.size(); rjidx++) { SSres const& Resj = Residues_[rjidx]; @@ -448,19 +500,23 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) if ( (isBonded(Resi.PrevIdx(), rjidx) && isBonded(rjidx, Resi.NextIdx())) || (isBonded(Resj.PrevIdx(), riidx) && isBonded(riidx, Resj.NextIdx())) ) { - mprintf("\tAssigning %i to parallel beta.\n", Resi.Num()+1); + mprintf("\tAssigning %i to parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); } else if ( (isBonded(riidx, rjidx) && isBonded(rjidx, riidx)) || (isBonded(Resi.PrevIdx(), Resj.NextIdx()) && isBonded(Resj.PrevIdx(), Resi.NextIdx())) ) { - mprintf("\tAssigning %i to anti-parallel beta.\n", Resi.Num()+1); + mprintf("\tAssigning %i to anti-parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); } } } + t_oldBridge.Stop(); mprintf("\n"); // DBG } -/* // SET + // SET + Timer t_bridgeSearch; + t_bridgeSearch.Start(); mprintf("Potential bridge hbonds:\n"); + char currentStrandChar = 'a'; for (HbondMapType::const_iterator it = bridgeHbonds.begin(); it != bridgeHbonds.end(); ++it) { mprintf("\t%8i -- %8i\n", it->first+1, it->second+1); @@ -469,13 +525,15 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) // Assume (i-1, j). Check for (j, i+1) PARALLEL HbondMapType::iterator hb = bridgeHbonds.find( HbondPairType(it->second, it->first+2) ); if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i PARALLEL with %i\n", it->first+2, it->second+1); + mprintf("\t\t%i PARALLELa with %i (%i)\n", it->first+2, it->second+1, hb->first+1); + AssignBridge(it->first+1, it->second, PARALLEL, currentStrandChar); continue; } // Assume (j, i+1). Check for (i-1, j) hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first) ); if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i PARALLEL with %i\n", it->second+2, it->first+1); + mprintf("\t\t%i PARALLELb with %i (%i)\n", it->second+2, it->first+1, hb->first+1); + AssignBridge(it->second+1, it->first, PARALLEL, currentStrandChar); continue; } // Assume (j-1, i). Check for (i, j+1) @@ -486,17 +544,22 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) // Assume (i,j). Look for (j,i) hb = bridgeHbonds.find( HbondPairType(it->second, it->first) ); if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i ANTI-PARALLEL with %i\n", it->first+1, it->second+1); + mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", it->first+1, it->second+1, hb->first+1); + AssignBridge(it->first, it->second, ANTIPARALLEL, currentStrandChar); continue; } // Assume (i-1, j+1). Look for (j-1, i+1) hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first+2) ); if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i ANTI-PARALLEL with %i\n", it->first+2, it->second); + mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", it->first+2, it->second, hb->first+1); + AssignBridge(it->first+1, it->second-1, ANTIPARALLEL, currentStrandChar); continue; } } -*/ + t_bridgeSearch.Stop(); + t_oldBridge.WriteTiming(1, "OldBridge"); + t_bridgeSearch.WriteTiming(1, "BridgeSearch"); + // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index a218570d3b..c50e41ad96 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -34,6 +34,9 @@ class Action_DSSP2 : public Action { bool isBonded(int,int) const; + enum BridgeType { PARALLEL=0, ANTIPARALLEL }; + void AssignBridge(int, int, BridgeType, char&); + class ElemHbond; class SSres; @@ -104,6 +107,8 @@ class Action_DSSP2::SSres { bool HasNH() const { return (N_!=-1 && H_!=-1); } bool HasCA() const { return (CA_!=-1); } + bool IsBridgedWith(int) const; + char StrandChar() const; void PrintSSchar() const; typedef HbArrayType::const_iterator const_iterator; @@ -114,6 +119,8 @@ class Action_DSSP2::SSres { void SetTurn(ssCharType); void SetEnd(ssCharType); + void SetBridge(int, char); + void SetNum(int i) { num_ = i; } void SetResChar(char c) { resChar_ = c; } void SetSelected(bool b) { isSelected_ = b; } diff --git a/src/cpptrajdepend b/src/cpptrajdepend index 0254f5b8a3..4e061c975d 100644 --- a/src/cpptrajdepend +++ b/src/cpptrajdepend @@ -22,7 +22,7 @@ Action_CreateCrd.o : Action_CreateCrd.cpp Action.h ActionState.h Action_CreateCr Action_CreateReservoir.o : Action_CreateReservoir.cpp Action.h ActionState.h Action_CreateReservoir.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DNAionTracker.o : Action_DNAionTracker.cpp Action.h ActionState.h Action_DNAionTracker.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DSSP.o : Action_DSSP.cpp Action.h ActionState.h Action_DSSP.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h -Action_DSSP2.o : Action_DSSP2.cpp Action.h ActionState.h Action_DSSP2.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h +Action_DSSP2.o : Action_DSSP2.cpp Action.h ActionState.h Action_DSSP2.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Density.o : Action_Density.cpp Action.h ActionState.h Action_Density.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h OnlineVarT.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Diffusion.o : Action_Diffusion.cpp Action.h ActionState.h Action_Diffusion.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Dihedral.o : Action_Dihedral.cpp Action.h ActionState.h Action_Dihedral.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h DataSet_double.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h TorsionRoutines.h Vec3.h From ae0503f868c9c67aa37de18cdbc7c6e14747f5f8 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 23 Aug 2019 15:24:47 -0400 Subject: [PATCH 19/62] DRR - Cpptraj: Fix off by one issue in parallel type b assignment. Character assignment still seems off --- src/Action_DSSP2.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 79f2b9511e..7f4c93c386 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -509,7 +509,6 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) } } t_oldBridge.Stop(); - mprintf("\n"); // DBG } // SET @@ -532,8 +531,8 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) // Assume (j, i+1). Check for (i-1, j) hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first) ); if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i PARALLELb with %i (%i)\n", it->second+2, it->first+1, hb->first+1); - AssignBridge(it->second+1, it->first, PARALLEL, currentStrandChar); + mprintf("\t\t%i PARALLELb with %i (%i)\n", it->second, it->first+1, hb->first+1); + AssignBridge(it->second-1, it->first, PARALLEL, currentStrandChar); continue; } // Assume (j-1, i). Check for (i, j+1) From d6c0154831172daf7f885826c59bff6c112a2c8e Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Mon, 26 Aug 2019 08:44:36 -0400 Subject: [PATCH 20/62] DRR - Cpptraj: Start new approach, loop over hbonds instead of residues --- src/Action_DSSP.cpp | 22 +++++ src/Action_DSSP2.cpp | 213 ++++++++++++++++++++++++++++++++++++++----- src/Action_DSSP2.h | 3 + 3 files changed, 217 insertions(+), 21 deletions(-) diff --git a/src/Action_DSSP.cpp b/src/Action_DSSP.cpp index 25fdd7e161..5fc4463df6 100644 --- a/src/Action_DSSP.cpp +++ b/src/Action_DSSP.cpp @@ -2,6 +2,9 @@ #include "Action_DSSP.h" #include "CpptrajStdio.h" #include "DistRoutines.h" +#ifdef DSSPDEBUG +#include "Timer.h" +#endif /// Hbond energy calc prefactor for kcal/mol: q1*q2*E, 0.42*0.20*332 const double Action_DSSP::DSSP_fac = 27.888; @@ -323,6 +326,12 @@ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { int resi, resj; const double *C, *O, *H, *N; double rON, rCH, rOH, rCN, E; +# ifdef DSSPDEBUG + Timer t_total; + t_total.Start(); + Timer t_calchb; + t_calchb.Start(); +# endif // Determine C=O to H-N hydrogen bonds for each residue to each other residue #ifdef _OPENMP #pragma omp parallel private(resi,resj,C,O,H,N,rON, rCH, rOH, rCN, E) @@ -363,6 +372,11 @@ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { #ifdef _OPENMP } // END pragma omp parallel #endif +# ifdef DSSPDEBUG + t_calchb.Stop(); + Timer t_assign; + t_assign.Start(); +# endif // Determine Secondary Structure based on Hbonding pattern. // In case of structural overlap, priority is given to the structure first @@ -507,6 +521,14 @@ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { totalDS_[i]->Add(frameNum, &fvar); } ++Nframe_; +# ifdef DSSPDEBUG + t_assign.Start(); + t_total.Stop(); + + t_calchb.WriteTiming(1, "Calc Hbonds", t_total.Total()); + t_assign.WriteTiming(1, "Assignment ", t_total.Total()); + t_total.WriteTiming(0, "Total"); +# endif return Action::OK; } diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 7f4c93c386..4eb558257d 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -384,8 +384,12 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char } // Action_DSSP2::DoAction() -Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) +int Action_DSSP2::OverResidues(int frameNum, ActionFrame& frm) { + Timer t_overresidues; + t_overresidues.Start(); + Timer t_calchb; + t_calchb.Start(); int resi; int Nres = (int)Residues_.size(); // The first pass is used to determine hydrogen bonding @@ -428,10 +432,10 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) # endif ResCO.AddHbond( resj ); } -# ifdef DSSPDEBUG - else if (resDelta < 6) - mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -# endif +//# ifdef DSSPDEBUG +// else if (resDelta < 6) +// mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +//# endif } // END ResNH selected } // END residues spaced > 2 apart } // END inner loop over residues @@ -441,26 +445,31 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) #ifdef _OPENMP } // END pragma omp parallel #endif + t_calchb.Stop(); + + Timer t_assign; + t_assign.Start(); +/* // SET typedef std::pair HbondPairType; typedef std::set HbondMapType; /// Map resi (CO) to resj (NH) potential bridge hbonds HbondMapType bridgeHbonds; - - Timer t_oldBridge; +*/ +// Timer t_oldBridge; // Do basic assignment for (int riidx = 0; riidx != (int)Residues_.size(); riidx++) { SSres const& Resi = Residues_[riidx]; - mprintf("Res %8i %c", Resi.Num()+1, Resi.ResChar()); // DBG + mprintf("Res %8i %c\n", Resi.Num()+1, Resi.ResChar()); // DBG // Loop over all residues this residue is h-bonded to looking for turns. for (SSres::const_iterator rjidx = Resi.begin(); rjidx != Resi.end(); ++rjidx) { SSres const& Resj = Residues_[*rjidx]; - mprintf(" %8i", Resj.Num()+1); // DBG + mprintf("\t%8i %c", Resj.Num()+1, Resj.ResChar()); // DBG int resDelta = Resj.Num() - Resi.Num(); if (resDelta < 0) resDelta = -resDelta; - mprintf("(%4i)", resDelta); + mprintf("(%4i)\n", resDelta); if (resDelta == 3) { // 3-TURN Residues_[riidx ].SetBegin(T3); @@ -482,35 +491,35 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) Residues_[riidx+3].SetTurn(T5); Residues_[riidx+4].SetTurn(T5); Residues_[riidx+5].SetEnd(T5); - } else { // SET + } /*else { // SET // Potential bridge hbond bridgeHbonds.insert( HbondPairType(Resi.Num(), Resj.Num()) ); - } + }*/ } // END loop over hbonded residues // Look for bridge to this res. - t_oldBridge.Start(); +// t_oldBridge.Start(); for (int rjidx = 0; rjidx != (int)Residues_.size(); rjidx++) { SSres const& Resj = Residues_[rjidx]; - // Only consider residues spaced more than 5 apart. + // Only consider residues spaced more than 2 apart. int resDelta = Resj.Num() - Resi.Num(); if (resDelta < 0) resDelta = -resDelta; - if (resDelta > 5) { + if (resDelta > 2) { if ( (isBonded(Resi.PrevIdx(), rjidx) && isBonded(rjidx, Resi.NextIdx())) || (isBonded(Resj.PrevIdx(), riidx) && isBonded(riidx, Resj.NextIdx())) ) { - mprintf("\tAssigning %i to parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); + mprintf("\t\tAssigning %i to parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); } else if ( (isBonded(riidx, rjidx) && isBonded(rjidx, riidx)) || (isBonded(Resi.PrevIdx(), Resj.NextIdx()) && isBonded(Resj.PrevIdx(), Resi.NextIdx())) ) { - mprintf("\tAssigning %i to anti-parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); + mprintf("\t\tAssigning %i to anti-parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); } } } - t_oldBridge.Stop(); - mprintf("\n"); // DBG +// t_oldBridge.Stop(); } +/* // SET Timer t_bridgeSearch; t_bridgeSearch.Start(); @@ -556,10 +565,172 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) } } t_bridgeSearch.Stop(); - t_oldBridge.WriteTiming(1, "OldBridge"); - t_bridgeSearch.WriteTiming(1, "BridgeSearch"); +*/ + t_assign.Stop(); + t_overresidues.Stop(); + + t_calchb.WriteTiming(1, "Calc Hbonds", t_overresidues.Total()); + t_assign.WriteTiming(1, "Assignment ", t_overresidues.Total()); +// t_oldBridge.WriteTiming( 2, "OldBridge ", t_assign.Total()); +// t_bridgeSearch.WriteTiming(2, "BridgeSearch", t_assign.Total()); + t_overresidues.WriteTiming(0,"Over Residues"); + + return 0; +} + + + + + + + +int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) +{ + Timer t_overhbonds; + t_overhbonds.Start(); + typedef std::pair HbondPairType; + typedef std::set HbondMapType; + /// Map resi (CO) to resj (NH) potential bridge hbonds + HbondMapType CO_NH_bonds; + + Timer t_calchb; + t_calchb.Start(); + int resi; + int Nres = (int)Residues_.size(); + // The first pass is used to determine hydrogen bonding +#ifdef _OPENMP +#pragma omp parallel private(resi) +{ +#pragma omp for +#endif /* _OPENMP */ + for (resi = 0; resi < Nres; resi++) + { + if (Residues_[resi].IsSelected()) + { + SSres& ResCO = Residues_[resi]; + ResCO.Unassign(); + if (ResCO.HasCO()) + { + const double* Cxyz = frm.Frm().CRD( ResCO.C() ); + const double* Oxyz = frm.Frm().CRD( ResCO.O() ); + for (int resj = 0; resj < Nres; resj++) + { + // Only consider residues spaced more than 2 apart. + int resDelta = resi - resj; + if (resDelta < 0) resDelta = -resDelta; + if (resDelta > 2) { + SSres& ResNH = Residues_[resj]; + if (ResNH.IsSelected() && ResNH.HasNH()) + { + const double* Nxyz = frm.Frm().CRD( ResNH.N() ); + const double* Hxyz = frm.Frm().CRD( ResNH.H() ); + + double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); + double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); + double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); + double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); + + double E = DSSP_fac_ * (rON + rCH - rOH - rCN); + if (E < DSSP_cut_) { +# ifdef DSSPDEBUG + mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +# endif + CO_NH_bonds.insert( HbondPairType(resi, resj) ); + } +//# ifdef DSSPDEBUG +// else if (resDelta < 6) +// mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +//# endif + } // END ResNH selected + } // END residues spaced > 2 apart + } // END inner loop over residues + } // END has CO + } // END ResCO selected + } // END outer loop over residues +#ifdef _OPENMP +} // END pragma omp parallel +#endif + t_calchb.Stop(); + + Timer t_assign; + t_assign.Start(); + // Do basic assignment + char currentStrandChar = 'a'; + for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) + { + int riidx = hb0->first; + int rjidx = hb0->second; + SSres const& Resi = Residues_[riidx]; + SSres const& Resj = Residues_[rjidx]; + mprintf("Res %8i %c -- %8i %c", Resi.Num()+1, Resi.ResChar(), + Resj.Num()+1, Resj.ResChar()); // DBG + // Spacing between residues i and j + int resDelta = Resj.Num() - Resi.Num(); + if (resDelta < 0) resDelta = -resDelta; + mprintf("(%4i)\n", resDelta); + if (resDelta == 3) { + // 3-TURN + Residues_[riidx ].SetBegin(T3); + Residues_[riidx+1].SetTurn(T3); + Residues_[riidx+2].SetTurn(T3); + Residues_[riidx+3].SetEnd(T3); + } else if (resDelta == 4) { + // 4-TURN + Residues_[riidx ].SetBegin(T4); + Residues_[riidx+1].SetTurn(T4); + Residues_[riidx+2].SetTurn(T4); + Residues_[riidx+3].SetTurn(T4); + Residues_[riidx+4].SetEnd(T4); + } else if (resDelta == 5) { + // 5-TURN + Residues_[riidx ].SetBegin(T5); + Residues_[riidx+1].SetTurn(T5); + Residues_[riidx+2].SetTurn(T5); + Residues_[riidx+3].SetTurn(T5); + Residues_[riidx+4].SetTurn(T5); + Residues_[riidx+5].SetEnd(T5); + } + // Look for bridge. Start with the premise that this bond is part of one + // of the 4 potential bridge patterns, then check if the compliment exists. + // Assume (i-1, j). Check for (j, i+1) PARALLEL + HbondMapType::iterator hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); + AssignBridge(hb0->first+1, hb0->second, PARALLEL, currentStrandChar); + } + // Assume (j, i+1). Check for (i-1, j) + hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); + AssignBridge(hb0->second-1, hb0->first, PARALLEL, currentStrandChar); + } + // Assume (i,j). Look for (j,i) + hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); + AssignBridge(hb0->first, hb0->second, ANTIPARALLEL, currentStrandChar); + } + // Assume (i-1, j+1). Look for (j-1, i+1) + hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); + AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL, currentStrandChar); + } + } + t_assign.Stop(); + + t_overhbonds.Stop(); + t_calchb.WriteTiming(1, "Calc Hbonds", t_overhbonds.Total()); + t_assign.WriteTiming(1, "Assignment ", t_overhbonds.Total()); + t_overhbonds.WriteTiming(0,"Over Hbonds"); + return 0; +} +Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) +{ + OverResidues(frameNum, frm); + OverHbonds(frameNum, frm); // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) it->PrintSSchar(); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index c50e41ad96..3fa75fa8b1 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -34,6 +34,9 @@ class Action_DSSP2 : public Action { bool isBonded(int,int) const; + int OverResidues(int, ActionFrame&); + int OverHbonds(int, ActionFrame&); + enum BridgeType { PARALLEL=0, ANTIPARALLEL }; void AssignBridge(int, int, BridgeType, char&); From 84bc2004ba7aa8859d6a1f483c248d30333936a2 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Mon, 26 Aug 2019 09:56:55 -0400 Subject: [PATCH 21/62] DRR - Cpptraj: Not useful to assign ladder/sheet chars until full beta hbond network known --- src/Action_DSSP2.cpp | 86 +++++++++++++++++++++++++++++++++++++------- src/Action_DSSP2.h | 2 +- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 4eb558257d..f0a2234b59 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -358,6 +358,39 @@ bool Action_DSSP2::isBonded(int idx1, int idx2) const { return ( std::find( Residues_[idx1].begin(), Residues_[idx1].end(), idx2 ) != Residues_[idx1].end() ); } +void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { + // By convention, always make idx1 the lower one + int idx1, idx2; + if (idx1in < idx2in) { + idx1 = idx1in; + idx2 = idx2in; + } else { + idx1 = idx2in; + idx2 = idx1in; + } + SSres& Resi = Residues_[idx1]; + SSres& Resj = Residues_[idx2]; + + char bchar; + if (btypeIn == ANTIPARALLEL) { + mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); + bchar = 'A'; + } else { + mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); + bchar = 'p'; + } + + // Do not duplicate bridges + if (Resi.IsBridgedWith(idx2)) { + mprintf("\t\tAlready present.\n"); + return; + } + + Resi.SetBridge( idx2, bchar ); + Resj.SetBridge( idx1, bchar ); +} + +/* void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char& currentStrandChar) { // By convention, always make idx1 the lower one int idx1, idx2; @@ -368,20 +401,47 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char idx1 = idx2in; idx2 = idx1in; } + if (btypeIn == ANTIPARALLEL) + mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); + else + mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); SSres& Resi = Residues_[idx1]; SSres& Resj = Residues_[idx2]; // Do not duplicate bridges - if (Resi.IsBridgedWith(idx2)) return; - char prevStrandChar = Residues_[Resi.PrevIdx()].StrandChar(); - if (prevStrandChar == ' ') { - prevStrandChar = currentStrandChar; - currentStrandChar++; + if (Resi.IsBridgedWith(idx2)) { + mprintf("\t\tAlready present.\n"); + return; } + // Determine if we are already part of a ladder. + char ladderChar = ' '; + char resiLadderChar = Residues_[Resi.PrevIdx()].StrandChar(); + if (resiLadderChar == ' ') + resiLadderChar = Residues_[Resi.NextIdx()].StrandChar(); + char resjLadderChar = Residues_[Resj.PrevIdx()].StrandChar(); + if (resjLadderChar == ' ') + resjLadderChar = Residues_[Resj.NextIdx()].StrandChar(); + if (resiLadderChar == ' ' && resjLadderChar == ' ') { + // If both are blank, new ladder. + ladderChar = currentStrandChar; + ++currentStrandChar; + } else if (resiLadderChar != resjLadderChar) { + // They do not match. New ladder. + ladderChar = currentStrandChar; + ++currentStrandChar; + } else + ladderChar = resiLadderChar; + //else if (resiLadderChar != ' ') + // ladderChar = resiLadderChar; + //else + // ladderChar = resjLadderChar; + // Set the bridge; adjust character case if needed if (btypeIn == ANTIPARALLEL) - prevStrandChar = toupper( prevStrandChar ); - Resi.SetBridge( idx2, prevStrandChar ); - Resj.SetBridge( idx1, prevStrandChar ); + ladderChar = toupper( ladderChar ); + mprintf("\t\tResi strand %c, resj strand %c, ladder char %c\n", resiLadderChar, resjLadderChar, ladderChar); + Resi.SetBridge( idx2, ladderChar ); + Resj.SetBridge( idx1, ladderChar ); } +*/ // Action_DSSP2::DoAction() int Action_DSSP2::OverResidues(int frameNum, ActionFrame& frm) @@ -696,28 +756,28 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) HbondMapType::iterator hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); if (hb != CO_NH_bonds.end()) { mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); - AssignBridge(hb0->first+1, hb0->second, PARALLEL, currentStrandChar); + AssignBridge(hb0->first+1, hb0->second, PARALLEL); } // Assume (j, i+1). Check for (i-1, j) hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); if (hb != CO_NH_bonds.end()) { mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); - AssignBridge(hb0->second-1, hb0->first, PARALLEL, currentStrandChar); + AssignBridge(hb0->second-1, hb0->first, PARALLEL); } // Assume (i,j). Look for (j,i) hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); if (hb != CO_NH_bonds.end()) { mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); - AssignBridge(hb0->first, hb0->second, ANTIPARALLEL, currentStrandChar); + AssignBridge(hb0->first, hb0->second, ANTIPARALLEL); } // Assume (i-1, j+1). Look for (j-1, i+1) hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); if (hb != CO_NH_bonds.end()) { mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); - AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL, currentStrandChar); + AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL); } - } + } // END loop over Hbonds t_assign.Stop(); t_overhbonds.Stop(); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 3fa75fa8b1..c808cbdb45 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -38,7 +38,7 @@ class Action_DSSP2 : public Action { int OverHbonds(int, ActionFrame&); enum BridgeType { PARALLEL=0, ANTIPARALLEL }; - void AssignBridge(int, int, BridgeType, char&); + void AssignBridge(int, int, BridgeType); class ElemHbond; class SSres; From 8963199c7ca49a23c8505bfca3113f133d6d5dbd Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Mon, 26 Aug 2019 12:19:21 -0400 Subject: [PATCH 22/62] DRR - Cpptraj: Finish up new assignment. --- src/Action_DSSP2.cpp | 164 +++++++++++++++++++++++++++++++++++++++++-- src/Action_DSSP2.h | 8 +++ 2 files changed, 168 insertions(+), 4 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index f0a2234b59..07aecf61db 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -11,6 +11,7 @@ Action_DSSP2::SSres::SSres() : resDataSet_(0), chirality_(0), + bend_(0), // pattern_(NOHBOND), sstype_(NONE), num_(-1), @@ -30,6 +31,55 @@ Action_DSSP2::SSres::SSres() : std::fill(ssChar_, ssChar_ + NSSCHARTYPE_, ' '); } +Action_DSSP2::SSres::SSres(SSres const& rhs) : + CO_HN_Hbonds_(rhs.CO_HN_Hbonds_), + resDataSet_(rhs.resDataSet_), + chirality_(rhs.chirality_), + bend_(rhs.bend_), + sstype_(rhs.sstype_), + num_(rhs.num_), + C_(rhs.C_), + O_(rhs.O_), + N_(rhs.N_), + H_(rhs.H_), + CA_(rhs.CA_), + prevIdx_(rhs.prevIdx_), + nextIdx_(rhs.nextIdx_), + bridge1idx_(rhs.bridge1idx_), + bridge2idx_(rhs.bridge2idx_), + resChar_(rhs.resChar_), + isSelected_(rhs.isSelected_) +{ + std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); + std::copy(rhs.ssChar_, rhs.ssChar_ + NSSCHARTYPE_, ssChar_); +} + +Action_DSSP2::SSres& Action_DSSP2::SSres::operator=(SSres const& rhs) { + if (this == &rhs) return *this; + CO_HN_Hbonds_ = rhs.CO_HN_Hbonds_; + resDataSet_ = rhs.resDataSet_; + chirality_ = rhs.chirality_; + bend_ = rhs.bend_; + sstype_ = rhs.sstype_; + num_ = rhs.num_; + C_ = rhs.C_; + O_ = rhs.O_; + N_ = rhs.N_; + H_ = rhs.H_; + CA_ = rhs.CA_; + prevIdx_ = rhs.prevIdx_; + nextIdx_ = rhs.nextIdx_; + bridge1idx_ = rhs.bridge1idx_; + bridge2idx_ = rhs.bridge2idx_; + resChar_ = rhs.resChar_; + isSelected_ = rhs.isSelected_; + std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); + std::copy(rhs.ssChar_, rhs.ssChar_ + NSSCHARTYPE_, ssChar_); + return *this; +} + + + void Action_DSSP2::SSres::Deselect() { isSelected_ = false; C_ = -1; @@ -72,6 +122,24 @@ void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { } } +bool Action_DSSP2::SSres::HasTurnType(ssCharType typeIn) const { + if (ssChar_[typeIn] != ' ') return true; + return false; +} + +bool Action_DSSP2::SSres::HasTurnStart(ssCharType typeIn) const { + if (ssChar_[typeIn] == '>') return true; + return false; +} + +bool Action_DSSP2::SSres::HasNonTerminal(ssCharType typeIn) const { + if (ssChar_[typeIn] == ' ' || + ssChar_[typeIn] == '>' || + ssChar_[typeIn] == '<') + return false; + return true; +} + void Action_DSSP2::SSres::SetBridge(int idx, char bchar) { if (bridge1idx_ == -1) { bridge1idx_ = idx; @@ -83,6 +151,11 @@ void Action_DSSP2::SSres::SetBridge(int idx, char bchar) { mprinterr("Error: Too many bridges for %i (to %i)\n", Num(), idx+1); } +bool Action_DSSP2::SSres::HasBridge() const { + if (bridge1idx_ != -1) return true; + return false; +} + bool Action_DSSP2::SSres::IsBridgedWith(int idx2) const { if (bridge1idx_ == idx2) return true; if (bridge2idx_ == idx2) return true; @@ -95,9 +168,10 @@ char Action_DSSP2::SSres::StrandChar() const { } void Action_DSSP2::SSres::PrintSSchar() const { - mprintf("\t%8i %c %c %c %c %c(%8i) %c(%8i)\n", num_+1, resChar_, + mprintf("\t%8i %c %c %c %c %c(%8i) %c(%8i) %c\n", num_+1, resChar_, ssChar_[T3], ssChar_[T4], ssChar_[T5], - ssChar_[B1], bridge1idx_+1, ssChar_[B2], bridge2idx_+1); + ssChar_[B1], bridge1idx_+1, ssChar_[B2], bridge2idx_+1, + DSSP_char_[sstype_]); } // ----- Action_DSSP2 ---------------------------------------------------------- @@ -714,8 +788,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Timer t_assign; t_assign.Start(); - // Do basic assignment - char currentStrandChar = 'a'; + // Do basic assignment. for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) { int riidx = hb0->first; @@ -734,6 +807,8 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[riidx+1].SetTurn(T3); Residues_[riidx+2].SetTurn(T3); Residues_[riidx+3].SetEnd(T3); + Residues_[riidx+1].SetSS( TURN ); + Residues_[riidx+2].SetSS( TURN ); } else if (resDelta == 4) { // 4-TURN Residues_[riidx ].SetBegin(T4); @@ -741,6 +816,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[riidx+2].SetTurn(T4); Residues_[riidx+3].SetTurn(T4); Residues_[riidx+4].SetEnd(T4); + Residues_[riidx+1].SetSS( TURN ); + Residues_[riidx+2].SetSS( TURN ); + Residues_[riidx+3].SetSS( TURN ); } else if (resDelta == 5) { // 5-TURN Residues_[riidx ].SetBegin(T5); @@ -749,6 +827,10 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[riidx+3].SetTurn(T5); Residues_[riidx+4].SetTurn(T5); Residues_[riidx+5].SetEnd(T5); + Residues_[riidx+1].SetSS( TURN ); + Residues_[riidx+2].SetSS( TURN ); + Residues_[riidx+3].SetSS( TURN ); + Residues_[riidx+4].SetSS( TURN ); } // Look for bridge. Start with the premise that this bond is part of one // of the 4 potential bridge patterns, then check if the compliment exists. @@ -778,6 +860,80 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } } // END loop over Hbonds + + // Do SS assignment. + // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' + resi = 0; + while (resi < Nres) { + mprintf("Residue %i\n", resi+1); + SSres& Resi = Residues_[resi]; + int prevRes = resi - 1; + int nextRes = resi + 1; + if ( Resi.HasTurnStart(T4) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T4) ) + { + // Alpha helix. + mprintf("ALPHA helix starting at %i\n", resi+1); + while (Residues_[resi+1].HasTurnType(T4)) { + Residues_[resi++].SetSS( ALPHA ); + if (resi+1 == Nres) break; + } + } else if (Resi.HasBridge()) { + // Beta + if (nextRes == Nres || !Residues_[nextRes].HasBridge()) { + mprintf("Isolated BETA bridge at %i.\n", resi+1); + Resi.SetSS( BRIDGE ); + } else { + mprintf("Extended BETA bridge starting from %i\n", resi+1); + while (Residues_[resi].HasBridge()) { + Residues_[resi++].SetSS( EXTENDED ); + if (resi+1 == Nres) break; + } + } + } else if ( Resi.HasTurnStart(T3) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T3)) { + // 3-10 helix + mprintf("3-10 helix starting at %i\n", resi+1); + while (Residues_[resi+1].HasTurnType(T3)) { + Residues_[resi++].SetSS( H3_10 ); + if (resi == Nres) break; + } + } else if ( Resi.HasTurnStart(T5) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T5)) { + // PI helix + mprintf("PI helix starting at %i\n", resi+1); + while (Residues_[resi+1].HasTurnType(T5)) { + Residues_[resi++].SetSS( HPI ); + if (resi+1 == Nres) break; + } + } else { + if (Resi.SS() == NONE) { + // Check for Bend, which has lowest priority. + int im2 = resi - 2; + if (im2 > 0) { + int ip2 = resi + 2; + if (ip2 < Nres) { + if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { + const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); + const double* CA0 = frm.Frm().CRD(Resi.CA()); + const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); + Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); + Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); + CA1.Normalize(); + CA2.Normalize(); + // 1.221730476 rad = 70 degrees + if (CA1.Angle(CA2) > 1.221730476) { +# ifdef DSSPDEBUG + mprintf("DEBUG: Bend calc %i-%i-%i: %g rad.\n", resi-1, resi+1, resi+3, CA1.Angle(CA2)); +# endif + Resi.SetSS( BEND ); + } + } + } + } + } + resi++; + } + } + + t_assign.Stop(); t_overhbonds.Stop(); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index c808cbdb45..1b6fc9072a 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -93,6 +93,9 @@ class Action_DSSP2::SSres { typedef std::vector HbArrayType; public: SSres(); + SSres(SSres const&); + SSres& operator=(SSres const&); + SStype SS() const { return sstype_; } int Num() const { return num_; } char ResChar() const { return resChar_; } bool IsSelected() const { return isSelected_; } @@ -110,6 +113,10 @@ class Action_DSSP2::SSres { bool HasNH() const { return (N_!=-1 && H_!=-1); } bool HasCA() const { return (CA_!=-1); } + bool HasTurnType(ssCharType) const; + bool HasTurnStart(ssCharType) const; + bool HasNonTerminal(ssCharType) const; + bool HasBridge() const; bool IsBridgedWith(int) const; char StrandChar() const; void PrintSSchar() const; @@ -124,6 +131,7 @@ class Action_DSSP2::SSres { void SetBridge(int, char); + void SetSS(SStype s) { sstype_ = s; } void SetNum(int i) { num_ = i; } void SetResChar(char c) { resChar_ = c; } void SetSelected(bool b) { isSelected_ = b; } From 5d719067aa06be313a61c87c3b2afa52d2262302 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Mon, 26 Aug 2019 14:10:28 -0400 Subject: [PATCH 23/62] DRR - Cpptraj: Calc hbonds for adjacent residues since very tight turns are possible. When looking for beta, do not attempt to assign if 3-residue stretches would overlap. Report bend as degrees in debug. --- src/Action_DSSP2.cpp | 99 ++++++++++++++++++++++++++------------------ src/cpptrajdepend | 2 +- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 07aecf61db..f826622fe3 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -6,6 +6,9 @@ #include "DataSet.h" #include "DistRoutines.h" #include "Timer.h" +#ifdef DSSPDEBUG +#include "Constants.h" +#endif // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : @@ -442,6 +445,7 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { idx1 = idx2in; idx2 = idx1in; } + SSres& Resi = Residues_[idx1]; SSres& Resj = Residues_[idx2]; @@ -544,10 +548,8 @@ int Action_DSSP2::OverResidues(int frameNum, ActionFrame& frm) const double* Oxyz = frm.Frm().CRD( ResCO.O() ); for (int resj = 0; resj < Nres; resj++) { - // Only consider residues spaced more than 2 apart. - int resDelta = resi - resj; - if (resDelta < 0) resDelta = -resDelta; - if (resDelta > 2) { + // Need to consider adjacent residues since delta (2 res) turns possible + if (resi != resj) { SSres& ResNH = Residues_[resj]; if (ResNH.IsSelected() && ResNH.HasNH()) { @@ -714,8 +716,12 @@ int Action_DSSP2::OverResidues(int frameNum, ActionFrame& frm) - - +// TODO use Num()? +static inline int AbsResDelta(int idx1, int idx2) { + int resDelta = idx1 - idx2; + if (resDelta < 0) resDelta = -resDelta; + return resDelta; +} int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) @@ -749,10 +755,8 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) const double* Oxyz = frm.Frm().CRD( ResCO.O() ); for (int resj = 0; resj < Nres; resj++) { - // Only consider residues spaced more than 2 apart. - int resDelta = resi - resj; - if (resDelta < 0) resDelta = -resDelta; - if (resDelta > 2) { + // Need to consider adjacent residues since delta (2 res) turns possible + if (resi != resj) { SSres& ResNH = Residues_[resj]; if (ResNH.IsSelected() && ResNH.HasNH()) { @@ -799,8 +803,8 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Resj.Num()+1, Resj.ResChar()); // DBG // Spacing between residues i and j int resDelta = Resj.Num() - Resi.Num(); - if (resDelta < 0) resDelta = -resDelta; mprintf("(%4i)\n", resDelta); + // Check for H bond from CO i to NH i+n if (resDelta == 3) { // 3-TURN Residues_[riidx ].SetBegin(T3); @@ -834,31 +838,44 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } // Look for bridge. Start with the premise that this bond is part of one // of the 4 potential bridge patterns, then check if the compliment exists. - // Assume (i-1, j). Check for (j, i+1) PARALLEL - HbondMapType::iterator hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); - if (hb != CO_NH_bonds.end()) { - mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); - AssignBridge(hb0->first+1, hb0->second, PARALLEL); + HbondMapType::iterator hb; + // Here we want absolute value of spacing. + if (resDelta < 0) resDelta = -resDelta; + if (resDelta > 2) { + // Assume (i,j). Look for (j,i) + hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); + AssignBridge(hb0->first, hb0->second, ANTIPARALLEL); + } } - // Assume (j, i+1). Check for (i-1, j) - hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); - if (hb != CO_NH_bonds.end()) { - mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); - AssignBridge(hb0->second-1, hb0->first, PARALLEL); + resDelta = AbsResDelta(hb0->first+1, hb0->second-1); + if (resDelta > 2) { + // Assume (i-1, j+1). Look for (j-1, i+1) + hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); + AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL); + } } - // Assume (i,j). Look for (j,i) - hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); - if (hb != CO_NH_bonds.end()) { - mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); - AssignBridge(hb0->first, hb0->second, ANTIPARALLEL); + resDelta = AbsResDelta(hb0->first+1, hb0->second); + if (resDelta > 2) { + // Assume (i-1, j). Check for (j, i+1) PARALLEL + hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); + AssignBridge(hb0->first+1, hb0->second, PARALLEL); + } } - // Assume (i-1, j+1). Look for (j-1, i+1) - hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); - if (hb != CO_NH_bonds.end()) { - mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); - AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL); + resDelta = AbsResDelta(hb0->second-1, hb0->first); + if (resDelta > 2) { + // Assume (j, i+1). Check for (i-1, j) + hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); + if (hb != CO_NH_bonds.end()) { + mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); + AssignBridge(hb0->second-1, hb0->first, PARALLEL); + } } - } // END loop over Hbonds // Do SS assignment. @@ -882,11 +899,12 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) if (nextRes == Nres || !Residues_[nextRes].HasBridge()) { mprintf("Isolated BETA bridge at %i.\n", resi+1); Resi.SetSS( BRIDGE ); + resi++; } else { mprintf("Extended BETA bridge starting from %i\n", resi+1); while (Residues_[resi].HasBridge()) { Residues_[resi++].SetSS( EXTENDED ); - if (resi+1 == Nres) break; + if (resi == Nres) break; } } } else if ( Resi.HasTurnStart(T3) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T3)) { @@ -894,7 +912,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) mprintf("3-10 helix starting at %i\n", resi+1); while (Residues_[resi+1].HasTurnType(T3)) { Residues_[resi++].SetSS( H3_10 ); - if (resi == Nres) break; + if (resi+1 == Nres) break; } } else if ( Resi.HasTurnStart(T5) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T5)) { // PI helix @@ -907,7 +925,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) if (Resi.SS() == NONE) { // Check for Bend, which has lowest priority. int im2 = resi - 2; - if (im2 > 0) { + if (im2 > -1) { int ip2 = resi + 2; if (ip2 < Nres) { if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { @@ -918,11 +936,12 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); CA1.Normalize(); CA2.Normalize(); + double bAngle = CA1.Angle(CA2); +# ifdef DSSPDEBUG + mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); +# endif // 1.221730476 rad = 70 degrees - if (CA1.Angle(CA2) > 1.221730476) { -# ifdef DSSPDEBUG - mprintf("DEBUG: Bend calc %i-%i-%i: %g rad.\n", resi-1, resi+1, resi+3, CA1.Angle(CA2)); -# endif + if (bAngle > 1.221730476) { Resi.SetSS( BEND ); } } @@ -945,7 +964,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { - OverResidues(frameNum, frm); + //OverResidues(frameNum, frm); OverHbonds(frameNum, frm); // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) diff --git a/src/cpptrajdepend b/src/cpptrajdepend index 4e061c975d..1b2d5e045f 100644 --- a/src/cpptrajdepend +++ b/src/cpptrajdepend @@ -22,7 +22,7 @@ Action_CreateCrd.o : Action_CreateCrd.cpp Action.h ActionState.h Action_CreateCr Action_CreateReservoir.o : Action_CreateReservoir.cpp Action.h ActionState.h Action_CreateReservoir.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DNAionTracker.o : Action_DNAionTracker.cpp Action.h ActionState.h Action_DNAionTracker.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DSSP.o : Action_DSSP.cpp Action.h ActionState.h Action_DSSP.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h -Action_DSSP2.o : Action_DSSP2.cpp Action.h ActionState.h Action_DSSP2.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h +Action_DSSP2.o : Action_DSSP2.cpp Action.h ActionState.h Action_DSSP2.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Density.o : Action_Density.cpp Action.h ActionState.h Action_Density.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h OnlineVarT.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Diffusion.o : Action_Diffusion.cpp Action.h ActionState.h Action_Diffusion.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Dihedral.o : Action_Dihedral.cpp Action.h ActionState.h Action_Dihedral.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h DataSet_double.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h TorsionRoutines.h Vec3.h From 9ec9bdcba30e16ddc3b86dd32794020665a98860 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Mon, 26 Aug 2019 15:15:04 -0400 Subject: [PATCH 24/62] DRR - Cpptraj: In order to correctly detect when cooperative helices end, only assign one segment at a time. --- src/Action_DSSP2.cpp | 79 +++++++++++++++++++++++++------------------- src/Action_DSSP2.h | 4 +-- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index f826622fe3..fca52775a1 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -125,24 +125,37 @@ void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { } } +int Action_DSSP2::SSres::ssPriority(SStype typeIn) { + switch (typeIn) { + case ALPHA : return 8; + case BRIDGE : return 7; + case EXTENDED : return 6; + case H3_10 : return 5; + case HPI : return 4; + case TURN : return 3; + case BEND : return 2; + case NONE : return 1; + } + return 0; +} + +void Action_DSSP2::SSres::SetSS(SStype typeIn) { + if (ssPriority(typeIn) > ssPriority(sstype_)) + sstype_ = typeIn; +} + bool Action_DSSP2::SSres::HasTurnType(ssCharType typeIn) const { if (ssChar_[typeIn] != ' ') return true; return false; } bool Action_DSSP2::SSres::HasTurnStart(ssCharType typeIn) const { - if (ssChar_[typeIn] == '>') return true; + if (ssChar_[typeIn] == '>' || + ssChar_[typeIn] == 'X') + return true; return false; } -bool Action_DSSP2::SSres::HasNonTerminal(ssCharType typeIn) const { - if (ssChar_[typeIn] == ' ' || - ssChar_[typeIn] == '>' || - ssChar_[typeIn] == '<') - return false; - return true; -} - void Action_DSSP2::SSres::SetBridge(int idx, char bchar) { if (bridge1idx_ == -1) { bridge1idx_ = idx; @@ -881,46 +894,45 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Do SS assignment. // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' resi = 0; - while (resi < Nres) { + for (resi = 0; resi < Nres; resi++) + { mprintf("Residue %i\n", resi+1); SSres& Resi = Residues_[resi]; int prevRes = resi - 1; int nextRes = resi + 1; - if ( Resi.HasTurnStart(T4) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T4) ) + if ( Resi.HasTurnStart(T4) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T4) ) { // Alpha helix. mprintf("ALPHA helix starting at %i\n", resi+1); - while (Residues_[resi+1].HasTurnType(T4)) { - Residues_[resi++].SetSS( ALPHA ); - if (resi+1 == Nres) break; - } + Residues_[resi ].SetSS( ALPHA ); + Residues_[resi+1].SetSS( ALPHA ); + Residues_[resi+2].SetSS( ALPHA ); + Residues_[resi+3].SetSS( ALPHA ); } else if (Resi.HasBridge()) { // Beta - if (nextRes == Nres || !Residues_[nextRes].HasBridge()) { + if ( (prevRes > -1 && Residues_[prevRes].HasBridge()) || + (nextRes < Nres && Residues_[nextRes].HasBridge()) ) + { + mprintf("Extended BETA bridge at %i\n", resi+1); + Resi.SetSS( EXTENDED ); + } else { mprintf("Isolated BETA bridge at %i.\n", resi+1); Resi.SetSS( BRIDGE ); - resi++; - } else { - mprintf("Extended BETA bridge starting from %i\n", resi+1); - while (Residues_[resi].HasBridge()) { - Residues_[resi++].SetSS( EXTENDED ); - if (resi == Nres) break; - } } - } else if ( Resi.HasTurnStart(T3) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T3)) { + } else if ( Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { // 3-10 helix mprintf("3-10 helix starting at %i\n", resi+1); - while (Residues_[resi+1].HasTurnType(T3)) { - Residues_[resi++].SetSS( H3_10 ); - if (resi+1 == Nres) break; - } - } else if ( Resi.HasTurnStart(T5) && prevRes > 0 && Residues_[prevRes].HasTurnStart(T5)) { + Residues_[resi ].SetSS( H3_10 ); + Residues_[resi+1].SetSS( H3_10 ); + Residues_[resi+2].SetSS( H3_10 ); + } else if ( Resi.HasTurnStart(T5) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T5)) { // PI helix mprintf("PI helix starting at %i\n", resi+1); - while (Residues_[resi+1].HasTurnType(T5)) { - Residues_[resi++].SetSS( HPI ); - if (resi+1 == Nres) break; - } + Residues_[resi ].SetSS( HPI ); + Residues_[resi+1].SetSS( HPI ); + Residues_[resi+2].SetSS( HPI ); + Residues_[resi+3].SetSS( HPI ); + Residues_[resi+4].SetSS( HPI ); } else { if (Resi.SS() == NONE) { // Check for Bend, which has lowest priority. @@ -948,7 +960,6 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } } } - resi++; } } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 1b6fc9072a..2e0345df68 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -115,7 +115,6 @@ class Action_DSSP2::SSres { bool HasTurnType(ssCharType) const; bool HasTurnStart(ssCharType) const; - bool HasNonTerminal(ssCharType) const; bool HasBridge() const; bool IsBridgedWith(int) const; char StrandChar() const; @@ -131,7 +130,7 @@ class Action_DSSP2::SSres { void SetBridge(int, char); - void SetSS(SStype s) { sstype_ = s; } + void SetSS(SStype); void SetNum(int i) { num_ = i; } void SetResChar(char c) { resChar_ = c; } void SetSelected(bool b) { isSelected_ = b; } @@ -150,6 +149,7 @@ class Action_DSSP2::SSres { /// Add hbond from this CO to specified NH void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } private: + static inline int ssPriority(SStype); HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. From 601669b47617946f46d7a036c85ebc913bf534aa Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Mon, 26 Aug 2019 15:35:45 -0400 Subject: [PATCH 25/62] DRR - Cpptraj: only attempt to assign secondary structure if a higher priority assignment not already present --- src/Action_DSSP2.cpp | 18 ++++++++++-------- src/Action_DSSP2.h | 4 +++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index fca52775a1..4481ba6566 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -139,16 +139,16 @@ int Action_DSSP2::SSres::ssPriority(SStype typeIn) { return 0; } +int Action_DSSP2::SSres::SSpriority() const { + return ssPriority(sstype_); +} + void Action_DSSP2::SSres::SetSS(SStype typeIn) { + // TODO check if the priority check is necessary if (ssPriority(typeIn) > ssPriority(sstype_)) sstype_ = typeIn; } -bool Action_DSSP2::SSres::HasTurnType(ssCharType typeIn) const { - if (ssChar_[typeIn] != ' ') return true; - return false; -} - bool Action_DSSP2::SSres::HasTurnStart(ssCharType typeIn) const { if (ssChar_[typeIn] == '>' || ssChar_[typeIn] == 'X') @@ -893,6 +893,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Do SS assignment. // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' + // 8 7 6 5 4 3 2 resi = 0; for (resi = 0; resi < Nres; resi++) { @@ -900,6 +901,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) SSres& Resi = Residues_[resi]; int prevRes = resi - 1; int nextRes = resi + 1; + int priority = Resi.SSpriority(); if ( Resi.HasTurnStart(T4) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T4) ) { // Alpha helix. @@ -908,7 +910,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi+1].SetSS( ALPHA ); Residues_[resi+2].SetSS( ALPHA ); Residues_[resi+3].SetSS( ALPHA ); - } else if (Resi.HasBridge()) { + } else if (Resi.SS() != ALPHA && Resi.HasBridge()) { // Beta if ( (prevRes > -1 && Residues_[prevRes].HasBridge()) || (nextRes < Nres && Residues_[nextRes].HasBridge()) ) @@ -919,13 +921,13 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) mprintf("Isolated BETA bridge at %i.\n", resi+1); Resi.SetSS( BRIDGE ); } - } else if ( Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { + } else if ( priority < 6 && Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { // 3-10 helix mprintf("3-10 helix starting at %i\n", resi+1); Residues_[resi ].SetSS( H3_10 ); Residues_[resi+1].SetSS( H3_10 ); Residues_[resi+2].SetSS( H3_10 ); - } else if ( Resi.HasTurnStart(T5) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T5)) { + } else if ( priority < 5 && Resi.HasTurnStart(T5) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T5)) { // PI helix mprintf("PI helix starting at %i\n", resi+1); Residues_[resi ].SetSS( HPI ); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 2e0345df68..5b9911417a 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -113,7 +113,6 @@ class Action_DSSP2::SSres { bool HasNH() const { return (N_!=-1 && H_!=-1); } bool HasCA() const { return (CA_!=-1); } - bool HasTurnType(ssCharType) const; bool HasTurnStart(ssCharType) const; bool HasBridge() const; bool IsBridgedWith(int) const; @@ -148,7 +147,10 @@ class Action_DSSP2::SSres { void Unassign(); /// Add hbond from this CO to specified NH void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } + /// \return Relative priority of currently assigned SS type + int SSpriority() const; private: + /// \return Relative priority of given SS type static inline int ssPriority(SStype); HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). From c4d19e10424e206f2f6324dd6dd0cb21ca1b591e Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 11:39:45 -0400 Subject: [PATCH 26/62] DRR - Cpptraj: Remove over residues code, going with over hbonds --- src/Action_DSSP2.cpp | 196 ------------------------------------------- src/Action_DSSP2.h | 1 - 2 files changed, 197 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 4481ba6566..a5cf7fbc21 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -534,201 +534,6 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char } */ -// Action_DSSP2::DoAction() -int Action_DSSP2::OverResidues(int frameNum, ActionFrame& frm) -{ - Timer t_overresidues; - t_overresidues.Start(); - Timer t_calchb; - t_calchb.Start(); - int resi; - int Nres = (int)Residues_.size(); - // The first pass is used to determine hydrogen bonding -#ifdef _OPENMP -#pragma omp parallel private(resi) -{ -#pragma omp for -#endif /* _OPENMP */ - for (resi = 0; resi < Nres; resi++) - { - if (Residues_[resi].IsSelected()) - { - SSres& ResCO = Residues_[resi]; - ResCO.Unassign(); - if (ResCO.HasCO()) - { - const double* Cxyz = frm.Frm().CRD( ResCO.C() ); - const double* Oxyz = frm.Frm().CRD( ResCO.O() ); - for (int resj = 0; resj < Nres; resj++) - { - // Need to consider adjacent residues since delta (2 res) turns possible - if (resi != resj) { - SSres& ResNH = Residues_[resj]; - if (ResNH.IsSelected() && ResNH.HasNH()) - { - const double* Nxyz = frm.Frm().CRD( ResNH.N() ); - const double* Hxyz = frm.Frm().CRD( ResNH.H() ); - - double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); - double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); - double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); - double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); - - double E = DSSP_fac_ * (rON + rCH - rOH - rCN); - if (E < DSSP_cut_) { -# ifdef DSSPDEBUG - mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -# endif - ResCO.AddHbond( resj ); - } -//# ifdef DSSPDEBUG -// else if (resDelta < 6) -// mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -//# endif - } // END ResNH selected - } // END residues spaced > 2 apart - } // END inner loop over residues - } // END has CO - } // END ResCO selected - } // END outer loop over residues -#ifdef _OPENMP -} // END pragma omp parallel -#endif - t_calchb.Stop(); - - Timer t_assign; - t_assign.Start(); -/* - // SET - typedef std::pair HbondPairType; - typedef std::set HbondMapType; - /// Map resi (CO) to resj (NH) potential bridge hbonds - HbondMapType bridgeHbonds; -*/ -// Timer t_oldBridge; - // Do basic assignment - for (int riidx = 0; riidx != (int)Residues_.size(); riidx++) - { - SSres const& Resi = Residues_[riidx]; - mprintf("Res %8i %c\n", Resi.Num()+1, Resi.ResChar()); // DBG - // Loop over all residues this residue is h-bonded to looking for turns. - for (SSres::const_iterator rjidx = Resi.begin(); rjidx != Resi.end(); ++rjidx) - { - SSres const& Resj = Residues_[*rjidx]; - mprintf("\t%8i %c", Resj.Num()+1, Resj.ResChar()); // DBG - int resDelta = Resj.Num() - Resi.Num(); - if (resDelta < 0) resDelta = -resDelta; - mprintf("(%4i)\n", resDelta); - if (resDelta == 3) { - // 3-TURN - Residues_[riidx ].SetBegin(T3); - Residues_[riidx+1].SetTurn(T3); - Residues_[riidx+2].SetTurn(T3); - Residues_[riidx+3].SetEnd(T3); - } else if (resDelta == 4) { - // 4-TURN - Residues_[riidx ].SetBegin(T4); - Residues_[riidx+1].SetTurn(T4); - Residues_[riidx+2].SetTurn(T4); - Residues_[riidx+3].SetTurn(T4); - Residues_[riidx+4].SetEnd(T4); - } else if (resDelta == 5) { - // 5-TURN - Residues_[riidx ].SetBegin(T5); - Residues_[riidx+1].SetTurn(T5); - Residues_[riidx+2].SetTurn(T5); - Residues_[riidx+3].SetTurn(T5); - Residues_[riidx+4].SetTurn(T5); - Residues_[riidx+5].SetEnd(T5); - } /*else { // SET - // Potential bridge hbond - bridgeHbonds.insert( HbondPairType(Resi.Num(), Resj.Num()) ); - }*/ - } // END loop over hbonded residues - - // Look for bridge to this res. -// t_oldBridge.Start(); - for (int rjidx = 0; rjidx != (int)Residues_.size(); rjidx++) - { - SSres const& Resj = Residues_[rjidx]; - // Only consider residues spaced more than 2 apart. - int resDelta = Resj.Num() - Resi.Num(); - if (resDelta < 0) resDelta = -resDelta; - if (resDelta > 2) { - if ( (isBonded(Resi.PrevIdx(), rjidx) && isBonded(rjidx, Resi.NextIdx())) || - (isBonded(Resj.PrevIdx(), riidx) && isBonded(riidx, Resj.NextIdx())) ) - { - mprintf("\t\tAssigning %i to parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); - } else if ( (isBonded(riidx, rjidx) && isBonded(rjidx, riidx)) || - (isBonded(Resi.PrevIdx(), Resj.NextIdx()) && isBonded(Resj.PrevIdx(), Resi.NextIdx())) ) - { - mprintf("\t\tAssigning %i to anti-parallel beta with %i.\n", Resi.Num()+1, Resj.Num()+1); - } - } - } -// t_oldBridge.Stop(); - } -/* - // SET - Timer t_bridgeSearch; - t_bridgeSearch.Start(); - mprintf("Potential bridge hbonds:\n"); - char currentStrandChar = 'a'; - for (HbondMapType::const_iterator it = bridgeHbonds.begin(); it != bridgeHbonds.end(); ++it) - { - mprintf("\t%8i -- %8i\n", it->first+1, it->second+1); - // Start with the premise that this bond is part of one of the 4 potential bridge patterns. - // Then check if the compliment exists. - // Assume (i-1, j). Check for (j, i+1) PARALLEL - HbondMapType::iterator hb = bridgeHbonds.find( HbondPairType(it->second, it->first+2) ); - if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i PARALLELa with %i (%i)\n", it->first+2, it->second+1, hb->first+1); - AssignBridge(it->first+1, it->second, PARALLEL, currentStrandChar); - continue; - } - // Assume (j, i+1). Check for (i-1, j) - hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first) ); - if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i PARALLELb with %i (%i)\n", it->second, it->first+1, hb->first+1); - AssignBridge(it->second-1, it->first, PARALLEL, currentStrandChar); - continue; - } - // Assume (j-1, i). Check for (i, j+1) - //hb = bridgeHbonds.find( HbondPairType(it->second, it->first+2) ); - // Assume (i, j+1). Check for (j-1, i) - //hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first) ); - - // Assume (i,j). Look for (j,i) - hb = bridgeHbonds.find( HbondPairType(it->second, it->first) ); - if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", it->first+1, it->second+1, hb->first+1); - AssignBridge(it->first, it->second, ANTIPARALLEL, currentStrandChar); - continue; - } - // Assume (i-1, j+1). Look for (j-1, i+1) - hb = bridgeHbonds.find( HbondPairType(it->second-2, it->first+2) ); - if (hb != bridgeHbonds.end()) { - mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", it->first+2, it->second, hb->first+1); - AssignBridge(it->first+1, it->second-1, ANTIPARALLEL, currentStrandChar); - continue; - } - } - t_bridgeSearch.Stop(); -*/ - t_assign.Stop(); - t_overresidues.Stop(); - - t_calchb.WriteTiming(1, "Calc Hbonds", t_overresidues.Total()); - t_assign.WriteTiming(1, "Assignment ", t_overresidues.Total()); -// t_oldBridge.WriteTiming( 2, "OldBridge ", t_assign.Total()); -// t_bridgeSearch.WriteTiming(2, "BridgeSearch", t_assign.Total()); - t_overresidues.WriteTiming(0,"Over Residues"); - - return 0; -} - - - // TODO use Num()? static inline int AbsResDelta(int idx1, int idx2) { int resDelta = idx1 - idx2; @@ -736,7 +541,6 @@ static inline int AbsResDelta(int idx1, int idx2) { return resDelta; } - int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) { Timer t_overhbonds; diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 5b9911417a..b675962d69 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -34,7 +34,6 @@ class Action_DSSP2 : public Action { bool isBonded(int,int) const; - int OverResidues(int, ActionFrame&); int OverHbonds(int, ActionFrame&); enum BridgeType { PARALLEL=0, ANTIPARALLEL }; From 147da92005c76bf7d53d27c27a0b9982d3ccc404 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 11:44:04 -0400 Subject: [PATCH 27/62] DRR - Cpptraj: Not storing hbonds in SSres anymore --- src/Action_DSSP2.cpp | 11 ----------- src/Action_DSSP2.h | 10 ---------- 2 files changed, 21 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index a5cf7fbc21..ceba9a3d59 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -35,7 +35,6 @@ Action_DSSP2::SSres::SSres() : } Action_DSSP2::SSres::SSres(SSres const& rhs) : - CO_HN_Hbonds_(rhs.CO_HN_Hbonds_), resDataSet_(rhs.resDataSet_), chirality_(rhs.chirality_), bend_(rhs.bend_), @@ -59,7 +58,6 @@ Action_DSSP2::SSres::SSres(SSres const& rhs) : Action_DSSP2::SSres& Action_DSSP2::SSres::operator=(SSres const& rhs) { if (this == &rhs) return *this; - CO_HN_Hbonds_ = rhs.CO_HN_Hbonds_; resDataSet_ = rhs.resDataSet_; chirality_ = rhs.chirality_; bend_ = rhs.bend_; @@ -93,7 +91,6 @@ void Action_DSSP2::SSres::Deselect() { } void Action_DSSP2::SSres::Unassign() { - CO_HN_Hbonds_.clear(); // pattern_ = NOHBOND; sstype_ = NONE; bridge1idx_ = -1; @@ -441,13 +438,6 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) return Action::OK; } -/** \return true if idx1 is CO-NH bonded to idx2. - */ -bool Action_DSSP2::isBonded(int idx1, int idx2) const { - if (idx1 < 0 || idx2 < 0) return false; - return ( std::find( Residues_[idx1].begin(), Residues_[idx1].end(), idx2 ) != Residues_[idx1].end() ); -} - void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { // By convention, always make idx1 the lower one int idx1, idx2; @@ -781,7 +771,6 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { - //OverResidues(frameNum, frm); OverHbonds(frameNum, frm); // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index b675962d69..eaa0a3e202 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -32,8 +32,6 @@ class Action_DSSP2 : public Action { Action::RetType DoAction(int, ActionFrame&); void Print() {} - bool isBonded(int,int) const; - int OverHbonds(int, ActionFrame&); enum BridgeType { PARALLEL=0, ANTIPARALLEL }; @@ -89,7 +87,6 @@ class Action_DSSP2 : public Action { }; class Action_DSSP2::SSres { - typedef std::vector HbArrayType; public: SSres(); SSres(SSres const&); @@ -118,10 +115,6 @@ class Action_DSSP2::SSres { char StrandChar() const; void PrintSSchar() const; - typedef HbArrayType::const_iterator const_iterator; - const_iterator begin() const { return CO_HN_Hbonds_.begin(); } - const_iterator end() const { return CO_HN_Hbonds_.end(); } - void SetBegin(ssCharType); void SetTurn(ssCharType); void SetEnd(ssCharType); @@ -144,15 +137,12 @@ class Action_DSSP2::SSres { void Deselect(); /// Reset hbonds, pattern and SS type assignments void Unassign(); - /// Add hbond from this CO to specified NH - void AddHbond(int i) { CO_HN_Hbonds_.push_back( i ); } /// \return Relative priority of currently assigned SS type int SSpriority() const; private: /// \return Relative priority of given SS type static inline int ssPriority(SStype); - HbArrayType CO_HN_Hbonds_; ///< This res C-O hbonded to these res H-N (indicies into Residues_). DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] double bend_; ///< Angle CA[i-2, i, i+2] From 237d999201cbba5137089801182a32b39a1bfd84 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 11:52:25 -0400 Subject: [PATCH 28/62] DRR - Cpptraj: Improve comments --- src/Action_DSSP2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index ceba9a3d59..52a2051a02 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -535,6 +535,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) { Timer t_overhbonds; t_overhbonds.Start(); + // ----- Determine hydrogen bonding ------------ typedef std::pair HbondPairType; typedef std::set HbondMapType; /// Map resi (CO) to resj (NH) potential bridge hbonds @@ -544,7 +545,6 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) t_calchb.Start(); int resi; int Nres = (int)Residues_.size(); - // The first pass is used to determine hydrogen bonding #ifdef _OPENMP #pragma omp parallel private(resi) { @@ -599,7 +599,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Timer t_assign; t_assign.Start(); - // Do basic assignment. + // ----- Do basic assignment ------------------- for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) { int riidx = hb0->first; @@ -685,7 +685,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } } // END loop over Hbonds - // Do SS assignment. + // ----- Do SS assignment ---------------------- // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' // 8 7 6 5 4 3 2 resi = 0; From 36ed550e47f96d33100a2199d09013f2a9aade51 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 12:57:15 -0400 Subject: [PATCH 29/62] DRR - Cpptraj: Start beta bulge calculation --- src/Action_DSSP2.cpp | 113 ++++++++++++++++++++++++------------------- src/Action_DSSP2.h | 2 + 2 files changed, 64 insertions(+), 51 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 52a2051a02..82cc9cc7c7 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -686,8 +686,8 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } // END loop over Hbonds // ----- Do SS assignment ---------------------- - // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' - // 8 7 6 5 4 3 2 + // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' None + // 8 7 6 5 4 3 2 1 resi = 0; for (resi = 0; resi < Nres; resi++) { @@ -704,60 +704,71 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi+1].SetSS( ALPHA ); Residues_[resi+2].SetSS( ALPHA ); Residues_[resi+3].SetSS( ALPHA ); - } else if (Resi.SS() != ALPHA && Resi.HasBridge()) { - // Beta - if ( (prevRes > -1 && Residues_[prevRes].HasBridge()) || - (nextRes < Nres && Residues_[nextRes].HasBridge()) ) - { - mprintf("Extended BETA bridge at %i\n", resi+1); - Resi.SetSS( EXTENDED ); - } else { - mprintf("Isolated BETA bridge at %i.\n", resi+1); - Resi.SetSS( BRIDGE ); + } else if (Resi.SS() != ALPHA) { + bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); + bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); + if (Resi.HasBridge()) { + // Beta + if ( prevHasBridge || nextHasBridge ) // TODO or previous is assigned bridge/extended? + { + mprintf("Extended BETA bridge at %i\n", resi+1); + Resi.SetSS( EXTENDED ); + } else { + mprintf("Isolated BETA bridge at %i.\n", resi+1); + Resi.SetSS( BRIDGE ); + } + } else if (prevHasBridge && nextHasBridge) { + // Potential Beta bulge. Check other strand. + int presb1 = Residues_[prevRes].Bridge1Idx(); + int presb2 = Residues_[prevRes].Bridge2Idx(); + int nresb1 = Residues_[nextRes].Bridge1Idx(); + int nresb2 = Residues_[nextRes].Bridge2Idx(); + mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); } - } else if ( priority < 6 && Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { - // 3-10 helix - mprintf("3-10 helix starting at %i\n", resi+1); - Residues_[resi ].SetSS( H3_10 ); - Residues_[resi+1].SetSS( H3_10 ); - Residues_[resi+2].SetSS( H3_10 ); - } else if ( priority < 5 && Resi.HasTurnStart(T5) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T5)) { - // PI helix - mprintf("PI helix starting at %i\n", resi+1); - Residues_[resi ].SetSS( HPI ); - Residues_[resi+1].SetSS( HPI ); - Residues_[resi+2].SetSS( HPI ); - Residues_[resi+3].SetSS( HPI ); - Residues_[resi+4].SetSS( HPI ); - } else { - if (Resi.SS() == NONE) { - // Check for Bend, which has lowest priority. - int im2 = resi - 2; - if (im2 > -1) { - int ip2 = resi + 2; - if (ip2 < Nres) { - if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { - const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); - const double* CA0 = frm.Frm().CRD(Resi.CA()); - const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); - Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); - Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); - CA1.Normalize(); - CA2.Normalize(); - double bAngle = CA1.Angle(CA2); -# ifdef DSSPDEBUG - mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); -# endif - // 1.221730476 rad = 70 degrees - if (bAngle > 1.221730476) { - Resi.SetSS( BEND ); + // Update priority in case we have done beta assignment + priority = Resi.SSpriority(); + if ( priority < 6 && Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { + // 3-10 helix + mprintf("3-10 helix starting at %i\n", resi+1); + Residues_[resi ].SetSS( H3_10 ); + Residues_[resi+1].SetSS( H3_10 ); + Residues_[resi+2].SetSS( H3_10 ); + } else if ( priority < 5 && Resi.HasTurnStart(T5) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T5)) { + // PI helix + mprintf("PI helix starting at %i\n", resi+1); + Residues_[resi ].SetSS( HPI ); + Residues_[resi+1].SetSS( HPI ); + Residues_[resi+2].SetSS( HPI ); + Residues_[resi+3].SetSS( HPI ); + Residues_[resi+4].SetSS( HPI ); + } else if (priority < 2) { + // Check for Bend, which has lowest priority. + int im2 = resi - 2; + if (im2 > -1) { + int ip2 = resi + 2; + if (ip2 < Nres) { + if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { + const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); + const double* CA0 = frm.Frm().CRD(Resi.CA()); + const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); + Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); + Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); + CA1.Normalize(); + CA2.Normalize(); + double bAngle = CA1.Angle(CA2); +# ifdef DSSPDEBUG + mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); +# endif + // 1.221730476 rad = 70 degrees + if (bAngle > 1.221730476) { + Resi.SetSS( BEND ); + } } } } - } } - } - } + } // END not alpha + } // END loop over residues t_assign.Stop(); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index eaa0a3e202..1184f9300a 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -102,6 +102,8 @@ class Action_DSSP2::SSres { int CA() const { return CA_; } int PrevIdx() const { return prevIdx_; } int NextIdx() const { return nextIdx_; } + int Bridge1Idx() const { return bridge1idx_; } + int Bridge2Idx() const { return bridge2idx_; } DataSet* Dset() const { return resDataSet_; } bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } From 186502bafbfa13fc43362886a6a74581c08f4248 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 14:31:32 -0400 Subject: [PATCH 30/62] DRR - Cpptraj: Finish up initial beta bulge detection - make sure extended is set for both strands. Do not check for priority in SetSS() - rely on calling algorithm to do the right thing. --- src/Action_DSSP2.cpp | 75 +++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 82cc9cc7c7..ea2d58b548 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -142,7 +142,7 @@ int Action_DSSP2::SSres::SSpriority() const { void Action_DSSP2::SSres::SetSS(SStype typeIn) { // TODO check if the priority check is necessary - if (ssPriority(typeIn) > ssPriority(sstype_)) + //if (ssPriority(typeIn) > ssPriority(sstype_)) sstype_ = typeIn; } @@ -531,6 +531,17 @@ static inline int AbsResDelta(int idx1, int idx2) { return resDelta; } +static inline void SetMin(int& resGapSize, int& sres0, int& sres1, int Nres, int Pres) +{ + int itmp = AbsResDelta(Nres, Pres); + if (itmp < resGapSize) { + resGapSize = itmp; + sres0 = std::min(Pres, Nres); + sres1 = std::max(Pres, Nres); + } +} + + int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) { Timer t_overhbonds; @@ -705,25 +716,51 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi+2].SetSS( ALPHA ); Residues_[resi+3].SetSS( ALPHA ); } else if (Resi.SS() != ALPHA) { - bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); - bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); - if (Resi.HasBridge()) { - // Beta - if ( prevHasBridge || nextHasBridge ) // TODO or previous is assigned bridge/extended? - { - mprintf("Extended BETA bridge at %i\n", resi+1); - Resi.SetSS( EXTENDED ); - } else { - mprintf("Isolated BETA bridge at %i.\n", resi+1); - Resi.SetSS( BRIDGE ); + if (priority < 6) { + bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); + bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); + if (Resi.HasBridge()) { + // Regular Beta + if ( prevHasBridge || nextHasBridge ) // TODO or previous is assigned bridge/extended? + { + mprintf("Extended BETA bridge at %i\n", resi+1); + Resi.SetSS( EXTENDED ); + } else { + mprintf("Isolated BETA bridge at %i.\n", resi+1); + Resi.SetSS( BRIDGE ); + } + } else if (prevHasBridge && nextHasBridge) { + // Potential Beta bulge. Check other strand. + int presb1 = Residues_[prevRes].Bridge1Idx(); + int presb2 = Residues_[prevRes].Bridge2Idx(); + int nresb1 = Residues_[nextRes].Bridge1Idx(); + int nresb2 = Residues_[nextRes].Bridge2Idx(); + mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); + // The largest allowed gap in the other strand is 5 residues. + // Since we know that next res and previous res both have at + // least 1 bridge, Next B1 - Prev B1 can be the gap to beat. + int resGapSize = AbsResDelta(nresb1, presb1); + int sres0 = std::min(presb1, nresb1); + int sres1 = std::max(presb1, nresb1); + if (presb2 != -1) + SetMin( resGapSize, sres0, sres1, nresb1, presb2 ); + if (nresb2 != -1) { + SetMin( resGapSize, sres0, sres1, nresb2, presb1 ); + if (presb2 != -1) + SetMin( resGapSize, sres0, sres1, nresb2, presb2 ); + } + mprintf("Min res gap size on other strand = %i (%i to %i)\n", resGapSize, sres0, sres1); + // Minimum allowed gap is 4 residues in between, so 5 residues total. + if (resGapSize < 6) { + mprintf("Beta bulge.\n"); + Residues_[prevRes].SetSS( EXTENDED ); + Resi.SetSS( EXTENDED ); + Residues_[nextRes].SetSS( EXTENDED ); + // Set extended on other strand as well + for (int sres = sres0; sres != sres1; sres++) + Residues_[sres].SetSS( EXTENDED ); + } } - } else if (prevHasBridge && nextHasBridge) { - // Potential Beta bulge. Check other strand. - int presb1 = Residues_[prevRes].Bridge1Idx(); - int presb2 = Residues_[prevRes].Bridge2Idx(); - int nresb1 = Residues_[nextRes].Bridge1Idx(); - int nresb2 = Residues_[nextRes].Bridge2Idx(); - mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); } // Update priority in case we have done beta assignment priority = Resi.SSpriority(); From 3cf5e809bbbd5634148d313b13cab61803b0fd5a Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 14:34:16 -0400 Subject: [PATCH 31/62] DRR - Cpptraj: Update comments --- src/Action_DSSP2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index ea2d58b548..1a3d67b5c9 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -717,6 +717,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi+3].SetSS( ALPHA ); } else if (Resi.SS() != ALPHA) { if (priority < 6) { + // Check for Beta structure bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); if (Resi.HasBridge()) { @@ -761,8 +762,8 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[sres].SetSS( EXTENDED ); } } - } - // Update priority in case we have done beta assignment + } // END check for Beta structure + // Update priority in case we have done Beta assignment priority = Resi.SSpriority(); if ( priority < 6 && Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { // 3-10 helix From d7c47d2f44caaf9477ba08441e1b80d4da0b0a73 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 15:04:31 -0400 Subject: [PATCH 32/62] DRR - Cpptraj: Finish up print stuff --- src/Action_DSSP.cpp | 2 +- src/Action_DSSP2.cpp | 147 +++++++++++++++++++++++++++++++++++++------ src/Action_DSSP2.h | 5 +- 3 files changed, 132 insertions(+), 22 deletions(-) diff --git a/src/Action_DSSP.cpp b/src/Action_DSSP.cpp index 5fc4463df6..f7ed65c2dc 100644 --- a/src/Action_DSSP.cpp +++ b/src/Action_DSSP.cpp @@ -522,7 +522,7 @@ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { } ++Nframe_; # ifdef DSSPDEBUG - t_assign.Start(); + t_assign.Stop(); t_total.Stop(); t_calchb.WriteTiming(1, "Calc Hbonds", t_total.Total()); diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 1a3d67b5c9..d5cd8a8507 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -10,6 +10,24 @@ #include "Constants.h" #endif +/** From the original Kabsch & Sander 1983 paper. Obtained via + * q1 = 0.42e + * q2 = 0.20e + * f = 332 (approximate conversion factor to get energies in kcal/mol) + * fac = q1*q2*f + */ +const double Action_DSSP2::DSSP_fac_ = 27.888; + +const double Action_DSSP2::DSSP_cut_ = -0.5; + +const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; + +const char* Action_DSSP2::SSchar_[] = { "0", "b", "B", "G", "H", "I", "T", "S" }; + +const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; + +const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"; + // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : resDataSet_(0), @@ -34,6 +52,14 @@ Action_DSSP2::SSres::SSres() : std::fill(ssChar_, ssChar_ + NSSCHARTYPE_, ' '); } +void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString) { + SScount_[sstype_]++; + if (useString) + resDataSet_->Add(frameNum, SSchar_[sstype_]); + else + resDataSet_->Add(frameNum, &sstype_); +} + Action_DSSP2::SSres::SSres(SSres const& rhs) : resDataSet_(rhs.resDataSet_), chirality_(rhs.chirality_), @@ -189,24 +215,6 @@ void Action_DSSP2::SSres::PrintSSchar() const { // ----- Action_DSSP2 ---------------------------------------------------------- -/** From the original Kabsch & Sander 1983 paper. Obtained via - * q1 = 0.42e - * q2 = 0.20e - * f = 332 (approximate conversion factor to get energies in kcal/mol) - * fac = q1*q2*f - */ -const double Action_DSSP2::DSSP_fac_ = 27.888; - -const double Action_DSSP2::DSSP_cut_ = -0.5; - -const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; - -const char* Action_DSSP2::SSchar_[] = { "0", "b", "B", "G", "H", "I", "T", "S" }; - -const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; - -const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"; - Action_DSSP2::Action_DSSP2() : debug_(0), BB_N_("N"), @@ -699,7 +707,6 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // ----- Do SS assignment ---------------------- // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' None // 8 7 6 5 4 3 2 1 - resi = 0; for (resi = 0; resi < Nres; resi++) { mprintf("Residue %i\n", resi+1); @@ -807,7 +814,23 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } } // END not alpha } // END loop over residues - + + // ----- Store data for each res. Get stats ---- + int totalSS[NSSTYPE_]; + std::fill( totalSS, totalSS + NSSTYPE_, 0 ); + int Nselected = 0; + for (resi=0; resi < Nres; resi++) { + if (Residues_[resi].IsSelected()) { + Residues_[resi].AccumulateData(frameNum, printString_); + Nselected++; + } + } + for (int i = 0; i < NSSTYPE_; i++) { + float fvar = (float)totalSS[i]; + fvar /= (float)Nselected; + totalDS_[i]->Add(frameNum, &fvar); + } + t_assign.Stop(); @@ -826,3 +849,87 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) it->PrintSSchar(); return Action::OK; } + +// Action_DSSP::Print() +void Action_DSSP2::Print() { + if (dsetname_.empty()) return; + // Try not to print empty residues. Find the minimum and maximum residue + // for which there is data. Output res nums start from 1. + int min_res = -1; + int max_res = -1; + for (int resi = 0; resi != (int)Residues_.size(); resi++) { + if (Residues_[resi].Dset() != 0) { + if (min_res < 0) min_res = resi; + if (resi > max_res) max_res = resi; + } + } + if (min_res < 0 || max_res < min_res) { + mprinterr("Error: No residues have SS data.\n"); + return; + } + // Calculate average of each SS type across all residues. + if (dsspFile_ != 0) { + std::vector dsspData_(NSSTYPE_); + Dimension Xdim( min_res + 1, 1, "Residue" ); + MetaData md(dsetname_, "avgss", MetaData::NOT_TS); + // Set up a dataset for each SS type. TODO: NONE type? + for (int ss = 1; ss < NSSTYPE_; ss++) { + md.SetIdx(ss); + md.SetLegend( SSname_[ss] ); + dsspData_[ss] = Init_.DSL().AddSet(DataSet::DOUBLE, md); + dsspData_[ss]->SetDim(Dimension::X, Xdim); + dsspFile_->AddDataSet( dsspData_[ss] ); + } + + // Calc the avg SS type for each residue that has data. + int idx = 0; + for (int resi = min_res; resi < max_res+1; resi++) { + if (Residues_[resi].Dset() != 0) { + int Nframe = 0; + for (int ss = 0; ss < NSSTYPE_; ss++) + Nframe += Residues_[resi].SScount((SStype)ss); + for (int ss = 1; ss < NSSTYPE_; ss++) { + double avg = (double)Residues_[resi].SScount((SStype)ss); + avg /= (double)Nframe; + dsspData_[ss]->Add(idx, &avg); + } + ++idx; + } + } + } + // Print out SS assignment like PDB + if (assignout_ != 0) { + int total = 0; + int startRes = -1; + std::string resLine, ssLine; + for (int resi = min_res; resi < max_res+1; resi++) { + if (startRes == -1) startRes = resi; + // Convert residue name. + resLine += Residue::ConvertResName( Residues_[resi].Dset()->Meta().Legend() ); + // Figure out which SS element is dominant for res if selected + if (Residues_[resi].Dset() != 0) { + int dominantType = 0; + int ssmax = 0; + for (int ss = 0; ss < NSSTYPE_; ss++) { + if ( Residues_[resi].SScount((SStype)ss) > ssmax ) { + ssmax = Residues_[resi].SScount((SStype)ss); + dominantType = ss; + } + } + ssLine += DSSP_char_[dominantType]; + } else + ssLine += '-'; + total++; + if ((total % 50) == 0 || resi == max_res) { + assignout_->Printf("%-8i %s\n", startRes+1, resLine.c_str()); + assignout_->Printf("%8s %s\n\n", " ", ssLine.c_str()); + startRes = -1; + resLine.clear(); + ssLine.clear(); + } else if ((total % 10) == 0) { + resLine += ' '; + ssLine += ' '; + } + } + } +} diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 1184f9300a..031bd5384d 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -30,7 +30,7 @@ class Action_DSSP2 : public Action { Action::RetType Init(ArgList&, ActionInit&, int); Action::RetType Setup(ActionSetup&); Action::RetType DoAction(int, ActionFrame&); - void Print() {} + void Print(); int OverHbonds(int, ActionFrame&); @@ -91,6 +91,7 @@ class Action_DSSP2::SSres { SSres(); SSres(SSres const&); SSres& operator=(SSres const&); + int SScount(SStype t) const { return SScount_[t]; } SStype SS() const { return sstype_; } int Num() const { return num_; } char ResChar() const { return resChar_; } @@ -123,6 +124,8 @@ class Action_DSSP2::SSres { void SetBridge(int, char); + void AccumulateData(int, bool); + void SetSS(SStype); void SetNum(int i) { num_ = i; } void SetResChar(char c) { resChar_ = c; } From 778d057d0c9e454afd40fb22c54d11157d229009 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 15:33:33 -0400 Subject: [PATCH 33/62] DRR - Cpptraj: Make sure ALPHA is not overwritten during BETA assignment. Count previous residue assigned EXTENDED via bulge when determining extended beta. Look for 3-10 and PI helices after ALPHA and BETA assignment to prevent too-short regions of these helices from being assigned. --- src/Action_DSSP2.cpp | 121 +++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 46 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index d5cd8a8507..3f37c117b2 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -724,12 +724,14 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi+3].SetSS( ALPHA ); } else if (Resi.SS() != ALPHA) { if (priority < 6) { + // Priority < 6 means not alpha or beta assigned yet. // Check for Beta structure bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); if (Resi.HasBridge()) { - // Regular Beta - if ( prevHasBridge || nextHasBridge ) // TODO or previous is assigned bridge/extended? + // Regular Beta. Check if previous is assigned EXTENDED in case it + // was assigned via a Beta bulge. + if ( prevHasBridge || nextHasBridge || Residues_[prevRes].SS() == EXTENDED ) { mprintf("Extended BETA bridge at %i\n", resi+1); Resi.SetSS( EXTENDED ); @@ -761,59 +763,86 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Minimum allowed gap is 4 residues in between, so 5 residues total. if (resGapSize < 6) { mprintf("Beta bulge.\n"); - Residues_[prevRes].SetSS( EXTENDED ); + if (Residues_[prevRes].SS() != ALPHA) + Residues_[prevRes].SetSS( EXTENDED ); Resi.SetSS( EXTENDED ); - Residues_[nextRes].SetSS( EXTENDED ); + if (Residues_[nextRes].SS() != ALPHA) + Residues_[nextRes].SetSS( EXTENDED ); // Set extended on other strand as well for (int sres = sres0; sres != sres1; sres++) - Residues_[sres].SetSS( EXTENDED ); + if (Residues_[sres].SS() != ALPHA) + Residues_[sres].SetSS( EXTENDED ); } } } // END check for Beta structure - // Update priority in case we have done Beta assignment - priority = Resi.SSpriority(); - if ( priority < 6 && Resi.HasTurnStart(T3) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T3)) { - // 3-10 helix - mprintf("3-10 helix starting at %i\n", resi+1); - Residues_[resi ].SetSS( H3_10 ); - Residues_[resi+1].SetSS( H3_10 ); - Residues_[resi+2].SetSS( H3_10 ); - } else if ( priority < 5 && Resi.HasTurnStart(T5) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T5)) { - // PI helix - mprintf("PI helix starting at %i\n", resi+1); - Residues_[resi ].SetSS( HPI ); - Residues_[resi+1].SetSS( HPI ); - Residues_[resi+2].SetSS( HPI ); - Residues_[resi+3].SetSS( HPI ); - Residues_[resi+4].SetSS( HPI ); - } else if (priority < 2) { - // Check for Bend, which has lowest priority. - int im2 = resi - 2; - if (im2 > -1) { - int ip2 = resi + 2; - if (ip2 < Nres) { - if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { - const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); - const double* CA0 = frm.Frm().CRD(Resi.CA()); - const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); - Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); - Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); - CA1.Normalize(); - CA2.Normalize(); - double bAngle = CA1.Angle(CA2); -# ifdef DSSPDEBUG - mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); -# endif - // 1.221730476 rad = 70 degrees - if (bAngle > 1.221730476) { - Resi.SetSS( BEND ); - } - } + } // END not alpha + } // END loop over residues + + // Check for 3-10 helices. Do this separately so we dont assign regions + // that are too small because other residues have already been assigned. + for (resi = 1; resi < Nres-2; resi++) { + if (Residues_[resi ].SSpriority() < 6 && + Residues_[resi+1].SSpriority() < 6 && + Residues_[resi+2].SSpriority() < 6 && + Residues_[resi ].HasTurnStart(T3) && + Residues_[resi-1].HasTurnStart(T3)) + { + // 3-10 helix + mprintf("3-10 helix starting at %i\n", resi+1); + Residues_[resi ].SetSS( H3_10 ); + Residues_[resi+1].SetSS( H3_10 ); + Residues_[resi+2].SetSS( H3_10 ); + } + } + // Check for PI helices, similar to 3-10 helices. + for (resi = 1; resi < Nres-4; resi++) { + if (Residues_[resi ].SSpriority() < 5 && + Residues_[resi+1].SSpriority() < 5 && + Residues_[resi+2].SSpriority() < 5 && + Residues_[resi+3].SSpriority() < 5 && + Residues_[resi+4].SSpriority() < 5 && + Residues_[resi ].HasTurnStart(T5) && + Residues_[resi-1].HasTurnStart(T5)) + { + // PI helix + mprintf("PI helix starting at %i\n", resi+1); + Residues_[resi ].SetSS( HPI ); + Residues_[resi+1].SetSS( HPI ); + Residues_[resi+2].SetSS( HPI ); + Residues_[resi+3].SetSS( HPI ); + Residues_[resi+4].SetSS( HPI ); + } + } + // Check for bends. Only do if no other assignment. + for (resi = 0; resi < Nres; resi++) { + if (Residues_[resi].IsSelected() && Residues_[resi].SS() == NONE) + { + int im2 = resi - 2; + if (im2 > -1) { + int ip2 = resi + 2; + if (ip2 < Nres) { + SSres& Resi = Residues_[resi]; + if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { + const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); + const double* CA0 = frm.Frm().CRD(Resi.CA()); + const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); + Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); + Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); + CA1.Normalize(); + CA2.Normalize(); + double bAngle = CA1.Angle(CA2); +# ifdef DSSPDEBUG + mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); +# endif + // 1.221730476 rad = 70 degrees + if (bAngle > 1.221730476) { + Resi.SetSS( BEND ); } } + } } - } // END not alpha - } // END loop over residues + } // END selected and no assignment + } // ----- Store data for each res. Get stats ---- int totalSS[NSSTYPE_]; From 11ce50f12c893099d5ffcfcc3be23d75b9b6b0e5 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 27 Aug 2019 15:48:09 -0400 Subject: [PATCH 34/62] DRR - Cpptraj: Fix output string char and gnuplot z label --- src/Action_DSSP2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 3f37c117b2..ac311f529a 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -22,11 +22,11 @@ const double Action_DSSP2::DSSP_cut_ = -0.5; const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; -const char* Action_DSSP2::SSchar_[] = { "0", "b", "B", "G", "H", "I", "T", "S" }; +const char* Action_DSSP2::SSchar_[] = { "0", "E", "B", "G", "H", "I", "T", "S" }; const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; -const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"; +const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Ext,Bridge,3-10,Alpha,Pi,Turn,Bend"; // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : From faa5124589c6f51f802b5d75b772490a66a8f8da Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 10:47:09 -0400 Subject: [PATCH 35/62] DRR - Cpptraj: Add new test with dssp2 code. Actually does the same assignment as old code because of parallel beta sheets. --- test/Test_DSSP/RunTest.sh | 19 +++++++++++++++++-- test/Test_DSSP/ftufabi.assign.dat.save | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 test/Test_DSSP/ftufabi.assign.dat.save diff --git a/test/Test_DSSP/RunTest.sh b/test/Test_DSSP/RunTest.sh index 264748dd5d..107aa3ce48 100755 --- a/test/Test_DSSP/RunTest.sh +++ b/test/Test_DSSP/RunTest.sh @@ -5,7 +5,7 @@ # Clean CleanFiles cpptraj.in dssp.dat dssp.dat.sum dssp.sum.agr dssp.gnu \ dssp2.gnu dssp2.sum.agr total.agr assign.4.dat \ - DSSP.assign.dat + DSSP.assign.dat ftufabi.assign.dat INPUT="-i cpptraj.in" TOP="../DPDP.parm7" @@ -62,10 +62,25 @@ parm test.4.pdb trajin test.4.pdb dssp N4 assignout assign.4.dat EOF - RunCpptraj "Secstruct (DSSP): SS assign output test" + RunCpptraj "$UNITNAME" DoTest assign.4.dat.save assign.4.dat fi +# FtuFabI Assignment test +UNITNAME='FtuFabI Assignment test' +CheckFor maxthreads 1 +if [ $? -eq 0 ] ; then + TOP='' + cat > cpptraj.in < Date: Wed, 28 Aug 2019 11:14:42 -0400 Subject: [PATCH 36/62] DRR - Cpptraj: Split up turn and beta - no beta char for now since we arent actually calculating it yet. Clean up a bit. --- src/Action_DSSP2.cpp | 75 +++++++++++++++++++++++--------------------- src/Action_DSSP2.h | 57 +++++++++++++++------------------ 2 files changed, 64 insertions(+), 68 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index ac311f529a..bc5d7a82bd 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1,5 +1,5 @@ #include // sqrt -#include // std::fill +#include // std::fill, std::min, std::max #include // SET #include "Action_DSSP2.h" #include "CpptrajStdio.h" @@ -33,7 +33,6 @@ Action_DSSP2::SSres::SSres() : resDataSet_(0), chirality_(0), bend_(0), -// pattern_(NOHBOND), sstype_(NONE), num_(-1), C_(-1), @@ -44,12 +43,14 @@ Action_DSSP2::SSres::SSres() : prevIdx_(-1), nextIdx_(-1), bridge1idx_(-1), + b1type_(NO_BRIDGE), bridge2idx_(-1), + b2type_(NO_BRIDGE), resChar_(' '), isSelected_(false) { std::fill(SScount_, SScount_ + NSSTYPE_, 0); - std::fill(ssChar_, ssChar_ + NSSCHARTYPE_, ' '); + std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); } void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString) { @@ -74,12 +75,14 @@ Action_DSSP2::SSres::SSres(SSres const& rhs) : prevIdx_(rhs.prevIdx_), nextIdx_(rhs.nextIdx_), bridge1idx_(rhs.bridge1idx_), + b1type_(rhs.b1type_), bridge2idx_(rhs.bridge2idx_), + b2type_(rhs.b2type_), resChar_(rhs.resChar_), isSelected_(rhs.isSelected_) { std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); - std::copy(rhs.ssChar_, rhs.ssChar_ + NSSCHARTYPE_, ssChar_); + std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); } Action_DSSP2::SSres& Action_DSSP2::SSres::operator=(SSres const& rhs) { @@ -97,16 +100,17 @@ Action_DSSP2::SSres& Action_DSSP2::SSres::operator=(SSres const& rhs) { prevIdx_ = rhs.prevIdx_; nextIdx_ = rhs.nextIdx_; bridge1idx_ = rhs.bridge1idx_; + b1type_ = rhs.b1type_; bridge2idx_ = rhs.bridge2idx_; + b2type_ = rhs.b2type_; resChar_ = rhs.resChar_; isSelected_ = rhs.isSelected_; std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); - std::copy(rhs.ssChar_, rhs.ssChar_ + NSSCHARTYPE_, ssChar_); + std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); return *this; } - void Action_DSSP2::SSres::Deselect() { isSelected_ = false; C_ = -1; @@ -117,34 +121,33 @@ void Action_DSSP2::SSres::Deselect() { } void Action_DSSP2::SSres::Unassign() { -// pattern_ = NOHBOND; sstype_ = NONE; bridge1idx_ = -1; bridge2idx_ = -1; - std::fill(ssChar_, ssChar_ + NSSCHARTYPE_, ' '); + std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); } /** Set turn beginning. */ -void Action_DSSP2::SSres::SetBegin(ssCharType typeIn) { - if (ssChar_[typeIn] == '<') - ssChar_[typeIn] = 'X'; +void Action_DSSP2::SSres::SetBegin(TurnType typeIn) { + if (turnChar_[typeIn] == '<') + turnChar_[typeIn] = 'X'; else - ssChar_[typeIn] = '>'; + turnChar_[typeIn] = '>'; } -void Action_DSSP2::SSres::SetEnd(ssCharType typeIn) { - if (ssChar_[typeIn] == '>') - ssChar_[typeIn] = 'X'; +void Action_DSSP2::SSres::SetEnd(TurnType typeIn) { + if (turnChar_[typeIn] == '>') + turnChar_[typeIn] = 'X'; else - ssChar_[typeIn] = '<'; + turnChar_[typeIn] = '<'; } -void Action_DSSP2::SSres::SetTurn(ssCharType typeIn) { +void Action_DSSP2::SSres::SetTurn(TurnType typeIn) { // Do not overwrite an existing end character - if (ssChar_[typeIn] == ' ') { - if (typeIn == T3) ssChar_[typeIn] = '3'; - else if (typeIn == T4) ssChar_[typeIn] = '4'; - else if (typeIn == T5) ssChar_[typeIn] = '5'; + if (turnChar_[typeIn] == ' ') { + if (typeIn == T3) turnChar_[typeIn] = '3'; + else if (typeIn == T4) turnChar_[typeIn] = '4'; + else if (typeIn == T5) turnChar_[typeIn] = '5'; } } @@ -172,20 +175,20 @@ void Action_DSSP2::SSres::SetSS(SStype typeIn) { sstype_ = typeIn; } -bool Action_DSSP2::SSres::HasTurnStart(ssCharType typeIn) const { - if (ssChar_[typeIn] == '>' || - ssChar_[typeIn] == 'X') +bool Action_DSSP2::SSres::HasTurnStart(TurnType typeIn) const { + if (turnChar_[typeIn] == '>' || + turnChar_[typeIn] == 'X') return true; return false; } -void Action_DSSP2::SSres::SetBridge(int idx, char bchar) { +void Action_DSSP2::SSres::SetBridge(int idx, BridgeType btypeIn) { if (bridge1idx_ == -1) { bridge1idx_ = idx; - ssChar_[B1] = bchar; + b1type_ = btypeIn; } else if (bridge2idx_ == -1) { bridge2idx_ = idx; - ssChar_[B2] = bchar; + b2type_ = btypeIn; } else mprinterr("Error: Too many bridges for %i (to %i)\n", Num(), idx+1); } @@ -201,15 +204,16 @@ bool Action_DSSP2::SSres::IsBridgedWith(int idx2) const { return false; } -char Action_DSSP2::SSres::StrandChar() const { +/*char Action_DSSP2::SSres::StrandChar() const { // TODO ever b2? return ssChar_[B1]; -} +}*/ void Action_DSSP2::SSres::PrintSSchar() const { + static const char btypeChar[] = { ' ', 'p', 'A' }; mprintf("\t%8i %c %c %c %c %c(%8i) %c(%8i) %c\n", num_+1, resChar_, - ssChar_[T3], ssChar_[T4], ssChar_[T5], - ssChar_[B1], bridge1idx_+1, ssChar_[B2], bridge2idx_+1, + turnChar_[T3], turnChar_[T4], turnChar_[T5], + btypeChar[b1type_], bridge1idx_+1, btypeChar[b2type_], bridge2idx_+1, DSSP_char_[sstype_]); } @@ -460,13 +464,12 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { SSres& Resi = Residues_[idx1]; SSres& Resj = Residues_[idx2]; - char bchar; if (btypeIn == ANTIPARALLEL) { mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); - bchar = 'A'; + //bchar = 'A'; } else { mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); - bchar = 'p'; + //bchar = 'p'; } // Do not duplicate bridges @@ -475,8 +478,8 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { return; } - Resi.SetBridge( idx2, bchar ); - Resj.SetBridge( idx1, bchar ); + Resi.SetBridge( idx2, btypeIn ); + Resj.SetBridge( idx1, btypeIn ); } /* diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 031bd5384d..7e78be091f 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -32,14 +32,8 @@ class Action_DSSP2 : public Action { Action::RetType DoAction(int, ActionFrame&); void Print(); - int OverHbonds(int, ActionFrame&); - - enum BridgeType { PARALLEL=0, ANTIPARALLEL }; - void AssignBridge(int, int, BridgeType); - - class ElemHbond; + /// Class that will hold SS info for each residue class SSres; - /// Secondary structure types enum SStype { NONE=0, EXTENDED, BRIDGE, H3_10, ALPHA, HPI, TURN, BEND }; static const int NSSTYPE_ = 8; ///< # of secondary structure types. @@ -47,28 +41,25 @@ class Action_DSSP2 : public Action { static const char* SSname_[]; ///< Full secondary structure names corresponding to SStype static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype static const std::string SSzlabels_; ///< Output graph Z labels corresponding to SStype - - /// Elementary hydrogen bond pattern -/* enum PatternType { - NOHBOND = 0, ///< No hydrogen bond - TURNBEG, ///< Start of n-Turn, > - TURNEND, ///< End of n-Turn, < - TURNX, ///< Coincidence of TURNBEG and TURNEND, X - TURN3, ///< Inside i to i + 3 turn (T) - TURN4, ///< Inside i to i + 4 turn (T) - TURN5, ///< Inside i to i + 5 turn (T) - BRIDGEPARA, ///< (i-1,j) and (j,i+1) or (j-1,i) and (i,j+1), lower case - BRIDGEANTI ///< (i,j) and (j,i) or (i-1,j+1) and (j-1,i+1), upper case - };*/ - - enum ssCharType { T3 = 0, T4, T5, B1, B2, S }; - static const int NSSCHARTYPE_ = 6; + /// Turn types + enum TurnType { T3 = 0, T4, T5 }; + static const int NTURNTYPE_ = 3; + /// Beta types + enum BetaType { B1 = 0, B2, S }; + static const int NBETATYPE_ = 3; + /// Bridge direction types + enum BridgeType { NO_BRIDGE = 0, PARALLEL, ANTIPARALLEL }; static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol typedef std::vector SSarrayType; + int OverHbonds(int, ActionFrame&); + + void AssignBridge(int, int, BridgeType); + + int debug_; ///< Action debug level SSarrayType Residues_; ///< Hold SS data for all residues. NameType BB_N_; ///< Protein N atom name ('N') @@ -86,6 +77,7 @@ class Action_DSSP2 : public Action { bool printString_; ///< If true print 1 char per residue indicating ss type }; +// ============================================================================= class Action_DSSP2::SSres { public: SSres(); @@ -112,17 +104,17 @@ class Action_DSSP2::SSres { bool HasNH() const { return (N_!=-1 && H_!=-1); } bool HasCA() const { return (CA_!=-1); } - bool HasTurnStart(ssCharType) const; + bool HasTurnStart(TurnType) const; bool HasBridge() const; bool IsBridgedWith(int) const; - char StrandChar() const; +// char StrandChar() const; void PrintSSchar() const; - void SetBegin(ssCharType); - void SetTurn(ssCharType); - void SetEnd(ssCharType); - - void SetBridge(int, char); + void SetBegin(TurnType); + void SetTurn(TurnType); + void SetEnd(TurnType); + /// Set a bridge between this res and other res index into Residues_ + void SetBridge(int, BridgeType); void AccumulateData(int, bool); @@ -152,7 +144,6 @@ class Action_DSSP2::SSres { double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] double bend_; ///< Angle CA[i-2, i, i+2] int SScount_[NSSTYPE_]; ///< Hold count for each SS type - //PatternType pattern_; ///< Assigned hbond pattern for this frame SStype sstype_; ///< SS assignment for this frame int num_; ///< Residue index in Topology int C_; ///< Coord idx of BB carbon @@ -163,9 +154,11 @@ class Action_DSSP2::SSres { int prevIdx_; ///< Index in Residues_ of previous residue int nextIdx_; ///< Index in Residues_ of next residue int bridge1idx_; ///< Index in Residues_ of res this is bridged to + BridgeType b1type_; ///< Type of bridge1 int bridge2idx_; ///< Index in Residues_ of res this is bridged to + BridgeType b2type_; ///< Type of bridge2 char resChar_; ///< Single char residue ID - char ssChar_[NSSCHARTYPE_]; ///< Character if part of N turn/bridge/sheet + char turnChar_[NTURNTYPE_]; ///< Character if part of N turn bool isSelected_; ///< True if calculating SS for this residue. }; #endif From 734b00afaa118a0b81f4052ee784933d9ad2e8f4 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 11:16:41 -0400 Subject: [PATCH 37/62] DRR - Cpptraj: Rename some functions --- src/Action_DSSP2.cpp | 16 ++++++++-------- src/Action_DSSP2.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index bc5d7a82bd..92d8769702 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -128,14 +128,14 @@ void Action_DSSP2::SSres::Unassign() { } /** Set turn beginning. */ -void Action_DSSP2::SSres::SetBegin(TurnType typeIn) { +void Action_DSSP2::SSres::SetTurnBegin(TurnType typeIn) { if (turnChar_[typeIn] == '<') turnChar_[typeIn] = 'X'; else turnChar_[typeIn] = '>'; } -void Action_DSSP2::SSres::SetEnd(TurnType typeIn) { +void Action_DSSP2::SSres::SetTurnEnd(TurnType typeIn) { if (turnChar_[typeIn] == '>') turnChar_[typeIn] = 'X'; else @@ -636,30 +636,30 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Check for H bond from CO i to NH i+n if (resDelta == 3) { // 3-TURN - Residues_[riidx ].SetBegin(T3); + Residues_[riidx ].SetTurnBegin(T3); Residues_[riidx+1].SetTurn(T3); Residues_[riidx+2].SetTurn(T3); - Residues_[riidx+3].SetEnd(T3); + Residues_[riidx+3].SetTurnEnd(T3); Residues_[riidx+1].SetSS( TURN ); Residues_[riidx+2].SetSS( TURN ); } else if (resDelta == 4) { // 4-TURN - Residues_[riidx ].SetBegin(T4); + Residues_[riidx ].SetTurnBegin(T4); Residues_[riidx+1].SetTurn(T4); Residues_[riidx+2].SetTurn(T4); Residues_[riidx+3].SetTurn(T4); - Residues_[riidx+4].SetEnd(T4); + Residues_[riidx+4].SetTurnEnd(T4); Residues_[riidx+1].SetSS( TURN ); Residues_[riidx+2].SetSS( TURN ); Residues_[riidx+3].SetSS( TURN ); } else if (resDelta == 5) { // 5-TURN - Residues_[riidx ].SetBegin(T5); + Residues_[riidx ].SetTurnBegin(T5); Residues_[riidx+1].SetTurn(T5); Residues_[riidx+2].SetTurn(T5); Residues_[riidx+3].SetTurn(T5); Residues_[riidx+4].SetTurn(T5); - Residues_[riidx+5].SetEnd(T5); + Residues_[riidx+5].SetTurnEnd(T5); Residues_[riidx+1].SetSS( TURN ); Residues_[riidx+2].SetSS( TURN ); Residues_[riidx+3].SetSS( TURN ); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 7e78be091f..b987bb7d24 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -110,9 +110,9 @@ class Action_DSSP2::SSres { // char StrandChar() const; void PrintSSchar() const; - void SetBegin(TurnType); + void SetTurnBegin(TurnType); void SetTurn(TurnType); - void SetEnd(TurnType); + void SetTurnEnd(TurnType); /// Set a bridge between this res and other res index into Residues_ void SetBridge(int, BridgeType); From 0dd33e109243f81287ea971dcf8e1ed20f66f4d4 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 11:20:53 -0400 Subject: [PATCH 38/62] DRR - Cpptraj: Ensure residue is always unassigned at the beginning of frame. Ensure bridge type is unsassigned. --- src/Action_DSSP2.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 92d8769702..4fc5c698f3 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -123,7 +123,9 @@ void Action_DSSP2::SSres::Deselect() { void Action_DSSP2::SSres::Unassign() { sstype_ = NONE; bridge1idx_ = -1; + b1type_ = NO_BRIDGE; bridge2idx_ = -1; + b2type_ = NO_BRIDGE; std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); } @@ -574,10 +576,10 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) #endif /* _OPENMP */ for (resi = 0; resi < Nres; resi++) { + Residues_[resi].Unassign(); if (Residues_[resi].IsSelected()) { SSres& ResCO = Residues_[resi]; - ResCO.Unassign(); if (ResCO.HasCO()) { const double* Cxyz = frm.Frm().CRD( ResCO.C() ); From 6df34ec28e7ae86197174a4c9df1b31ae5511969 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 11:30:21 -0400 Subject: [PATCH 39/62] DRR - Cpptraj: Use ResChar() --- src/Action_DSSP2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 4fc5c698f3..f5d1843cc8 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -939,7 +939,7 @@ void Action_DSSP2::Print() { for (int resi = min_res; resi < max_res+1; resi++) { if (startRes == -1) startRes = resi; // Convert residue name. - resLine += Residue::ConvertResName( Residues_[resi].Dset()->Meta().Legend() ); + resLine += Residues_[resi].ResChar(); // Figure out which SS element is dominant for res if selected if (Residues_[resi].Dset() != 0) { int dominantType = 0; From 48366b82c4c190a4ffc2975203f528275fdd6bff Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 11:47:33 -0400 Subject: [PATCH 40/62] DRR - Cpptraj: FabI traj is actually 10 frames --- test/Test_DSSP/RunTest.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Test_DSSP/RunTest.sh b/test/Test_DSSP/RunTest.sh index 107aa3ce48..4536962531 100755 --- a/test/Test_DSSP/RunTest.sh +++ b/test/Test_DSSP/RunTest.sh @@ -68,13 +68,12 @@ fi # FtuFabI Assignment test UNITNAME='FtuFabI Assignment test' -CheckFor maxthreads 1 +CheckFor maxthreads 10 if [ $? -eq 0 ] ; then TOP='' cat > cpptraj.in < Date: Wed, 28 Aug 2019 11:50:16 -0400 Subject: [PATCH 41/62] DRR - Cpptraj: Accumulate stats on parallel and antiparallel sheet --- src/Action_DSSP2.cpp | 31 ++++++++++++++++++++++--------- src/Action_DSSP2.h | 2 ++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index f5d1843cc8..28d97b89be 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -50,17 +50,10 @@ Action_DSSP2::SSres::SSres() : isSelected_(false) { std::fill(SScount_, SScount_ + NSSTYPE_, 0); + std::fill(Bcount_, Bcount_ + NBRIDGETYPE_, 0); std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); } -void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString) { - SScount_[sstype_]++; - if (useString) - resDataSet_->Add(frameNum, SSchar_[sstype_]); - else - resDataSet_->Add(frameNum, &sstype_); -} - Action_DSSP2::SSres::SSres(SSres const& rhs) : resDataSet_(rhs.resDataSet_), chirality_(rhs.chirality_), @@ -82,6 +75,7 @@ Action_DSSP2::SSres::SSres(SSres const& rhs) : isSelected_(rhs.isSelected_) { std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); + std::copy(rhs.Bcount_, rhs.Bcount_ + NBRIDGETYPE_, Bcount_); std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); } @@ -106,6 +100,7 @@ Action_DSSP2::SSres& Action_DSSP2::SSres::operator=(SSres const& rhs) { resChar_ = rhs.resChar_; isSelected_ = rhs.isSelected_; std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); + std::copy(rhs.Bcount_, rhs.Bcount_ + NBRIDGETYPE_, Bcount_); std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); return *this; } @@ -129,6 +124,23 @@ void Action_DSSP2::SSres::Unassign() { std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); } +/** Accumulate SS data. */ +void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString) { + SScount_[sstype_]++; + if (sstype_ == EXTENDED || sstype_ == BRIDGE) { + if (b1type_ == ANTIPARALLEL || b2type_ == ANTIPARALLEL) + Bcount_[ANTIPARALLEL]++; + if (b1type_ == PARALLEL || b2type_ == PARALLEL) + Bcount_[PARALLEL]++; + // TODO bulge? + } else + Bcount_[NO_BRIDGE]++; + if (useString) + resDataSet_->Add(frameNum, SSchar_[sstype_]); + else + resDataSet_->Add(frameNum, &sstype_); +} + /** Set turn beginning. */ void Action_DSSP2::SSres::SetTurnBegin(TurnType typeIn) { if (turnChar_[typeIn] == '<') @@ -213,9 +225,10 @@ bool Action_DSSP2::SSres::IsBridgedWith(int idx2) const { void Action_DSSP2::SSres::PrintSSchar() const { static const char btypeChar[] = { ' ', 'p', 'A' }; - mprintf("\t%8i %c %c %c %c %c(%8i) %c(%8i) %c\n", num_+1, resChar_, + mprintf("\t%6i %c %c %c %c %c(%6i) %c(%6i) %6i %6i %6i %c\n", num_+1, resChar_, turnChar_[T3], turnChar_[T4], turnChar_[T5], btypeChar[b1type_], bridge1idx_+1, btypeChar[b2type_], bridge2idx_+1, + Bcount_[NO_BRIDGE], Bcount_[PARALLEL], Bcount_[ANTIPARALLEL], DSSP_char_[sstype_]); } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index b987bb7d24..71deea7b2d 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -49,6 +49,7 @@ class Action_DSSP2 : public Action { static const int NBETATYPE_ = 3; /// Bridge direction types enum BridgeType { NO_BRIDGE = 0, PARALLEL, ANTIPARALLEL }; + static const int NBRIDGETYPE_ = 3; static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol @@ -144,6 +145,7 @@ class Action_DSSP2::SSres { double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] double bend_; ///< Angle CA[i-2, i, i+2] int SScount_[NSSTYPE_]; ///< Hold count for each SS type + int Bcount_[NBRIDGETYPE_]; ///< Hold count for Beta types SStype sstype_; ///< SS assignment for this frame int num_; ///< Residue index in Topology int C_; ///< Coord idx of BB carbon From c865f2e91c1d0b49e3ece96437d2815446c199bc Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 13:18:03 -0400 Subject: [PATCH 42/62] DRR - Cpptraj: When looking for beta bulge, make sure bridge types match --- src/Action_DSSP2.cpp | 29 ++++++++++++++++++++++------- src/Action_DSSP2.h | 2 ++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 28d97b89be..e421fe98b5 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -18,12 +18,16 @@ */ const double Action_DSSP2::DSSP_fac_ = 27.888; +/** DSSP default cutoff for determining hbonds, from 1983 Kabsch & Sander paper. */ const double Action_DSSP2::DSSP_cut_ = -0.5; +/** DSSP 1 character SS assignment. */ const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; +/** Used for output to STRING data set. */ const char* Action_DSSP2::SSchar_[] = { "0", "E", "B", "G", "H", "I", "T", "S" }; +/** Full SS names. */ const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Ext,Bridge,3-10,Alpha,Pi,Turn,Bend"; @@ -763,23 +767,34 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) int presb2 = Residues_[prevRes].Bridge2Idx(); int nresb1 = Residues_[nextRes].Bridge1Idx(); int nresb2 = Residues_[nextRes].Bridge2Idx(); + int prest1 = Residues_[prevRes].Bridge1Type(); + int prest2 = Residues_[prevRes].Bridge2Type(); + int nrest1 = Residues_[nextRes].Bridge1Type(); + int nrest2 = Residues_[nextRes].Bridge2Type(); mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); // The largest allowed gap in the other strand is 5 residues. // Since we know that next res and previous res both have at // least 1 bridge, Next B1 - Prev B1 can be the gap to beat. - int resGapSize = AbsResDelta(nresb1, presb1); - int sres0 = std::min(presb1, nresb1); - int sres1 = std::max(presb1, nresb1); - if (presb2 != -1) + // Need to also make sure the bridge types match. + int resGapSize = -1; + int sres0 = -1; + int sres1 = -1; + if (nrest1 == prest1) { + resGapSize = AbsResDelta(nresb1, presb1); + sres0 = std::min(presb1, nresb1); + sres1 = std::max(presb1, nresb1); + } + if (presb2 != -1 && nrest1 == prest2) SetMin( resGapSize, sres0, sres1, nresb1, presb2 ); if (nresb2 != -1) { - SetMin( resGapSize, sres0, sres1, nresb2, presb1 ); - if (presb2 != -1) + if (nrest2 == prest1) + SetMin( resGapSize, sres0, sres1, nresb2, presb1 ); + if (presb2 != -1 && nrest2 == prest2) SetMin( resGapSize, sres0, sres1, nresb2, presb2 ); } mprintf("Min res gap size on other strand = %i (%i to %i)\n", resGapSize, sres0, sres1); // Minimum allowed gap is 4 residues in between, so 5 residues total. - if (resGapSize < 6) { + if (resGapSize > -1 && resGapSize < 6) { mprintf("Beta bulge.\n"); if (Residues_[prevRes].SS() != ALPHA) Residues_[prevRes].SetSS( EXTENDED ); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 71deea7b2d..f8379c4698 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -97,7 +97,9 @@ class Action_DSSP2::SSres { int PrevIdx() const { return prevIdx_; } int NextIdx() const { return nextIdx_; } int Bridge1Idx() const { return bridge1idx_; } + BridgeType Bridge1Type() const { return b1type_;} int Bridge2Idx() const { return bridge2idx_; } + BridgeType Bridge2Type() const { return b2type_;} DataSet* Dset() const { return resDataSet_; } bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } From 89ad7e7e0163ddb13c567c0786a919e24f3ffc95 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 13:32:48 -0400 Subject: [PATCH 43/62] DRR - Cpptraj: Start adding 'betadetail' keyword which will track parallel/anti-parallel beta in place of extended/bridge (backwards compat.) --- src/Action_DSSP2.cpp | 26 +++++++++++++++++++------- src/Action_DSSP2.h | 3 ++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index e421fe98b5..a4dd46aeef 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -129,20 +129,26 @@ void Action_DSSP2::SSres::Unassign() { } /** Accumulate SS data. */ -void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString) { +void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString, bool betaDetail) +{ SScount_[sstype_]++; + int idata = (int)sstype_; if (sstype_ == EXTENDED || sstype_ == BRIDGE) { - if (b1type_ == ANTIPARALLEL || b2type_ == ANTIPARALLEL) - Bcount_[ANTIPARALLEL]++; - if (b1type_ == PARALLEL || b2type_ == PARALLEL) + if (b1type_ == PARALLEL || b2type_ == PARALLEL) { Bcount_[PARALLEL]++; + if (betaDetail) idata = (int)EXTENDED; + } + if (b1type_ == ANTIPARALLEL || b2type_ == ANTIPARALLEL) { + Bcount_[ANTIPARALLEL]++; + if (betaDetail) idata = (int)BRIDGE; + } // TODO bulge? } else Bcount_[NO_BRIDGE]++; if (useString) resDataSet_->Add(frameNum, SSchar_[sstype_]); else - resDataSet_->Add(frameNum, &sstype_); + resDataSet_->Add(frameNum, &idata); } /** Set turn beginning. */ @@ -247,13 +253,16 @@ Action_DSSP2::Action_DSSP2() : BB_CA_("CA"), outfile_(0), dsspFile_(0), - assignout_(0) + assignout_(0), + printString_(false), + betaDetail_(false) {} // Action_DSSP2::Help() void Action_DSSP2::Help() const { mprintf("\t[] [out ] [] [sumout ]\n" "\t[assignout ] [totalout ] [ptrajformat]\n" + "\t[betadetail]\n" "\t[namen ] [nameh ] [nameca ]\n" "\t[namec ] [nameo ]\n" " Calculate secondary structure content for residues in .\n" @@ -274,6 +283,7 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de DataFile* totalout = init.DFL().AddDataFile( actionArgs.GetStringKey("totalout"), actionArgs ); assignout_ = init.DFL().AddCpptrajFile(actionArgs.GetStringKey("assignout"), "SS assignment"); printString_ = actionArgs.hasKey("ptrajformat"); + betaDetail_ = actionArgs.hasKey("betadetail"); temp = actionArgs.GetStringKey("namen"); if (!temp.empty()) BB_N_ = temp; temp = actionArgs.GetStringKey("nameh"); @@ -311,6 +321,8 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de mprintf("\tDumping results to %s\n", outfile_->DataFilename().full()); if (dsspFile_ != 0) mprintf("\tSum results to %s\n", dsspFile_->DataFilename().full()); + if (betaDetail_) + mprintf("\tWill print parallel/anti-parallel beta in place of extended/bridge\n"); if (printString_) { mprintf("\tSS data for each residue will be stored as a string.\n"); for (int i = 0; i < NSSTYPE_; i++) @@ -883,7 +895,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) int Nselected = 0; for (resi=0; resi < Nres; resi++) { if (Residues_[resi].IsSelected()) { - Residues_[resi].AccumulateData(frameNum, printString_); + Residues_[resi].AccumulateData(frameNum, printString_, betaDetail_); Nselected++; } } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index f8379c4698..4d2b4a3209 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -76,6 +76,7 @@ class Action_DSSP2 : public Action { DataSet* totalDS_[NSSTYPE_]; ///< Hold total SS each frame for each SS type ActionInit Init_; ///< Hold pointers to master DSL/DFL bool printString_; ///< If true print 1 char per residue indicating ss type + bool betaDetail_; ///< If true use para/anti in place of extended/bridge }; // ============================================================================= @@ -119,7 +120,7 @@ class Action_DSSP2::SSres { /// Set a bridge between this res and other res index into Residues_ void SetBridge(int, BridgeType); - void AccumulateData(int, bool); + void AccumulateData(int, bool, bool); void SetSS(SStype); void SetNum(int i) { num_ = i; } From 71da45319e1de2729cdea3dd3d03f12a15c73f3f Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 13:36:07 -0400 Subject: [PATCH 44/62] DRR - Cpptraj: Fix zlabels for betaDetail --- src/Action_DSSP2.cpp | 9 ++++++--- src/Action_DSSP2.h | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index a4dd46aeef..5dc8e22e61 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -30,7 +30,6 @@ const char* Action_DSSP2::SSchar_[] = { "0", "E", "B", "G", "H", "I", "T", "S /** Full SS names. */ const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; -const std::string Action_DSSP2::SSzlabels_ = "zlabels None,Ext,Bridge,3-10,Alpha,Pi,Turn,Bend"; // ----- SSres ----------------------------------------------------------------- Action_DSSP2::SSres::SSres() : @@ -303,8 +302,12 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de if (dsetname_.empty()) dsetname_ = init.DSL().GenerateDefaultName("DSSP"); // Set up Z labels - if (outfile_ != 0) - outfile_->ProcessArgs(SSzlabels_); + if (outfile_ != 0) { + if (betaDetail_) + outfile_->ProcessArgs("zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"); + else + outfile_->ProcessArgs("zlabels None,Ext,Bridge,3-10,Alpha,Pi,Turn,Bend"); + } // Create data sets for total fraction SS vs time. for (int i = 0; i < NSSTYPE_; i++) { totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, SSname_[i])); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 4d2b4a3209..8ab0b7087f 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -40,7 +40,6 @@ class Action_DSSP2 : public Action { static const char DSSP_char_[]; ///< DSSP 1 char names corresponding to SStype static const char* SSname_[]; ///< Full secondary structure names corresponding to SStype static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype - static const std::string SSzlabels_; ///< Output graph Z labels corresponding to SStype /// Turn types enum TurnType { T3 = 0, T4, T5 }; static const int NTURNTYPE_ = 3; From 284086cd194219206c5bad1e18d4fe23483a02e2 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 13:44:28 -0400 Subject: [PATCH 45/62] DRR - Cpptraj: Fix sum output for betaDetail --- src/Action_DSSP2.cpp | 19 +++++++++++++++++-- src/Action_DSSP2.h | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 5dc8e22e61..b6de83e3b1 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -952,7 +952,12 @@ void Action_DSSP2::Print() { // Set up a dataset for each SS type. TODO: NONE type? for (int ss = 1; ss < NSSTYPE_; ss++) { md.SetIdx(ss); - md.SetLegend( SSname_[ss] ); + const char* legend = SSname_[ss]; + if (betaDetail_ && (SStype)ss == EXTENDED) + legend = "Para"; + else if (betaDetail_ && (SStype)ss == BRIDGE) + legend = "Anti"; + md.SetLegend( legend ); dsspData_[ss] = Init_.DSL().AddSet(DataSet::DOUBLE, md); dsspData_[ss]->SetDim(Dimension::X, Xdim); dsspFile_->AddDataSet( dsspData_[ss] ); @@ -966,7 +971,17 @@ void Action_DSSP2::Print() { for (int ss = 0; ss < NSSTYPE_; ss++) Nframe += Residues_[resi].SScount((SStype)ss); for (int ss = 1; ss < NSSTYPE_; ss++) { - double avg = (double)Residues_[resi].SScount((SStype)ss); + double avg; + if (betaDetail_) { + if ((SStype)ss == EXTENDED) + avg = (double)Residues_[resi].Bcount(PARALLEL); + else if ((SStype)ss == BRIDGE) + avg = (double)Residues_[resi].Bcount(ANTIPARALLEL); + else + avg = (double)Residues_[resi].SScount((SStype)ss); + } else { + avg = (double)Residues_[resi].SScount((SStype)ss); + } avg /= (double)Nframe; dsspData_[ss]->Add(idx, &avg); } diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 8ab0b7087f..ae21605550 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -85,6 +85,7 @@ class Action_DSSP2::SSres { SSres(SSres const&); SSres& operator=(SSres const&); int SScount(SStype t) const { return SScount_[t]; } + int Bcount(BridgeType t) const { return Bcount_[t]; } SStype SS() const { return sstype_; } int Num() const { return num_; } char ResChar() const { return resChar_; } From 601baa03bca3d121728a7f681c689bab06a77bdd Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 14:58:14 -0400 Subject: [PATCH 46/62] DRR - Cpptraj: Fix ptrajformat for betadetail --- src/Action_DSSP2.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index b6de83e3b1..fb7693dc6d 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -132,20 +132,27 @@ void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString, bool beta { SScount_[sstype_]++; int idata = (int)sstype_; + const char* sdata = SSchar_[sstype_]; if (sstype_ == EXTENDED || sstype_ == BRIDGE) { if (b1type_ == PARALLEL || b2type_ == PARALLEL) { Bcount_[PARALLEL]++; - if (betaDetail) idata = (int)EXTENDED; + if (betaDetail) { + idata = (int)EXTENDED; + sdata = "b"; + } } if (b1type_ == ANTIPARALLEL || b2type_ == ANTIPARALLEL) { Bcount_[ANTIPARALLEL]++; - if (betaDetail) idata = (int)BRIDGE; + if (betaDetail) { + idata = (int)BRIDGE; + sdata = "B"; + } } // TODO bulge? } else Bcount_[NO_BRIDGE]++; if (useString) - resDataSet_->Add(frameNum, SSchar_[sstype_]); + resDataSet_->Add(frameNum, sdata); else resDataSet_->Add(frameNum, &idata); } From a0b4f0532a025318a7b2b1c11a1a1aae324f08a7 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 15:14:50 -0400 Subject: [PATCH 47/62] DRR - Cpptraj: Change full name labels based on betaDetail --- src/Action_DSSP2.cpp | 34 +++++++++++++++++++++++----------- src/Action_DSSP2.h | 10 ++++++---- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index fb7693dc6d..d2c784737e 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -28,7 +28,7 @@ const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S const char* Action_DSSP2::SSchar_[] = { "0", "E", "B", "G", "H", "I", "T", "S" }; /** Full SS names. */ -const char* Action_DSSP2::SSname_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; +const char* Action_DSSP2::DSSP_name_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; // ----- SSres ----------------------------------------------------------------- @@ -317,7 +317,13 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de } // Create data sets for total fraction SS vs time. for (int i = 0; i < NSSTYPE_; i++) { - totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, SSname_[i])); + const char* aspect = DSSP_name_[i]; + if (betaDetail_ && (SStype)i == EXTENDED) + aspect = "Para"; + else if (betaDetail_ && (SStype)i == BRIDGE) + aspect = "Anti"; + SSname_.push_back( std::string(aspect) ); + totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, aspect)); if (totalDS_[i] == 0) { mprinterr("Error: Could not create DSSP total frac v time data set.\n"); return Action::ERR; @@ -336,11 +342,11 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de if (printString_) { mprintf("\tSS data for each residue will be stored as a string.\n"); for (int i = 0; i < NSSTYPE_; i++) - mprintf("\t\t%s = %s\n", SSchar_[i], SSname_[i]); + mprintf("\t\t%s = %s\n", SSchar_[i], SSname_[i].c_str()); } else { mprintf("\tSS data for each residue will be stored as integers.\n"); for (int i = 0; i < NSSTYPE_; i++) - mprintf("\t\t%i = %s\n", i, SSname_[i]); + mprintf("\t\t%i = %s\n", i, SSname_[i].c_str()); } if (assignout_ != 0) mprintf("\tOverall assigned SS will be written to %s\n", assignout_->Filename().full()); @@ -904,7 +910,18 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) std::fill( totalSS, totalSS + NSSTYPE_, 0 ); int Nselected = 0; for (resi=0; resi < Nres; resi++) { - if (Residues_[resi].IsSelected()) { + SSres& Resi = Residues_[resi]; + if (Resi.IsSelected()) { + if (betaDetail_ && (Resi.SS() == EXTENDED || Resi.SS() == BRIDGE)) + { + if (Resi.Bridge1Type() == ANTIPARALLEL || + Resi.Bridge2Type() == ANTIPARALLEL) + totalSS[BRIDGE]++; + else if (Resi.Bridge1Type() == PARALLEL || + Resi.Bridge2Type() == PARALLEL) + totalSS[EXTENDED]++; + } else + totalSS[Residues_[resi].SS()]++; Residues_[resi].AccumulateData(frameNum, printString_, betaDetail_); Nselected++; } @@ -959,12 +976,7 @@ void Action_DSSP2::Print() { // Set up a dataset for each SS type. TODO: NONE type? for (int ss = 1; ss < NSSTYPE_; ss++) { md.SetIdx(ss); - const char* legend = SSname_[ss]; - if (betaDetail_ && (SStype)ss == EXTENDED) - legend = "Para"; - else if (betaDetail_ && (SStype)ss == BRIDGE) - legend = "Anti"; - md.SetLegend( legend ); + md.SetLegend( SSname_[ss] ); dsspData_[ss] = Init_.DSL().AddSet(DataSet::DOUBLE, md); dsspData_[ss]->SetDim(Dimension::X, Xdim); dsspFile_->AddDataSet( dsspData_[ss] ); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index ae21605550..89965edd15 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -36,10 +36,10 @@ class Action_DSSP2 : public Action { class SSres; /// Secondary structure types enum SStype { NONE=0, EXTENDED, BRIDGE, H3_10, ALPHA, HPI, TURN, BEND }; - static const int NSSTYPE_ = 8; ///< # of secondary structure types. - static const char DSSP_char_[]; ///< DSSP 1 char names corresponding to SStype - static const char* SSname_[]; ///< Full secondary structure names corresponding to SStype - static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype + static const int NSSTYPE_ = 8; ///< # of secondary structure types. + static const char DSSP_char_[]; ///< DSSP 1 char names corresponding to SStype + static const char* DSSP_name_[]; ///< Full secondary structure names corresponding to SStype + static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype /// Turn types enum TurnType { T3 = 0, T4, T5 }; static const int NTURNTYPE_ = 3; @@ -54,6 +54,7 @@ class Action_DSSP2 : public Action { static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol typedef std::vector SSarrayType; + typedef std::vector Sarray; int OverHbonds(int, ActionFrame&); @@ -62,6 +63,7 @@ class Action_DSSP2 : public Action { int debug_; ///< Action debug level SSarrayType Residues_; ///< Hold SS data for all residues. + Sarray SSname_; ///< Hold full secondary structure names NameType BB_N_; ///< Protein N atom name ('N') NameType BB_H_; ///< Protein N-H atom name ('H') NameType BB_C_; ///< Protein C atom name ('C') From 016bb5f56db6b93912e45e7381029a863714d227 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 15:20:55 -0400 Subject: [PATCH 48/62] DRR - Cpptraj: Fix assignout for betaDetail --- src/Action_DSSP2.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index d2c784737e..efe46f9815 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -1016,14 +1016,22 @@ void Action_DSSP2::Print() { for (int resi = min_res; resi < max_res+1; resi++) { if (startRes == -1) startRes = resi; // Convert residue name. - resLine += Residues_[resi].ResChar(); + SSres& Resi = Residues_[resi]; + resLine += Resi.ResChar(); // Figure out which SS element is dominant for res if selected - if (Residues_[resi].Dset() != 0) { + if (Resi.Dset() != 0) { int dominantType = 0; int ssmax = 0; for (int ss = 0; ss < NSSTYPE_; ss++) { - if ( Residues_[resi].SScount((SStype)ss) > ssmax ) { - ssmax = Residues_[resi].SScount((SStype)ss); + int sscount; + if (betaDetail_ && (SStype)ss == EXTENDED) + sscount = Resi.Bcount(PARALLEL); + else if (betaDetail_ && (SStype)ss == BRIDGE) + sscount = Resi.Bcount(ANTIPARALLEL); + else + sscount = Resi.SScount((SStype)ss); + if ( sscount > ssmax ) { + ssmax = sscount; dominantType = ss; } } From 38eea4747b1338e8aa12683f36b91c4bd163e69e Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Wed, 28 Aug 2019 15:38:58 -0400 Subject: [PATCH 49/62] DRR - Cpptraj: Do normalization over total number of frames for backwards compat. --- src/Action_DSSP2.cpp | 35 ++++++++++++++++++----------------- src/Action_DSSP2.h | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index efe46f9815..0e2c8c4c5e 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -279,7 +279,7 @@ void Action_DSSP2::Help() const { Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int debugIn) { debug_ = debugIn; - //Nframe_ = 0; + Nframes_ = 0; // Get keywords outfile_ = init.DFL().AddDataFile( actionArgs.GetStringKey("out"), actionArgs ); std::string temp = actionArgs.GetStringKey("sumout"); @@ -945,6 +945,7 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) { OverHbonds(frameNum, frm); + Nframes_++; // DEBUG - Print basic assignment for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) it->PrintSSchar(); @@ -983,25 +984,25 @@ void Action_DSSP2::Print() { } // Calc the avg SS type for each residue that has data. - int idx = 0; + int idx = 0; + unsigned int norm = Nframes_; for (int resi = min_res; resi < max_res+1; resi++) { - if (Residues_[resi].Dset() != 0) { - int Nframe = 0; - for (int ss = 0; ss < NSSTYPE_; ss++) - Nframe += Residues_[resi].SScount((SStype)ss); + SSres& Resi = Residues_[resi]; + if (Resi.Dset() != 0) { + //int Nframe = 0; + //for (int ss = 0; ss < NSSTYPE_; ss++) + // Nframe += Resi.SScount((SStype)ss); + //mprintf("DEBUG: Total frames for residue %i is %i\n", Resi.Num()+1, Nframe); for (int ss = 1; ss < NSSTYPE_; ss++) { double avg; - if (betaDetail_) { - if ((SStype)ss == EXTENDED) - avg = (double)Residues_[resi].Bcount(PARALLEL); - else if ((SStype)ss == BRIDGE) - avg = (double)Residues_[resi].Bcount(ANTIPARALLEL); - else - avg = (double)Residues_[resi].SScount((SStype)ss); - } else { - avg = (double)Residues_[resi].SScount((SStype)ss); - } - avg /= (double)Nframe; + if (betaDetail_ && (SStype)ss == EXTENDED) + avg = (double)Resi.Bcount(PARALLEL); + else if (betaDetail_ && (SStype)ss == BRIDGE) + avg = (double)Resi.Bcount(ANTIPARALLEL); + else + avg = (double)Resi.SScount((SStype)ss); + //mprintf("DEBUG:\t\tCount for type %i is %f\n", ss, avg); + avg /= (double)norm; dsspData_[ss]->Add(idx, &avg); } ++idx; diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index 89965edd15..fd158565e1 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -62,6 +62,7 @@ class Action_DSSP2 : public Action { int debug_; ///< Action debug level + unsigned int Nframes_; ///< Number of frames processed, for total SS normalization SSarrayType Residues_; ///< Hold SS data for all residues. Sarray SSname_; ///< Hold full secondary structure names NameType BB_N_; ///< Protein N atom name ('N') From b0f34e2e79b73f89cba3495ad7f88d0017fbeac2 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 13:48:31 -0400 Subject: [PATCH 50/62] DRR - Cpptraj: Hide some debug info --- src/Action_DSSP2.cpp | 69 +++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 0e2c8c4c5e..8c98429011 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -436,7 +436,9 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) } } } +# ifdef DSSPDEBUG mprintf("\t %8i < %8i < %8i\n", prevresnum+1, *ridx+1, nextresnum+1); +# endif // Here we assume that residues are sequential! if (prevresnum > -1) Res->SetPrevIdx( Res-Residues_.begin()-1 ); if (nextresnum > -1) Res->SetNextIdx( Res-Residues_.begin()+1 ); @@ -481,18 +483,19 @@ Action::RetType Action_DSSP2::Setup(ActionSetup& setup) mprintf("\t%u of %zu solute residues selected.\n", nResSelected, soluteRes.Size()); // DEBUG - print each residue set up. - for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) - { - mprintf(" %8i", it->Num() + 1); - PrintAtom(setup.Top(), BB_C_, it->C()); - PrintAtom(setup.Top(), BB_O_, it->O()); - PrintAtom(setup.Top(), BB_N_, it->N()); - PrintAtom(setup.Top(), BB_H_, it->H()); - PrintAtom(setup.Top(), BB_CA_, it->CA()); - mprintf(" Prev=%8i Next=%8i", it->PrevIdx(), it->NextIdx()); - mprintf("\n"); + if (debug_ > 0) { + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + { + mprintf(" %8i", it->Num() + 1); + PrintAtom(setup.Top(), BB_C_, it->C()); + PrintAtom(setup.Top(), BB_O_, it->O()); + PrintAtom(setup.Top(), BB_N_, it->N()); + PrintAtom(setup.Top(), BB_H_, it->H()); + PrintAtom(setup.Top(), BB_CA_, it->CA()); + mprintf(" Prev=%8i Next=%8i", it->PrevIdx(), it->NextIdx()); + mprintf("\n"); + } } - return Action::OK; } @@ -510,18 +513,18 @@ void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { SSres& Resi = Residues_[idx1]; SSres& Resj = Residues_[idx2]; - +# ifdef DSSPDEBUG if (btypeIn == ANTIPARALLEL) { mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); - //bchar = 'A'; } else { mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); - //bchar = 'p'; } - +# endif // Do not duplicate bridges if (Resi.IsBridgedWith(idx2)) { +# ifdef DSSPDEBUG mprintf("\t\tAlready present.\n"); +# endif return; } @@ -675,11 +678,15 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) int rjidx = hb0->second; SSres const& Resi = Residues_[riidx]; SSres const& Resj = Residues_[rjidx]; +# ifdef DSSPDEBUG mprintf("Res %8i %c -- %8i %c", Resi.Num()+1, Resi.ResChar(), Resj.Num()+1, Resj.ResChar()); // DBG +# endif // Spacing between residues i and j int resDelta = Resj.Num() - Resi.Num(); +# ifdef DSSPDEBUG mprintf("(%4i)\n", resDelta); +# endif // Check for H bond from CO i to NH i+n if (resDelta == 3) { // 3-TURN @@ -721,7 +728,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Assume (i,j). Look for (j,i) hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); +# endif AssignBridge(hb0->first, hb0->second, ANTIPARALLEL); } } @@ -730,7 +739,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Assume (i-1, j+1). Look for (j-1, i+1) hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); +# endif AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL); } } @@ -739,7 +750,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Assume (i-1, j). Check for (j, i+1) PARALLEL hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); +# endif AssignBridge(hb0->first+1, hb0->second, PARALLEL); } } @@ -748,7 +761,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // Assume (j, i+1). Check for (i-1, j) hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); +# endif AssignBridge(hb0->second-1, hb0->first, PARALLEL); } } @@ -759,7 +774,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // 8 7 6 5 4 3 2 1 for (resi = 0; resi < Nres; resi++) { +# ifdef DSSPDEBUG mprintf("Residue %i\n", resi+1); +# endif SSres& Resi = Residues_[resi]; int prevRes = resi - 1; int nextRes = resi + 1; @@ -767,7 +784,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) if ( Resi.HasTurnStart(T4) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T4) ) { // Alpha helix. +# ifdef DSSPDEBUG mprintf("ALPHA helix starting at %i\n", resi+1); +# endif Residues_[resi ].SetSS( ALPHA ); Residues_[resi+1].SetSS( ALPHA ); Residues_[resi+2].SetSS( ALPHA ); @@ -783,10 +802,14 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) // was assigned via a Beta bulge. if ( prevHasBridge || nextHasBridge || Residues_[prevRes].SS() == EXTENDED ) { +# ifdef DSSPDEBUG mprintf("Extended BETA bridge at %i\n", resi+1); +# endif Resi.SetSS( EXTENDED ); } else { +# ifdef DSSPDEBUG mprintf("Isolated BETA bridge at %i.\n", resi+1); +# endif Resi.SetSS( BRIDGE ); } } else if (prevHasBridge && nextHasBridge) { @@ -799,7 +822,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) int prest2 = Residues_[prevRes].Bridge2Type(); int nrest1 = Residues_[nextRes].Bridge1Type(); int nrest2 = Residues_[nextRes].Bridge2Type(); +# ifdef DSSPDEBUG mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); +# endif // The largest allowed gap in the other strand is 5 residues. // Since we know that next res and previous res both have at // least 1 bridge, Next B1 - Prev B1 can be the gap to beat. @@ -820,10 +845,14 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) if (presb2 != -1 && nrest2 == prest2) SetMin( resGapSize, sres0, sres1, nresb2, presb2 ); } +# ifdef DSSPDEBUG mprintf("Min res gap size on other strand = %i (%i to %i)\n", resGapSize, sres0, sres1); +# endif // Minimum allowed gap is 4 residues in between, so 5 residues total. if (resGapSize > -1 && resGapSize < 6) { +# ifdef DSSPDEBUG mprintf("Beta bulge.\n"); +# endif if (Residues_[prevRes].SS() != ALPHA) Residues_[prevRes].SetSS( EXTENDED ); Resi.SetSS( EXTENDED ); @@ -849,7 +878,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi-1].HasTurnStart(T3)) { // 3-10 helix +# ifdef DSSPDEBUG mprintf("3-10 helix starting at %i\n", resi+1); +# endif Residues_[resi ].SetSS( H3_10 ); Residues_[resi+1].SetSS( H3_10 ); Residues_[resi+2].SetSS( H3_10 ); @@ -866,7 +897,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) Residues_[resi-1].HasTurnStart(T5)) { // PI helix +# ifdef DSSPDEBUG mprintf("PI helix starting at %i\n", resi+1); +# endif Residues_[resi ].SetSS( HPI ); Residues_[resi+1].SetSS( HPI ); Residues_[resi+2].SetSS( HPI ); @@ -947,8 +980,10 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) OverHbonds(frameNum, frm); Nframes_++; // DEBUG - Print basic assignment - for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) - it->PrintSSchar(); + if (debug_ > 1) { + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + it->PrintSSchar(); + } return Action::OK; } From d38190b0b0b167e8b03f3e2b0d8e1fd6914eae03 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 14:04:29 -0400 Subject: [PATCH 51/62] DRR - Cpptraj: Report timing at Print() --- src/Action_DSSP.cpp | 31 ++++++++++--------------------- src/Action_DSSP.h | 4 ++++ src/Action_DSSP2.cpp | 24 ++++++++++-------------- src/Action_DSSP2.h | 4 ++++ 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/Action_DSSP.cpp b/src/Action_DSSP.cpp index f7ed65c2dc..d35b6de2a5 100644 --- a/src/Action_DSSP.cpp +++ b/src/Action_DSSP.cpp @@ -2,9 +2,6 @@ #include "Action_DSSP.h" #include "CpptrajStdio.h" #include "DistRoutines.h" -#ifdef DSSPDEBUG -#include "Timer.h" -#endif /// Hbond energy calc prefactor for kcal/mol: q1*q2*E, 0.42*0.20*332 const double Action_DSSP::DSSP_fac = 27.888; @@ -323,15 +320,11 @@ void Action_DSSP::SSassign(int res1, int res2, SStype typeIn, bool force) { // Action_DSSP::DoAction() /** Determine secondary structure by hydrogen bonding pattern. */ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { + t_total_.Start(); + t_calchb_.Start(); int resi, resj; const double *C, *O, *H, *N; double rON, rCH, rOH, rCN, E; -# ifdef DSSPDEBUG - Timer t_total; - t_total.Start(); - Timer t_calchb; - t_calchb.Start(); -# endif // Determine C=O to H-N hydrogen bonds for each residue to each other residue #ifdef _OPENMP #pragma omp parallel private(resi,resj,C,O,H,N,rON, rCH, rOH, rCN, E) @@ -372,11 +365,8 @@ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { #ifdef _OPENMP } // END pragma omp parallel #endif -# ifdef DSSPDEBUG - t_calchb.Stop(); - Timer t_assign; - t_assign.Start(); -# endif + t_calchb_.Stop(); + t_assign_.Start(); // Determine Secondary Structure based on Hbonding pattern. // In case of structural overlap, priority is given to the structure first @@ -521,14 +511,9 @@ Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { totalDS_[i]->Add(frameNum, &fvar); } ++Nframe_; -# ifdef DSSPDEBUG - t_assign.Stop(); - t_total.Stop(); + t_assign_.Stop(); + t_total_.Stop(); - t_calchb.WriteTiming(1, "Calc Hbonds", t_total.Total()); - t_assign.WriteTiming(1, "Assignment ", t_total.Total()); - t_total.WriteTiming(0, "Total"); -# endif return Action::OK; } @@ -558,6 +543,10 @@ int Action_DSSP::SyncAction() { // Action_DSSP::Print() void Action_DSSP::Print() { if (dsetname_.empty()) return; + t_total_.WriteTiming(1, "DSSP Total"); + t_calchb_.WriteTiming(2, "Calc Hbonds", t_total_.Total()); + t_assign_.WriteTiming(2, "Assignment ", t_total_.Total()); + // Try not to print empty residues. Find the minimum and maximum residue // for which there is data. Output res nums start from 1. int min_res = -1; diff --git a/src/Action_DSSP.h b/src/Action_DSSP.h index ba68a46a18..bdb1d09401 100644 --- a/src/Action_DSSP.h +++ b/src/Action_DSSP.h @@ -1,6 +1,7 @@ #ifndef INC_ACTION_DSSP_H #define INC_ACTION_DSSP_H #include "Action.h" +#include "Timer.h" /// Calculate protein secondary structure using DSSP algorithm. class Action_DSSP : public Action { public: @@ -57,6 +58,9 @@ class Action_DSSP : public Action { NameType BB_C_; NameType BB_O_; NameType BB_CA_; + Timer t_total_; + Timer t_calchb_; + Timer t_assign_; // Private fns inline int isBonded(int, int); inline void SSassign(int, int, SStype, bool); diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 8c98429011..a458d79872 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -5,7 +5,6 @@ #include "CpptrajStdio.h" #include "DataSet.h" #include "DistRoutines.h" -#include "Timer.h" #ifdef DSSPDEBUG #include "Constants.h" #endif @@ -605,16 +604,14 @@ static inline void SetMin(int& resGapSize, int& sres0, int& sres1, int Nres, int int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) { - Timer t_overhbonds; - t_overhbonds.Start(); + t_total_.Start(); + t_calchb_.Start(); // ----- Determine hydrogen bonding ------------ typedef std::pair HbondPairType; typedef std::set HbondMapType; /// Map resi (CO) to resj (NH) potential bridge hbonds HbondMapType CO_NH_bonds; - Timer t_calchb; - t_calchb.Start(); int resi; int Nres = (int)Residues_.size(); #ifdef _OPENMP @@ -667,10 +664,9 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) #ifdef _OPENMP } // END pragma omp parallel #endif - t_calchb.Stop(); + t_calchb_.Stop(); - Timer t_assign; - t_assign.Start(); + t_assign_.Start(); // ----- Do basic assignment ------------------- for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) { @@ -966,12 +962,8 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } - t_assign.Stop(); - - t_overhbonds.Stop(); - t_calchb.WriteTiming(1, "Calc Hbonds", t_overhbonds.Total()); - t_assign.WriteTiming(1, "Assignment ", t_overhbonds.Total()); - t_overhbonds.WriteTiming(0,"Over Hbonds"); + t_assign_.Stop(); + t_total_.Stop(); return 0; } @@ -990,6 +982,10 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) // Action_DSSP::Print() void Action_DSSP2::Print() { if (dsetname_.empty()) return; + t_total_.WriteTiming(1,"DSSP total"); + t_calchb_.WriteTiming(2, "Calc Hbonds", t_total_.Total()); + t_assign_.WriteTiming(2, "Assignment ", t_total_.Total()); + // Try not to print empty residues. Find the minimum and maximum residue // for which there is data. Output res nums start from 1. int min_res = -1; diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index fd158565e1..c8388327e8 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -5,6 +5,7 @@ #include "Action.h" #include "NameType.h" #include "CharMask.h" +#include "Timer.h" class DataSet; /// /** Based on protein secondary structure definitions given in: @@ -79,6 +80,9 @@ class Action_DSSP2 : public Action { ActionInit Init_; ///< Hold pointers to master DSL/DFL bool printString_; ///< If true print 1 char per residue indicating ss type bool betaDetail_; ///< If true use para/anti in place of extended/bridge + Timer t_total_; + Timer t_calchb_; + Timer t_assign_; }; // ============================================================================= From 8906df91ddf026137e05fae955dee453a70b64fd Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 14:48:19 -0400 Subject: [PATCH 52/62] DRR - Cpptraj: Fix for openmp --- src/Action_DSSP2.cpp | 40 +++++++++++++++++++++++++++++++++------- src/Action_DSSP2.h | 10 +++++++++- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index a458d79872..0931e1dbc5 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -8,6 +8,9 @@ #ifdef DSSPDEBUG #include "Constants.h" #endif +#ifdef _OPENMP +# include +#endif /** From the original Kabsch & Sander 1983 paper. Obtained via * q1 = 0.42e @@ -330,8 +333,22 @@ Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int de // For now dont add 'None' so colors match up. if (totalout != 0 && i > 0) totalout->AddDataSet( totalDS_[i] ); } +# ifdef _OPENMP + // Each thread needs temp. space to store found hbonds every frame + // to avoid memory clashes when adding/updating in map. +# pragma omp parallel + { +# pragma omp master + { + CO_NH_bondsArray_.resize( omp_get_num_threads() ); + } + } +# endif mprintf( " SECSTRUCT: Calculating secondary structure using mask [%s]\n",Mask_.MaskString()); +# ifdef _OPENMP + mprintf("\tParallelizing calculation with %zu threads.\n", CO_NH_bondsArray_.size()); +# endif if (outfile_ != 0) mprintf("\tDumping results to %s\n", outfile_->DataFilename().full()); if (dsspFile_ != 0) @@ -607,17 +624,17 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) t_total_.Start(); t_calchb_.Start(); // ----- Determine hydrogen bonding ------------ - typedef std::pair HbondPairType; - typedef std::set HbondMapType; - /// Map resi (CO) to resj (NH) potential bridge hbonds - HbondMapType CO_NH_bonds; - int resi; int Nres = (int)Residues_.size(); #ifdef _OPENMP -#pragma omp parallel private(resi) + int mythread; +#pragma omp parallel private(resi, mythread) { + mythread = omp_get_thread_num(); + CO_NH_bondsArray_[mythread].clear(); #pragma omp for +#else /* _OPENMP */ + CO_NH_bonds_.clear(); #endif /* _OPENMP */ for (resi = 0; resi < Nres; resi++) { @@ -649,7 +666,11 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) # ifdef DSSPDEBUG mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); # endif - CO_NH_bonds.insert( HbondPairType(resi, resj) ); +# ifdef _OPENMP + CO_NH_bondsArray_[mythread].insert( HbondPairType(resi, resj) ); +# else + CO_NH_bonds_.insert( HbondPairType(resi, resj) ); +# endif } //# ifdef DSSPDEBUG // else if (resDelta < 6) @@ -663,6 +684,11 @@ int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) } // END outer loop over residues #ifdef _OPENMP } // END pragma omp parallel + for (unsigned int thread = 1; thread < CO_NH_bondsArray_.size(); thread++) + for (HbondMapType::const_iterator hb = CO_NH_bondsArray_[thread].begin(); + hb != CO_NH_bondsArray_[thread].end(); ++hb) + CO_NH_bondsArray_[0].insert( *hb ); + HbondMapType const& CO_NH_bonds = CO_NH_bondsArray_[0]; #endif t_calchb_.Stop(); diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index c8388327e8..e61965821a 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -1,5 +1,6 @@ #ifndef INC_ACTION_DSSP2_H #define INC_ACTION_DSSP2_H +#include #include #include #include "Action.h" @@ -56,12 +57,19 @@ class Action_DSSP2 : public Action { typedef std::vector SSarrayType; typedef std::vector Sarray; + typedef std::pair HbondPairType; + typedef std::set HbondMapType; int OverHbonds(int, ActionFrame&); void AssignBridge(int, int, BridgeType); - + /// Map resi (CO) to resj (NH) hydrogen bonds +# ifdef _OPENMP + std::vector CO_NH_bondsArray_; +# else + std::vector CO_NH_bonds_; +# endif int debug_; ///< Action debug level unsigned int Nframes_; ///< Number of frames processed, for total SS normalization SSarrayType Residues_; ///< Hold SS data for all residues. From 2b25f71f134df424e75a5a610343044f10b43b61 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 15:21:20 -0400 Subject: [PATCH 53/62] DRR - Cpptraj: Add MPI sync --- src/Action_DSSP2.cpp | 30 ++++++++++++++++++++++++++++++ src/Action_DSSP2.h | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp index 0931e1dbc5..044171232c 100644 --- a/src/Action_DSSP2.cpp +++ b/src/Action_DSSP2.cpp @@ -250,6 +250,21 @@ void Action_DSSP2::SSres::PrintSSchar() const { DSSP_char_[sstype_]); } +#ifdef MPI +/** Sync residue SS and beta counts to master. */ +void Action_DSSP2::SSres::SyncToMaster(Parallel::Comm const& commIn) { + int SSprob[NSSTYPE_ + NBRIDGETYPE_]; + int Buffer[NSSTYPE_ + NBRIDGETYPE_]; + std::copy( SScount_, SScount_ + NSSTYPE_, SSprob ); + std::copy( Bcount_, Bcount_ + NBRIDGETYPE_, SSprob + NSSTYPE_ ); + commIn.ReduceMaster( Buffer, SSprob, NSSTYPE_ + NBRIDGETYPE_, MPI_INT, MPI_SUM ); + if (commIn.Master()) { + std::copy( Buffer, Buffer + NSSTYPE_, SScount_ ); + std::copy( Buffer + NSSTYPE_, Buffer + NSSTYPE_ + NBRIDGETYPE_, Bcount_ ); + } +} +#endif + // ----- Action_DSSP2 ---------------------------------------------------------- Action_DSSP2::Action_DSSP2() : @@ -1005,6 +1020,21 @@ Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) return Action::OK; } +#ifdef MPI +int Action_DSSP2::SyncAction() { + // Consolidate SScount data to master. + for (SSarrayType::iterator res = Residues_.begin(); res != Residues_.end(); ++res) + res->SyncToMaster( Init_.TrajComm() ); + + // Calc total number of frames. + int total_frames = 0; + Init_.TrajComm().ReduceMaster( &total_frames, &Nframes_, 1, MPI_INT, MPI_SUM ); + if (Init_.TrajComm().Master()) + Nframes_ = total_frames; + return 0; +} +#endif + // Action_DSSP::Print() void Action_DSSP2::Print() { if (dsetname_.empty()) return; diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h index e61965821a..c02fd6896d 100644 --- a/src/Action_DSSP2.h +++ b/src/Action_DSSP2.h @@ -32,6 +32,9 @@ class Action_DSSP2 : public Action { Action::RetType Init(ArgList&, ActionInit&, int); Action::RetType Setup(ActionSetup&); Action::RetType DoAction(int, ActionFrame&); +# ifdef MPI + int SyncAction(); +# endif void Print(); /// Class that will hold SS info for each residue @@ -155,6 +158,9 @@ class Action_DSSP2::SSres { void Unassign(); /// \return Relative priority of currently assigned SS type int SSpriority() const; +# ifdef MPI + void SyncToMaster(Parallel::Comm const&); +# endif private: /// \return Relative priority of given SS type static inline int ssPriority(SStype); From 0233cb43ab2ec16e0eba2de300ff5d4bb4430d38 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 15:28:02 -0400 Subject: [PATCH 54/62] DRR - Cpptraj: Minor revision bump for new dssp code. --- src/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.h b/src/Version.h index d1d8d986ce..10d1a0d9f6 100644 --- a/src/Version.h +++ b/src/Version.h @@ -12,7 +12,7 @@ * Whenever a number that precedes is incremented, all subsequent * numbers should be reset to 0. */ -#define CPPTRAJ_INTERNAL_VERSION "V4.17.0" +#define CPPTRAJ_INTERNAL_VERSION "V4.18.0" /// PYTRAJ relies on this #define CPPTRAJ_VERSION_STRING CPPTRAJ_INTERNAL_VERSION #endif From 71759df357c4a7277dd5fe3c05652786199b9c54 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 15:36:11 -0400 Subject: [PATCH 55/62] DRR - Cpptraj: Replace old DSSP command with new one. --- src/Action_DSSP.cpp | 1314 +++++++++++++++++++++++++++++------------- src/Action_DSSP.h | 218 +++++-- src/Action_DSSP2.cpp | 1142 ------------------------------------ src/Action_DSSP2.h | 190 ------ src/Command.cpp | 2 - src/cpptrajdepend | 5 +- src/cpptrajfiles | 1 - 7 files changed, 1088 insertions(+), 1784 deletions(-) delete mode 100644 src/Action_DSSP2.cpp delete mode 100644 src/Action_DSSP2.h diff --git a/src/Action_DSSP.cpp b/src/Action_DSSP.cpp index d35b6de2a5..c2ea6a8e67 100644 --- a/src/Action_DSSP.cpp +++ b/src/Action_DSSP.cpp @@ -1,47 +1,302 @@ #include // sqrt +#include // std::fill, std::min, std::max +#include // SET #include "Action_DSSP.h" #include "CpptrajStdio.h" +#include "DataSet.h" #include "DistRoutines.h" -/// Hbond energy calc prefactor for kcal/mol: q1*q2*E, 0.42*0.20*332 -const double Action_DSSP::DSSP_fac = 27.888; +#ifdef DSSPDEBUG +#include "Constants.h" +#endif +#ifdef _OPENMP +# include +#endif + +/** From the original Kabsch & Sander 1983 paper. Obtained via + * q1 = 0.42e + * q2 = 0.20e + * f = 332 (approximate conversion factor to get energies in kcal/mol) + * fac = q1*q2*f + */ +const double Action_DSSP::DSSP_fac_ = 27.888; + +/** DSSP default cutoff for determining hbonds, from 1983 Kabsch & Sander paper. */ +const double Action_DSSP::DSSP_cut_ = -0.5; + +/** DSSP 1 character SS assignment. */ +const char Action_DSSP::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; + +/** Used for output to STRING data set. */ +const char* Action_DSSP::SSchar_[] = { "0", "E", "B", "G", "H", "I", "T", "S" }; + +/** Full SS names. */ +const char* Action_DSSP::DSSP_name_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; + + +// ----- SSres ----------------------------------------------------------------- +Action_DSSP::SSres::SSres() : + resDataSet_(0), + chirality_(0), + bend_(0), + sstype_(NONE), + num_(-1), + C_(-1), + O_(-1), + N_(-1), + H_(-1), + CA_(-1), + prevIdx_(-1), + nextIdx_(-1), + bridge1idx_(-1), + b1type_(NO_BRIDGE), + bridge2idx_(-1), + b2type_(NO_BRIDGE), + resChar_(' '), + isSelected_(false) +{ + std::fill(SScount_, SScount_ + NSSTYPE_, 0); + std::fill(Bcount_, Bcount_ + NBRIDGETYPE_, 0); + std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); +} + +Action_DSSP::SSres::SSres(SSres const& rhs) : + resDataSet_(rhs.resDataSet_), + chirality_(rhs.chirality_), + bend_(rhs.bend_), + sstype_(rhs.sstype_), + num_(rhs.num_), + C_(rhs.C_), + O_(rhs.O_), + N_(rhs.N_), + H_(rhs.H_), + CA_(rhs.CA_), + prevIdx_(rhs.prevIdx_), + nextIdx_(rhs.nextIdx_), + bridge1idx_(rhs.bridge1idx_), + b1type_(rhs.b1type_), + bridge2idx_(rhs.bridge2idx_), + b2type_(rhs.b2type_), + resChar_(rhs.resChar_), + isSelected_(rhs.isSelected_) +{ + std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); + std::copy(rhs.Bcount_, rhs.Bcount_ + NBRIDGETYPE_, Bcount_); + std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); +} + +Action_DSSP::SSres& Action_DSSP::SSres::operator=(SSres const& rhs) { + if (this == &rhs) return *this; + resDataSet_ = rhs.resDataSet_; + chirality_ = rhs.chirality_; + bend_ = rhs.bend_; + sstype_ = rhs.sstype_; + num_ = rhs.num_; + C_ = rhs.C_; + O_ = rhs.O_; + N_ = rhs.N_; + H_ = rhs.H_; + CA_ = rhs.CA_; + prevIdx_ = rhs.prevIdx_; + nextIdx_ = rhs.nextIdx_; + bridge1idx_ = rhs.bridge1idx_; + b1type_ = rhs.b1type_; + bridge2idx_ = rhs.bridge2idx_; + b2type_ = rhs.b2type_; + resChar_ = rhs.resChar_; + isSelected_ = rhs.isSelected_; + std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); + std::copy(rhs.Bcount_, rhs.Bcount_ + NBRIDGETYPE_, Bcount_); + std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); + return *this; +} + + +void Action_DSSP::SSres::Deselect() { + isSelected_ = false; + C_ = -1; + H_ = -1; + N_ = -1; + O_ = -1; + CA_ = -1; +} + +void Action_DSSP::SSres::Unassign() { + sstype_ = NONE; + bridge1idx_ = -1; + b1type_ = NO_BRIDGE; + bridge2idx_ = -1; + b2type_ = NO_BRIDGE; + std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); +} + +/** Accumulate SS data. */ +void Action_DSSP::SSres::AccumulateData(int frameNum, bool useString, bool betaDetail) +{ + SScount_[sstype_]++; + int idata = (int)sstype_; + const char* sdata = SSchar_[sstype_]; + if (sstype_ == EXTENDED || sstype_ == BRIDGE) { + if (b1type_ == PARALLEL || b2type_ == PARALLEL) { + Bcount_[PARALLEL]++; + if (betaDetail) { + idata = (int)EXTENDED; + sdata = "b"; + } + } + if (b1type_ == ANTIPARALLEL || b2type_ == ANTIPARALLEL) { + Bcount_[ANTIPARALLEL]++; + if (betaDetail) { + idata = (int)BRIDGE; + sdata = "B"; + } + } + // TODO bulge? + } else + Bcount_[NO_BRIDGE]++; + if (useString) + resDataSet_->Add(frameNum, sdata); + else + resDataSet_->Add(frameNum, &idata); +} + +/** Set turn beginning. */ +void Action_DSSP::SSres::SetTurnBegin(TurnType typeIn) { + if (turnChar_[typeIn] == '<') + turnChar_[typeIn] = 'X'; + else + turnChar_[typeIn] = '>'; +} + +void Action_DSSP::SSres::SetTurnEnd(TurnType typeIn) { + if (turnChar_[typeIn] == '>') + turnChar_[typeIn] = 'X'; + else + turnChar_[typeIn] = '<'; +} + +void Action_DSSP::SSres::SetTurn(TurnType typeIn) { + // Do not overwrite an existing end character + if (turnChar_[typeIn] == ' ') { + if (typeIn == T3) turnChar_[typeIn] = '3'; + else if (typeIn == T4) turnChar_[typeIn] = '4'; + else if (typeIn == T5) turnChar_[typeIn] = '5'; + } +} + +int Action_DSSP::SSres::ssPriority(SStype typeIn) { + switch (typeIn) { + case ALPHA : return 8; + case BRIDGE : return 7; + case EXTENDED : return 6; + case H3_10 : return 5; + case HPI : return 4; + case TURN : return 3; + case BEND : return 2; + case NONE : return 1; + } + return 0; +} + +int Action_DSSP::SSres::SSpriority() const { + return ssPriority(sstype_); +} + +void Action_DSSP::SSres::SetSS(SStype typeIn) { + // TODO check if the priority check is necessary + //if (ssPriority(typeIn) > ssPriority(sstype_)) + sstype_ = typeIn; +} + +bool Action_DSSP::SSres::HasTurnStart(TurnType typeIn) const { + if (turnChar_[typeIn] == '>' || + turnChar_[typeIn] == 'X') + return true; + return false; +} + +void Action_DSSP::SSres::SetBridge(int idx, BridgeType btypeIn) { + if (bridge1idx_ == -1) { + bridge1idx_ = idx; + b1type_ = btypeIn; + } else if (bridge2idx_ == -1) { + bridge2idx_ = idx; + b2type_ = btypeIn; + } else + mprinterr("Error: Too many bridges for %i (to %i)\n", Num(), idx+1); +} + +bool Action_DSSP::SSres::HasBridge() const { + if (bridge1idx_ != -1) return true; + return false; +} + +bool Action_DSSP::SSres::IsBridgedWith(int idx2) const { + if (bridge1idx_ == idx2) return true; + if (bridge2idx_ == idx2) return true; + return false; +} + +/*char Action_DSSP::SSres::StrandChar() const { + // TODO ever b2? + return ssChar_[B1]; +}*/ + +void Action_DSSP::SSres::PrintSSchar() const { + static const char btypeChar[] = { ' ', 'p', 'A' }; + mprintf("\t%6i %c %c %c %c %c(%6i) %c(%6i) %6i %6i %6i %c\n", num_+1, resChar_, + turnChar_[T3], turnChar_[T4], turnChar_[T5], + btypeChar[b1type_], bridge1idx_+1, btypeChar[b2type_], bridge2idx_+1, + Bcount_[NO_BRIDGE], Bcount_[PARALLEL], Bcount_[ANTIPARALLEL], + DSSP_char_[sstype_]); +} -const int Action_DSSP::NSSTYPE = 8; +#ifdef MPI +/** Sync residue SS and beta counts to master. */ +void Action_DSSP::SSres::SyncToMaster(Parallel::Comm const& commIn) { + int SSprob[NSSTYPE_ + NBRIDGETYPE_]; + int Buffer[NSSTYPE_ + NBRIDGETYPE_]; + std::copy( SScount_, SScount_ + NSSTYPE_, SSprob ); + std::copy( Bcount_, Bcount_ + NBRIDGETYPE_, SSprob + NSSTYPE_ ); + commIn.ReduceMaster( Buffer, SSprob, NSSTYPE_ + NBRIDGETYPE_, MPI_INT, MPI_SUM ); + if (commIn.Master()) { + std::copy( Buffer, Buffer + NSSTYPE_, SScount_ ); + std::copy( Buffer + NSSTYPE_, Buffer + NSSTYPE_ + NBRIDGETYPE_, Bcount_ ); + } +} +#endif + +// ----- Action_DSSP ----------------------------------------------------------- -// CONSTRUCTOR Action_DSSP::Action_DSSP() : debug_(0), - outfile_(0), - dsspFile_(0), - assignout_(0), - Nres_(0), - Nselected_(0), - Nframe_(0), - printString_(false), BB_N_("N"), BB_H_("H"), BB_C_("C"), BB_O_("O"), - BB_CA_("CA") + BB_CA_("CA"), + outfile_(0), + dsspFile_(0), + assignout_(0), + printString_(false), + betaDetail_(false) {} +// Action_DSSP::Help() void Action_DSSP::Help() const { mprintf("\t[] [out ] [] [sumout ]\n" "\t[assignout ] [totalout ] [ptrajformat]\n" + "\t[betadetail]\n" "\t[namen ] [nameh ] [nameca ]\n" "\t[namec ] [nameo ]\n" " Calculate secondary structure content for residues in .\n" " If sumout not specified, the filename specified by out is used with .sum suffix.\n"); } -const char Action_DSSP::dssp_char[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; -const char* Action_DSSP::SSchar[] = { "0", "b", "B", "G", "H", "I", "T", "S" }; -const char* Action_DSSP::SSname[]={"None", "Para", "Anti", "3-10", "Alpha", "Pi", "Turn", "Bend"}; - // Action_DSSP::Init() Action::RetType Action_DSSP::Init(ArgList& actionArgs, ActionInit& init, int debugIn) { debug_ = debugIn; - Nframe_ = 0; + Nframes_ = 0; // Get keywords outfile_ = init.DFL().AddDataFile( actionArgs.GetStringKey("out"), actionArgs ); std::string temp = actionArgs.GetStringKey("sumout"); @@ -51,6 +306,7 @@ Action::RetType Action_DSSP::Init(ArgList& actionArgs, ActionInit& init, int deb DataFile* totalout = init.DFL().AddDataFile( actionArgs.GetStringKey("totalout"), actionArgs ); assignout_ = init.DFL().AddCpptrajFile(actionArgs.GetStringKey("assignout"), "SS assignment"); printString_ = actionArgs.hasKey("ptrajformat"); + betaDetail_ = actionArgs.hasKey("betadetail"); temp = actionArgs.GetStringKey("namen"); if (!temp.empty()) BB_N_ = temp; temp = actionArgs.GetStringKey("nameh"); @@ -70,11 +326,21 @@ Action::RetType Action_DSSP::Init(ArgList& actionArgs, ActionInit& init, int deb if (dsetname_.empty()) dsetname_ = init.DSL().GenerateDefaultName("DSSP"); // Set up Z labels - if (outfile_ != 0) - outfile_->ProcessArgs("zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"); + if (outfile_ != 0) { + if (betaDetail_) + outfile_->ProcessArgs("zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"); + else + outfile_->ProcessArgs("zlabels None,Ext,Bridge,3-10,Alpha,Pi,Turn,Bend"); + } // Create data sets for total fraction SS vs time. - for (int i = 0; i < NSSTYPE; i++) { - totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, SSname[i])); + for (int i = 0; i < NSSTYPE_; i++) { + const char* aspect = DSSP_name_[i]; + if (betaDetail_ && (SStype)i == EXTENDED) + aspect = "Para"; + else if (betaDetail_ && (SStype)i == BRIDGE) + aspect = "Anti"; + SSname_.push_back( std::string(aspect) ); + totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, aspect)); if (totalDS_[i] == 0) { mprinterr("Error: Could not create DSSP total frac v time data set.\n"); return Action::ERR; @@ -82,20 +348,36 @@ Action::RetType Action_DSSP::Init(ArgList& actionArgs, ActionInit& init, int deb // For now dont add 'None' so colors match up. if (totalout != 0 && i > 0) totalout->AddDataSet( totalDS_[i] ); } +# ifdef _OPENMP + // Each thread needs temp. space to store found hbonds every frame + // to avoid memory clashes when adding/updating in map. +# pragma omp parallel + { +# pragma omp master + { + CO_NH_bondsArray_.resize( omp_get_num_threads() ); + } + } +# endif mprintf( " SECSTRUCT: Calculating secondary structure using mask [%s]\n",Mask_.MaskString()); +# ifdef _OPENMP + mprintf("\tParallelizing calculation with %zu threads.\n", CO_NH_bondsArray_.size()); +# endif if (outfile_ != 0) mprintf("\tDumping results to %s\n", outfile_->DataFilename().full()); if (dsspFile_ != 0) mprintf("\tSum results to %s\n", dsspFile_->DataFilename().full()); + if (betaDetail_) + mprintf("\tWill print parallel/anti-parallel beta in place of extended/bridge\n"); if (printString_) { mprintf("\tSS data for each residue will be stored as a string.\n"); - for (int i = 0; i < NSSTYPE; i++) - mprintf("\t\t%s = %s\n", SSchar[i], SSname[i]); + for (int i = 0; i < NSSTYPE_; i++) + mprintf("\t\t%s = %s\n", SSchar_[i], SSname_[i].c_str()); } else { mprintf("\tSS data for each residue will be stored as integers.\n"); - for (int i = 0; i < NSSTYPE; i++) - mprintf("\t\t%i = %s\n", i, SSname[i]); + for (int i = 0; i < NSSTYPE_; i++) + mprintf("\t\t%i = %s\n", i, SSname_[i].c_str()); } if (assignout_ != 0) mprintf("\tOverall assigned SS will be written to %s\n", assignout_->Filename().full()); @@ -109,433 +391,646 @@ Action::RetType Action_DSSP::Init(ArgList& actionArgs, ActionInit& init, int deb return Action::OK; } +static inline void PrintAtom(Topology const& top, NameType const& name, int idx) { + if (idx > -1) + mprintf(" '%s'=%-12s", *name, top.AtomMaskName(idx/3).c_str()); + else + mprintf(" '%s'=%-12s", *name, "NONE"); +} + // Action_DSSP::Setup() /** Set up secondary structure calculation for all residues selected by the - * mask expression. A residue is selected if at least one of the following - * atoms named "C ", "O ", "N ", or "H " (i.e. standard atom protein - * BB atom names) is selected. The residue is only initialized if it has not - * been previously selected and set up by a prior call to setup. + * mask. A residue is selected if at least one atom in the residue is + * selected by the mask. The coordinate indices (i.e. atom # * 3) for + * the C, O, N, H, and CA atoms are set up if those atoms are present. + * The residue is only initialized if it has not been previously selected + * and set up by a prior call to Setup(). */ -// NOTE: Currently relatively memory-intensive. Eventually set up so that SecStruct and -// CO_HN_Hbond members exist only for selected residues? Use Map? -Action::RetType Action_DSSP::Setup(ActionSetup& setup) { +Action::RetType Action_DSSP::Setup(ActionSetup& setup) +{ // Set up mask for this parm - if ( setup.Top().SetupIntegerMask( Mask_ ) ) return Action::ERR; + if ( setup.Top().SetupCharMask( Mask_ ) ) return Action::ERR; if ( Mask_.None() ) { mprintf("Warning: DSSP: Mask has no atoms.\n"); return Action::SKIP; } - // Initially mark all residues already set up as not selected and - // reset all atom coordinate indices. - for (unsigned int res = 0; res != SecStruct_.size(); res++) { - SecStruct_[res].isSelected = false; - SecStruct_[res].C = -1; - SecStruct_[res].H = -1; - SecStruct_[res].N = -1; - SecStruct_[res].O = -1; - SecStruct_[res].CA = -1; - } - - // Set up SecStruct for each solute residue + // Deselect any existing residues + for (SSarrayType::iterator it = Residues_.begin(); it != Residues_.end(); ++it) + it->Deselect(); + // Set up for each solute residue Range soluteRes = setup.Top().SoluteResidues(); - Nres_ = soluteRes.Back() + 1; - if (debug_>0) mprintf("\tDSSP: Setting up for %i residues.\n", Nres_); - - // Set up for each residue of the current Parm if not already set-up. - SSres RES; - RES.sstype = NONE; - RES.isSelected = false; - RES.C = -1; - RES.O = -1; - RES.N = -1; - RES.H = -1; - RES.CA = -1; - RES.CO_HN_Hbond.assign( Nres_, 0 ); - std::fill( RES.SSprob, RES.SSprob + NSSTYPE, 0 ); - RES.resDataSet = 0; - // Only resize SecStruct if current # residues > previous # residues - if (Nres_ > (int)SecStruct_.size()) - SecStruct_.resize(Nres_, RES); - - // Go through all atoms in mask. Determine which residues have their C, - // O, N, or H atoms selected. Store the actual coordinate index - // (i.e. atom# * 3) instead of atom # for slight speed gain. - for (AtomMask::const_iterator atom = Mask_.begin(); atom!=Mask_.end(); ++atom) { - int res = setup.Top()[*atom].ResNum(); - // If residue is out of bounds skip it - if ( res < Nres_ ) { - //fprintf(stdout,"DEBUG: Atom %i Res %i [%s]\n",*atom,res,P->names[*atom]); - SecStruct_[res].isSelected = true; - if ( setup.Top()[*atom].Name() == BB_C_) - SecStruct_[res].C = (*atom) * 3; - else if ( setup.Top()[*atom].Name() == BB_O_) - SecStruct_[res].O = (*atom) * 3; - else if ( setup.Top()[*atom].Name() == BB_N_) - SecStruct_[res].N = (*atom) * 3; - else if ( setup.Top()[*atom].Name() == BB_H_) - SecStruct_[res].H = (*atom) * 3; - else if ( setup.Top()[*atom].Name() == BB_CA_) - SecStruct_[res].CA = (*atom) * 3; - } - } - - // For each residue selected in the mask, check if residue is missing atoms. - // Set up DataSet if necessary. - Nselected_ = 0; - std::vector missingResidues; + mprintf("\tSetting up for %i solute residues.\n", soluteRes.Size()); + if ((unsigned int)soluteRes.Size() > Residues_.size()) + Residues_.resize( soluteRes.Size() ); MetaData md(dsetname_, "res"); - DataSet::DataType dt = DataSet::INTEGER; - if (printString_) dt = DataSet::STRING; - for (int res = 0; res < Nres_; ++res) { - if (SecStruct_[res].isSelected) { - // Check if C-O/N-H selected. - SecStruct_[res].hasCO = (SecStruct_[res].C != -1 && - SecStruct_[res].O != -1); - SecStruct_[res].hasNH = (SecStruct_[res].N != -1 && - SecStruct_[res].H != -1); - if (!SecStruct_[res].hasCO || !SecStruct_[res].hasNH || SecStruct_[res].CA == -1) - { - missingResidues.push_back( setup.Top().TruncResNameNum( res ) ); - if (debug_ > 0) { - mprintf("Warning: Not all BB atoms found for res %s:", missingResidues.back().c_str()); - if (SecStruct_[res].N==-1) mprintf(" N"); - if (SecStruct_[res].H==-1) mprintf(" H"); - if (SecStruct_[res].C==-1) mprintf(" C"); - if (SecStruct_[res].O==-1) mprintf(" O"); - if (SecStruct_[res].CA==-1) mprintf(" CA"); - mprintf("\n"); + DataSet::DataType dt; + if (printString_) + dt = DataSet::STRING; + else + dt = DataSet::INTEGER; + unsigned int nResSelected = 0; + SSarrayType::iterator Res = Residues_.begin(); + for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) + { + Residue const& thisRes = setup.Top().Res( *ridx ); + if (Res->Num() != -1) { + // Residue has previously been set up. Check that indices match. + if (Res->Num() != *ridx) { + mprinterr("Error: Solute residue index %i does not match previously setup\n" + "Error: index %i\n", *ridx, Res->Num()); + return Action::ERR; + } + } else { + // Set up Residue. TODO also molecule index? + Res->SetNum( *ridx ); + Res->SetResChar( thisRes.SingleCharName() ); + // Determine the previous and next residues + int prevresnum = -1; + int nextresnum = -1; + for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) { + if ( setup.Top()[at].Element() != Atom::HYDROGEN ) { + for (Atom::bond_iterator ib = setup.Top()[at].bondbegin(); + ib != setup.Top()[at].bondend(); ++ib) + { + if ( setup.Top()[*ib].ResNum() < *ridx ) { + if (prevresnum != -1) + mprintf("Warning: Multiple previous residues for res %i\n", *ridx+1); + else + prevresnum = setup.Top()[*ib].ResNum(); + } else if ( setup.Top()[*ib].ResNum() > *ridx ) { + if (nextresnum != -1) + mprintf("Warning: Multiple next residues for res %i\n", *ridx+1); + else + nextresnum = setup.Top()[*ib].ResNum(); + } + } } } - // Set up dataset if necessary - if (SecStruct_[res].resDataSet == 0) { - md.SetIdx( res+1 ); - md.SetLegend( setup.Top().TruncResNameNum(res) ); - // Setup dataset for this residue - SecStruct_[res].resDataSet = Init_.DSL().AddSet( dt, md ); - if (SecStruct_[res].resDataSet == 0) { - mprinterr("Error: Could not allocate DSSP data set for residue %i\n", res+1); +# ifdef DSSPDEBUG + mprintf("\t %8i < %8i < %8i\n", prevresnum+1, *ridx+1, nextresnum+1); +# endif + // Here we assume that residues are sequential! + if (prevresnum > -1) Res->SetPrevIdx( Res-Residues_.begin()-1 ); + if (nextresnum > -1) Res->SetNextIdx( Res-Residues_.begin()+1 ); + } + // Determine if this residue is selected + if (Mask_.AtomsInCharMask(thisRes.FirstAtom(), thisRes.LastAtom())) { + Res->SetSelected( true ); + ++nResSelected; + // Determine atom indices + for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) + { + if ( setup.Top()[at].Name() == BB_C_ ) Res->SetC( at*3 ); + else if ( setup.Top()[at].Name() == BB_O_ ) Res->SetO( at*3 ); + else if ( setup.Top()[at].Name() == BB_N_ ) Res->SetN( at*3 ); + else if ( setup.Top()[at].Name() == BB_H_ ) Res->SetH( at*3 ); + else if ( setup.Top()[at].Name() == BB_CA_ ) Res->SetCA( at*3 ); + } + // Check if residue is missing atoms + if (Res->IsMissingAtoms()) { + mprintf("Warning: Res %s is missing atoms", setup.Top().TruncResNameNum( *ridx ).c_str()); + if (Res->C() == -1) mprintf(" %s", *BB_C_); + if (Res->O() == -1) mprintf(" %s", *BB_N_); + if (Res->N() == -1) mprintf(" %s", *BB_O_); + if (Res->H() == -1) mprintf(" %s", *BB_H_); + if (Res->CA() == -1) mprintf(" %s", *BB_CA_); + mprintf("\n"); + } + // Set up DataSet if necessary + if (Res->Dset() == 0) { + md.SetIdx( *ridx+1 ); + md.SetLegend( setup.Top().TruncResNameNum( *ridx ) ); + // Setup DataSet for this residue + Res->SetDset( Init_.DSL().AddSet( dt, md ) ); + if (Res->Dset() == 0) { + mprinterr("Error: Could not allocate DSSP data set for residue %i\n", *ridx+1); return Action::ERR; } - if (outfile_ != 0) outfile_->AddDataSet(SecStruct_[res].resDataSet); + if (outfile_ != 0) outfile_->AddDataSet( Res->Dset() ); } - ++Nselected_; - } - } - if (!missingResidues.empty()) { - mprintf("Warning: Not all BB atoms found for %zu residues:", missingResidues.size()); - for (std::vector::const_iterator mr = missingResidues.begin(); - mr != missingResidues.end(); ++mr) - mprintf(" %s", mr->c_str()); - mprintf("\nInfo: This is expected for Proline and terminal/non-standard residues.\n" - "Info: Expected BB atom names: N=[%s] H=[%s] C=[%s] O=[%s] CA=[%s]\n", - *BB_N_, *BB_H_, *BB_C_, *BB_O_, *BB_CA_ ); - mprintf("Info: Re-run with action debug level >= 1 to see which residues are missing atoms.\n"); + } // END residue is selected } + mprintf("\t%u of %zu solute residues selected.\n", nResSelected, soluteRes.Size()); - // Count number of selected residues - mprintf("\tMask [%s] corresponds to %u residues.\n", Mask_.MaskString(), Nselected_); -# ifdef DSSPDEBUG - // DEBUG - Print atom nums for each residue set up - for (int res=0; res < Nres_; res++) { - if (SecStruct_[res].isSelected) { - mprintf("DEBUG: Res %i", res + 1); - if (SecStruct_[res].hasCO) - mprintf(" C=%s O=%s",setup.Top().AtomMaskName(SecStruct_[res].C/3).c_str(), - setup.Top().AtomMaskName(SecStruct_[res].O/3).c_str()); - if (SecStruct_[res].hasNH) - mprintf(" N=%s H=%s",setup.Top().AtomMaskName(SecStruct_[res].N/3).c_str(), - setup.Top().AtomMaskName(SecStruct_[res].H/3).c_str()); - if (SecStruct_[res].CA != -1) - mprintf(" CA=%s",setup.Top().AtomMaskName(SecStruct_[res].CA/3).c_str()); + // DEBUG - print each residue set up. + if (debug_ > 0) { + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + { + mprintf(" %8i", it->Num() + 1); + PrintAtom(setup.Top(), BB_C_, it->C()); + PrintAtom(setup.Top(), BB_O_, it->O()); + PrintAtom(setup.Top(), BB_N_, it->N()); + PrintAtom(setup.Top(), BB_H_, it->H()); + PrintAtom(setup.Top(), BB_CA_, it->CA()); + mprintf(" Prev=%8i Next=%8i", it->PrevIdx(), it->NextIdx()); mprintf("\n"); } } -# endif + return Action::OK; } -// Action_DSSP::isBonded() -/** Return 1 if residue 1 CO bonded to residue 2 NH. - * Ensure residue numbers are valid and residues are selected. - */ -int Action_DSSP::isBonded(int res1, int res2) { - if (res1<0 || res2<0 || res1>=Nres_ || res2>=Nres_) return 0; - return SecStruct_[res1].CO_HN_Hbond[res2]; +void Action_DSSP::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { + // By convention, always make idx1 the lower one + int idx1, idx2; + if (idx1in < idx2in) { + idx1 = idx1in; + idx2 = idx2in; + } else { + idx1 = idx2in; + idx2 = idx1in; + } + + SSres& Resi = Residues_[idx1]; + SSres& Resj = Residues_[idx2]; +# ifdef DSSPDEBUG + if (btypeIn == ANTIPARALLEL) { + mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); + } else { + mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); + } +# endif + // Do not duplicate bridges + if (Resi.IsBridgedWith(idx2)) { +# ifdef DSSPDEBUG + mprintf("\t\tAlready present.\n"); +# endif + return; + } + + Resi.SetBridge( idx2, btypeIn ); + Resj.SetBridge( idx1, btypeIn ); } -/// \return true if type1 has priority over type2. -bool Action_DSSP::HasPriority( SStype type1, SStype type2 ) { - switch (type1) { - case ALPHA: return true; - case ANTI: - if (type2 != ALPHA) return true; - break; - case PARA: - if (type2 != ALPHA && type2 != ANTI) return true; - break; - case H3_10: - if (type2 != ALPHA && type2 != ANTI && type2 != PARA) return true; - break; - case HPI: - if (type2 != ALPHA && type2 != ANTI && type2 != PARA && - type2 != H3_10) return true; - break; - case TURN: - if (type2 != ALPHA && type2 != ANTI && type2 != PARA && - type2 != H3_10 && type2 != HPI) return true; - break; - case BEND: - if (type2 != ALPHA && type2 != ANTI && type2 != PARA && - type2 != H3_10 && type2 != HPI && type2 != TURN) return true; - break; - case NONE: break; +/* +void Action_DSSP::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char& currentStrandChar) { + // By convention, always make idx1 the lower one + int idx1, idx2; + if (idx1in < idx2in) { + idx1 = idx1in; + idx2 = idx2in; + } else { + idx1 = idx2in; + idx2 = idx1in; } - return false; + if (btypeIn == ANTIPARALLEL) + mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); + else + mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); + SSres& Resi = Residues_[idx1]; + SSres& Resj = Residues_[idx2]; + // Do not duplicate bridges + if (Resi.IsBridgedWith(idx2)) { + mprintf("\t\tAlready present.\n"); + return; + } + // Determine if we are already part of a ladder. + char ladderChar = ' '; + char resiLadderChar = Residues_[Resi.PrevIdx()].StrandChar(); + if (resiLadderChar == ' ') + resiLadderChar = Residues_[Resi.NextIdx()].StrandChar(); + char resjLadderChar = Residues_[Resj.PrevIdx()].StrandChar(); + if (resjLadderChar == ' ') + resjLadderChar = Residues_[Resj.NextIdx()].StrandChar(); + if (resiLadderChar == ' ' && resjLadderChar == ' ') { + // If both are blank, new ladder. + ladderChar = currentStrandChar; + ++currentStrandChar; + } else if (resiLadderChar != resjLadderChar) { + // They do not match. New ladder. + ladderChar = currentStrandChar; + ++currentStrandChar; + } else + ladderChar = resiLadderChar; + //else if (resiLadderChar != ' ') + // ladderChar = resiLadderChar; + //else + // ladderChar = resjLadderChar; + // Set the bridge; adjust character case if needed + if (btypeIn == ANTIPARALLEL) + ladderChar = toupper( ladderChar ); + mprintf("\t\tResi strand %c, resj strand %c, ladder char %c\n", resiLadderChar, resjLadderChar, ladderChar); + Resi.SetBridge( idx2, ladderChar ); + Resj.SetBridge( idx1, ladderChar ); } +*/ -// Action_DSSP::SSassign() -/** Assign all residues from res1 to res2-1 the secondary structure sstype - * only if it has priority. - * Assumes given residue range is valid. - */ -void Action_DSSP::SSassign(int res1, int res2, SStype typeIn, bool force) { -# ifdef DSSPDEBUG - mprintf("DEBUG:\tCalling SSassign from %i to %i, %s:", res1+1, res2, SSname[typeIn]); -# endif - for (int res = res1; res < res2; res++) { - if (res==Nres_) break; - if ( HasPriority(typeIn, SecStruct_[res].sstype) || force ) { -# ifdef DSSPDEBUG - mprintf(" %i", res+1); // DEBUG -# endif - SecStruct_[res].sstype = typeIn; - } +// TODO use Num()? +static inline int AbsResDelta(int idx1, int idx2) { + int resDelta = idx1 - idx2; + if (resDelta < 0) resDelta = -resDelta; + return resDelta; +} + +static inline void SetMin(int& resGapSize, int& sres0, int& sres1, int Nres, int Pres) +{ + int itmp = AbsResDelta(Nres, Pres); + if (itmp < resGapSize) { + resGapSize = itmp; + sres0 = std::min(Pres, Nres); + sres1 = std::max(Pres, Nres); } -# ifdef DSSPDEBUG - mprintf("\n"); // DEBUG -# endif } - -// Action_DSSP::DoAction() -/** Determine secondary structure by hydrogen bonding pattern. */ -Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) { + + +int Action_DSSP::OverHbonds(int frameNum, ActionFrame& frm) +{ t_total_.Start(); t_calchb_.Start(); - int resi, resj; - const double *C, *O, *H, *N; - double rON, rCH, rOH, rCN, E; - // Determine C=O to H-N hydrogen bonds for each residue to each other residue + // ----- Determine hydrogen bonding ------------ + int resi; + int Nres = (int)Residues_.size(); #ifdef _OPENMP -#pragma omp parallel private(resi,resj,C,O,H,N,rON, rCH, rOH, rCN, E) + int mythread; +#pragma omp parallel private(resi, mythread) { + mythread = omp_get_thread_num(); + CO_NH_bondsArray_[mythread].clear(); #pragma omp for -#endif - for (resi = 0; resi < Nres_; resi++) { - if (SecStruct_[resi].isSelected) { - // Reset previous SS assignment/Hbond status - SecStruct_[resi].sstype = NONE; - SecStruct_[resi].CO_HN_Hbond.assign( Nres_, 0 ); - if (SecStruct_[resi].hasCO) { - C = frm.Frm().CRD(SecStruct_[resi].C); - O = frm.Frm().CRD(SecStruct_[resi].O); - for (resj = 0; resj < Nres_; resj++) { - if (SecStruct_[resj].isSelected && resi != resj && SecStruct_[resj].hasNH) - { - N = frm.Frm().CRD(SecStruct_[resj].N); - H = frm.Frm().CRD(SecStruct_[resj].H); - - rON = 1.0/sqrt(DIST2_NoImage(O, N)); - rCH = 1.0/sqrt(DIST2_NoImage(C, H)); - rOH = 1.0/sqrt(DIST2_NoImage(O, H)); - rCN = 1.0/sqrt(DIST2_NoImage(C, N)); - - E = DSSP_fac * (rON + rCH - rOH - rCN); - if (E < -0.5) { -# ifdef DSSPDEBUG - mprintf("DEBUG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -# endif - SecStruct_[resi].CO_HN_Hbond[resj] = 1; - } - } - } - } - } - } -#ifdef _OPENMP -} // END pragma omp parallel -#endif - t_calchb_.Stop(); - t_assign_.Start(); +#else /* _OPENMP */ + CO_NH_bonds_.clear(); +#endif /* _OPENMP */ + for (resi = 0; resi < Nres; resi++) + { + Residues_[resi].Unassign(); + if (Residues_[resi].IsSelected()) + { + SSres& ResCO = Residues_[resi]; + if (ResCO.HasCO()) + { + const double* Cxyz = frm.Frm().CRD( ResCO.C() ); + const double* Oxyz = frm.Frm().CRD( ResCO.O() ); + for (int resj = 0; resj < Nres; resj++) + { + // Need to consider adjacent residues since delta (2 res) turns possible + if (resi != resj) { + SSres& ResNH = Residues_[resj]; + if (ResNH.IsSelected() && ResNH.HasNH()) + { + const double* Nxyz = frm.Frm().CRD( ResNH.N() ); + const double* Hxyz = frm.Frm().CRD( ResNH.H() ); - // Determine Secondary Structure based on Hbonding pattern. - // In case of structural overlap, priority is given to the structure first - // in this list (see p. 2587 & 2595 in the Kabsch & Sander paper): - // H = 4-helix (alpha helix) - // B = residue in isolated beta bridge (anti) - // E = extended strand, participates in beta-ladder (para) - // G = 3-helix (310 helix) - // I = 5-helix (pi helix) - // T = H-bonded turn - // S = bend - for (resi=0; resi < Nres_; resi++) { - if (SecStruct_[resi].isSelected) { -# ifdef DSSPDEBUG - mprintf("DEBUG: Residue %i -----\n", resi+1); -# endif - // Alpha helices - if ( isBonded( resi - 1, resi+3 ) && isBonded( resi, resi + 4) ) - SSassign(resi, resi+4, ALPHA, false); - - // Beta sheets - only needed if SS not already assigned to alpha - if ( SecStruct_[resi].sstype != ALPHA ) { - for (resj=0; resj < Nres_; resj++) { - if (SecStruct_[resj].isSelected) { - // Only consider residues spaced more than 2 apart - int abs_resi_resj = resi - resj; - if (abs_resi_resj<0) abs_resi_resj = -abs_resi_resj; - if (abs_resi_resj > 2) { - if ( (isBonded(resi-1, resj) && isBonded(resj, resi+1)) || - (isBonded(resj-1, resi) && isBonded(resi, resj+1)) ) - { - // Parallel - // NOTE: Not checking if ANTI since not geometrically possible - // to be anti-parallel and parallel at the same time. + double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); + double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); + double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); + double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); + + double E = DSSP_fac_ * (rON + rCH - rOH - rCN); + if (E < DSSP_cut_) { # ifdef DSSPDEBUG - mprintf("DEBUG:\tAssigning %i to parallel beta.\n", resi+1); + mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); # endif - SecStruct_[resi].sstype = PARA; - break; - } else if ( (isBonded(resi-1, resj+1) && isBonded(resj-1, resi+1)) || - (isBonded(resi, resj ) && isBonded(resj, resi )) ) - { - // Anti-parallel -# ifdef DSSPDEBUG - mprintf("DEBUG:\tAssigning %i to anti-parallel beta.\n", resi+1); +# ifdef _OPENMP + CO_NH_bondsArray_[mythread].insert( HbondPairType(resi, resj) ); +# else + CO_NH_bonds_.insert( HbondPairType(resi, resj) ); # endif - SecStruct_[resi].sstype = ANTI; - break; } - } - } - } - } - - // 3-10 helix - if ( isBonded( resi - 1, resi+2 ) && isBonded( resi, resi + 3) ) - SSassign(resi, resi+3, H3_10, false); +//# ifdef DSSPDEBUG +// else if (resDelta < 6) +// mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); +//# endif + } // END ResNH selected + } // END residues spaced > 2 apart + } // END inner loop over residues + } // END has CO + } // END ResCO selected + } // END outer loop over residues +#ifdef _OPENMP +} // END pragma omp parallel + for (unsigned int thread = 1; thread < CO_NH_bondsArray_.size(); thread++) + for (HbondMapType::const_iterator hb = CO_NH_bondsArray_[thread].begin(); + hb != CO_NH_bondsArray_[thread].end(); ++hb) + CO_NH_bondsArray_[0].insert( *hb ); + HbondMapType const& CO_NH_bonds = CO_NH_bondsArray_[0]; +#endif + t_calchb_.Stop(); - // Pi helix - if ( isBonded( resi - 1, resi+4 ) && isBonded( resi, resi + 5) ) - SSassign(resi, resi+5, HPI, false); - - // n-Turn, n=3,4,5 - for (int n=5; n > 2; n--) { - if ( isBonded(resi, resi + n) ) { - SSassign(resi+1, resi + n, TURN, false); // FIXME: Should this be resi? - break; - } + t_assign_.Start(); + // ----- Do basic assignment ------------------- + for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) + { + int riidx = hb0->first; + int rjidx = hb0->second; + SSres const& Resi = Residues_[riidx]; + SSres const& Resj = Residues_[rjidx]; +# ifdef DSSPDEBUG + mprintf("Res %8i %c -- %8i %c", Resi.Num()+1, Resi.ResChar(), + Resj.Num()+1, Resj.ResChar()); // DBG +# endif + // Spacing between residues i and j + int resDelta = Resj.Num() - Resi.Num(); +# ifdef DSSPDEBUG + mprintf("(%4i)\n", resDelta); +# endif + // Check for H bond from CO i to NH i+n + if (resDelta == 3) { + // 3-TURN + Residues_[riidx ].SetTurnBegin(T3); + Residues_[riidx+1].SetTurn(T3); + Residues_[riidx+2].SetTurn(T3); + Residues_[riidx+3].SetTurnEnd(T3); + Residues_[riidx+1].SetSS( TURN ); + Residues_[riidx+2].SetSS( TURN ); + } else if (resDelta == 4) { + // 4-TURN + Residues_[riidx ].SetTurnBegin(T4); + Residues_[riidx+1].SetTurn(T4); + Residues_[riidx+2].SetTurn(T4); + Residues_[riidx+3].SetTurn(T4); + Residues_[riidx+4].SetTurnEnd(T4); + Residues_[riidx+1].SetSS( TURN ); + Residues_[riidx+2].SetSS( TURN ); + Residues_[riidx+3].SetSS( TURN ); + } else if (resDelta == 5) { + // 5-TURN + Residues_[riidx ].SetTurnBegin(T5); + Residues_[riidx+1].SetTurn(T5); + Residues_[riidx+2].SetTurn(T5); + Residues_[riidx+3].SetTurn(T5); + Residues_[riidx+4].SetTurn(T5); + Residues_[riidx+5].SetTurnEnd(T5); + Residues_[riidx+1].SetSS( TURN ); + Residues_[riidx+2].SetSS( TURN ); + Residues_[riidx+3].SetSS( TURN ); + Residues_[riidx+4].SetSS( TURN ); + } + // Look for bridge. Start with the premise that this bond is part of one + // of the 4 potential bridge patterns, then check if the compliment exists. + HbondMapType::iterator hb; + // Here we want absolute value of spacing. + if (resDelta < 0) resDelta = -resDelta; + if (resDelta > 2) { + // Assume (i,j). Look for (j,i) + hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); + if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG + mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); +# endif + AssignBridge(hb0->first, hb0->second, ANTIPARALLEL); + } + } + resDelta = AbsResDelta(hb0->first+1, hb0->second-1); + if (resDelta > 2) { + // Assume (i-1, j+1). Look for (j-1, i+1) + hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); + if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG + mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); +# endif + AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL); } + } + resDelta = AbsResDelta(hb0->first+1, hb0->second); + if (resDelta > 2) { + // Assume (i-1, j). Check for (j, i+1) PARALLEL + hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); + if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG + mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); +# endif + AssignBridge(hb0->first+1, hb0->second, PARALLEL); + } + } + resDelta = AbsResDelta(hb0->second-1, hb0->first); + if (resDelta > 2) { + // Assume (j, i+1). Check for (i-1, j) + hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); + if (hb != CO_NH_bonds.end()) { +# ifdef DSSPDEBUG + mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); +# endif + AssignBridge(hb0->second-1, hb0->first, PARALLEL); + } + } + } // END loop over Hbonds - // Bend - has lowest priority, so only do if no assignment. - if (SecStruct_[resi].sstype == NONE && resi > 1 && resi < Nres_ - 2) - { - if (SecStruct_[resi-2].CA != -1 && - SecStruct_[resi ].CA != -1 && - SecStruct_[resi+2].CA != -1) - { - const double* CAm2 = frm.Frm().CRD(SecStruct_[resi-2].CA); - const double* CA0 = frm.Frm().CRD(SecStruct_[resi ].CA); - const double* CAp2 = frm.Frm().CRD(SecStruct_[resi+2].CA); - Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); - Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); - CA1.Normalize(); - CA2.Normalize(); - // 1.221730476 rad = 70 degrees - if (CA1.Angle(CA2) > 1.221730476) { + // ----- Do SS assignment ---------------------- + // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' None + // 8 7 6 5 4 3 2 1 + for (resi = 0; resi < Nres; resi++) + { +# ifdef DSSPDEBUG + mprintf("Residue %i\n", resi+1); +# endif + SSres& Resi = Residues_[resi]; + int prevRes = resi - 1; + int nextRes = resi + 1; + int priority = Resi.SSpriority(); + if ( Resi.HasTurnStart(T4) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T4) ) + { + // Alpha helix. +# ifdef DSSPDEBUG + mprintf("ALPHA helix starting at %i\n", resi+1); +# endif + Residues_[resi ].SetSS( ALPHA ); + Residues_[resi+1].SetSS( ALPHA ); + Residues_[resi+2].SetSS( ALPHA ); + Residues_[resi+3].SetSS( ALPHA ); + } else if (Resi.SS() != ALPHA) { + if (priority < 6) { + // Priority < 6 means not alpha or beta assigned yet. + // Check for Beta structure + bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); + bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); + if (Resi.HasBridge()) { + // Regular Beta. Check if previous is assigned EXTENDED in case it + // was assigned via a Beta bulge. + if ( prevHasBridge || nextHasBridge || Residues_[prevRes].SS() == EXTENDED ) + { # ifdef DSSPDEBUG - mprintf("DEBUG: Bend calc %i-%i-%i: %g rad.\n", resi-1, resi+1, resi+3, CA1.Angle(CA2)); + mprintf("Extended BETA bridge at %i\n", resi+1); # endif - SecStruct_[resi].sstype = BEND; + Resi.SetSS( EXTENDED ); + } else { +# ifdef DSSPDEBUG + mprintf("Isolated BETA bridge at %i.\n", resi+1); +# endif + Resi.SetSS( BRIDGE ); } - } - } - } - } // End Initial SS assignment over all residues - - // Change 3-10 and Pi helices that are less than minimal size to Turn - SStype lastType = NONE; - int resStart = -1; - for (resi = 0; resi < Nres_; resi++) { - if (SecStruct_[resi].isSelected) { - if (lastType != SecStruct_[resi].sstype) { - // Secondary structure type has changed. - if (lastType == H3_10) { + } else if (prevHasBridge && nextHasBridge) { + // Potential Beta bulge. Check other strand. + int presb1 = Residues_[prevRes].Bridge1Idx(); + int presb2 = Residues_[prevRes].Bridge2Idx(); + int nresb1 = Residues_[nextRes].Bridge1Idx(); + int nresb2 = Residues_[nextRes].Bridge2Idx(); + int prest1 = Residues_[prevRes].Bridge1Type(); + int prest2 = Residues_[prevRes].Bridge2Type(); + int nrest1 = Residues_[nextRes].Bridge1Type(); + int nrest2 = Residues_[nextRes].Bridge2Type(); # ifdef DSSPDEBUG - mprintf("DEBUG: 3-10 helix length is %i\n", resi - resStart); + mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); # endif - if (resi - resStart < 3) - SSassign(resStart, resi, TURN, true); - } else if (lastType == HPI) { + // The largest allowed gap in the other strand is 5 residues. + // Since we know that next res and previous res both have at + // least 1 bridge, Next B1 - Prev B1 can be the gap to beat. + // Need to also make sure the bridge types match. + int resGapSize = -1; + int sres0 = -1; + int sres1 = -1; + if (nrest1 == prest1) { + resGapSize = AbsResDelta(nresb1, presb1); + sres0 = std::min(presb1, nresb1); + sres1 = std::max(presb1, nresb1); + } + if (presb2 != -1 && nrest1 == prest2) + SetMin( resGapSize, sres0, sres1, nresb1, presb2 ); + if (nresb2 != -1) { + if (nrest2 == prest1) + SetMin( resGapSize, sres0, sres1, nresb2, presb1 ); + if (presb2 != -1 && nrest2 == prest2) + SetMin( resGapSize, sres0, sres1, nresb2, presb2 ); + } # ifdef DSSPDEBUG - mprintf("DEBUG: PI helix length is %i\n", resi - resStart); + mprintf("Min res gap size on other strand = %i (%i to %i)\n", resGapSize, sres0, sres1); # endif - if (resi - resStart < 5) - SSassign(resStart, resi, TURN, true); + // Minimum allowed gap is 4 residues in between, so 5 residues total. + if (resGapSize > -1 && resGapSize < 6) { +# ifdef DSSPDEBUG + mprintf("Beta bulge.\n"); +# endif + if (Residues_[prevRes].SS() != ALPHA) + Residues_[prevRes].SetSS( EXTENDED ); + Resi.SetSS( EXTENDED ); + if (Residues_[nextRes].SS() != ALPHA) + Residues_[nextRes].SetSS( EXTENDED ); + // Set extended on other strand as well + for (int sres = sres0; sres != sres1; sres++) + if (Residues_[sres].SS() != ALPHA) + Residues_[sres].SetSS( EXTENDED ); + } } - resStart = resi; -# ifdef DSSPDEBUG - mprintf("DEBUG: ResStart=%i for type %s\n", resi+1, SSname[SecStruct_[resi].sstype]); -# endif - } - lastType = SecStruct_[resi].sstype; + } // END check for Beta structure + } // END not alpha + } // END loop over residues + + // Check for 3-10 helices. Do this separately so we dont assign regions + // that are too small because other residues have already been assigned. + for (resi = 1; resi < Nres-2; resi++) { + if (Residues_[resi ].SSpriority() < 6 && + Residues_[resi+1].SSpriority() < 6 && + Residues_[resi+2].SSpriority() < 6 && + Residues_[resi ].HasTurnStart(T3) && + Residues_[resi-1].HasTurnStart(T3)) + { + // 3-10 helix +# ifdef DSSPDEBUG + mprintf("3-10 helix starting at %i\n", resi+1); +# endif + Residues_[resi ].SetSS( H3_10 ); + Residues_[resi+1].SetSS( H3_10 ); + Residues_[resi+2].SetSS( H3_10 ); } } - - // Store data for each residue. Get statistics. - int totalSS[NSSTYPE]; - std::fill( totalSS, totalSS + NSSTYPE, 0 ); - for (resi=0; resi < Nres_; resi++) { - if (SecStruct_[resi].isSelected) { - totalSS[SecStruct_[resi].sstype]++; - SecStruct_[resi].SSprob[SecStruct_[resi].sstype]++; - if (printString_) - SecStruct_[resi].resDataSet->Add(frameNum, SSchar[SecStruct_[resi].sstype]); - else - SecStruct_[resi].resDataSet->Add(frameNum, &(SecStruct_[resi].sstype)); + // Check for PI helices, similar to 3-10 helices. + for (resi = 1; resi < Nres-4; resi++) { + if (Residues_[resi ].SSpriority() < 5 && + Residues_[resi+1].SSpriority() < 5 && + Residues_[resi+2].SSpriority() < 5 && + Residues_[resi+3].SSpriority() < 5 && + Residues_[resi+4].SSpriority() < 5 && + Residues_[resi ].HasTurnStart(T5) && + Residues_[resi-1].HasTurnStart(T5)) + { + // PI helix +# ifdef DSSPDEBUG + mprintf("PI helix starting at %i\n", resi+1); +# endif + Residues_[resi ].SetSS( HPI ); + Residues_[resi+1].SetSS( HPI ); + Residues_[resi+2].SetSS( HPI ); + Residues_[resi+3].SetSS( HPI ); + Residues_[resi+4].SetSS( HPI ); + } + } + // Check for bends. Only do if no other assignment. + for (resi = 0; resi < Nres; resi++) { + if (Residues_[resi].IsSelected() && Residues_[resi].SS() == NONE) + { + int im2 = resi - 2; + if (im2 > -1) { + int ip2 = resi + 2; + if (ip2 < Nres) { + SSres& Resi = Residues_[resi]; + if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { + const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); + const double* CA0 = frm.Frm().CRD(Resi.CA()); + const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); + Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); + Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); + CA1.Normalize(); + CA2.Normalize(); + double bAngle = CA1.Angle(CA2); +# ifdef DSSPDEBUG + mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); +# endif + // 1.221730476 rad = 70 degrees + if (bAngle > 1.221730476) { + Resi.SetSS( BEND ); + } + } + } + } + } // END selected and no assignment + } + + // ----- Store data for each res. Get stats ---- + int totalSS[NSSTYPE_]; + std::fill( totalSS, totalSS + NSSTYPE_, 0 ); + int Nselected = 0; + for (resi=0; resi < Nres; resi++) { + SSres& Resi = Residues_[resi]; + if (Resi.IsSelected()) { + if (betaDetail_ && (Resi.SS() == EXTENDED || Resi.SS() == BRIDGE)) + { + if (Resi.Bridge1Type() == ANTIPARALLEL || + Resi.Bridge2Type() == ANTIPARALLEL) + totalSS[BRIDGE]++; + else if (Resi.Bridge1Type() == PARALLEL || + Resi.Bridge2Type() == PARALLEL) + totalSS[EXTENDED]++; + } else + totalSS[Residues_[resi].SS()]++; + Residues_[resi].AccumulateData(frameNum, printString_, betaDetail_); + Nselected++; } } - for (int i = 0; i < NSSTYPE; i++) { + for (int i = 0; i < NSSTYPE_; i++) { float fvar = (float)totalSS[i]; - fvar /= (float)Nselected_; + fvar /= (float)Nselected; totalDS_[i]->Add(frameNum, &fvar); } - ++Nframe_; + + t_assign_.Stop(); t_total_.Stop(); + return 0; +} - +Action::RetType Action_DSSP::DoAction(int frameNum, ActionFrame& frm) +{ + OverHbonds(frameNum, frm); + Nframes_++; + // DEBUG - Print basic assignment + if (debug_ > 1) { + for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) + it->PrintSSchar(); + } return Action::OK; } #ifdef MPI int Action_DSSP::SyncAction() { - // Consolidate SSprob data to master. - int SSprob[8]; - for (std::vector::iterator res = SecStruct_.begin(); res != SecStruct_.end(); ++res) - { - std::fill(SSprob, SSprob+8, 0); - Init_.TrajComm().ReduceMaster( SSprob, res->SSprob, 8, MPI_INT, MPI_SUM ); - if (Init_.TrajComm().Master()) { - for (int idx = 0; idx != 8; idx++) - res->SSprob[idx] = SSprob[idx]; - } - } + // Consolidate SScount data to master. + for (SSarrayType::iterator res = Residues_.begin(); res != Residues_.end(); ++res) + res->SyncToMaster( Init_.TrajComm() ); + // Calc total number of frames. int total_frames = 0; - Init_.TrajComm().ReduceMaster( &total_frames, &Nframe_, 1, MPI_INT, MPI_SUM ); + Init_.TrajComm().ReduceMaster( &total_frames, &Nframes_, 1, MPI_INT, MPI_SUM ); if (Init_.TrajComm().Master()) - Nframe_ = total_frames; + Nframes_ = total_frames; return 0; } #endif @@ -543,7 +1038,7 @@ int Action_DSSP::SyncAction() { // Action_DSSP::Print() void Action_DSSP::Print() { if (dsetname_.empty()) return; - t_total_.WriteTiming(1, "DSSP Total"); + t_total_.WriteTiming(1,"DSSP total"); t_calchb_.WriteTiming(2, "Calc Hbonds", t_total_.Total()); t_assign_.WriteTiming(2, "Assignment ", t_total_.Total()); @@ -551,8 +1046,8 @@ void Action_DSSP::Print() { // for which there is data. Output res nums start from 1. int min_res = -1; int max_res = -1; - for (int resi = 0; resi != (int)SecStruct_.size(); resi++) { - if (SecStruct_[resi].resDataSet != 0) { + for (int resi = 0; resi != (int)Residues_.size(); resi++) { + if (Residues_[resi].Dset() != 0) { if (min_res < 0) min_res = resi; if (resi > max_res) max_res = resi; } @@ -563,25 +1058,38 @@ void Action_DSSP::Print() { } // Calculate average of each SS type across all residues. if (dsspFile_ != 0) { - std::vector dsspData_(NSSTYPE); + std::vector dsspData_(NSSTYPE_); Dimension Xdim( min_res + 1, 1, "Residue" ); MetaData md(dsetname_, "avgss", MetaData::NOT_TS); // Set up a dataset for each SS type. TODO: NONE type? - for (int ss = 1; ss < NSSTYPE; ss++) { + for (int ss = 1; ss < NSSTYPE_; ss++) { md.SetIdx(ss); - md.SetLegend( SSname[ss] ); + md.SetLegend( SSname_[ss] ); dsspData_[ss] = Init_.DSL().AddSet(DataSet::DOUBLE, md); dsspData_[ss]->SetDim(Dimension::X, Xdim); dsspFile_->AddDataSet( dsspData_[ss] ); } // Calc the avg SS type for each residue that has data. - int idx = 0; + int idx = 0; + unsigned int norm = Nframes_; for (int resi = min_res; resi < max_res+1; resi++) { - if (SecStruct_[resi].resDataSet != 0) { - for (int ss = 1; ss < NSSTYPE; ss++) { - double avg = (double)SecStruct_[resi].SSprob[ss]; - avg /= (double)Nframe_; + SSres& Resi = Residues_[resi]; + if (Resi.Dset() != 0) { + //int Nframe = 0; + //for (int ss = 0; ss < NSSTYPE_; ss++) + // Nframe += Resi.SScount((SStype)ss); + //mprintf("DEBUG: Total frames for residue %i is %i\n", Resi.Num()+1, Nframe); + for (int ss = 1; ss < NSSTYPE_; ss++) { + double avg; + if (betaDetail_ && (SStype)ss == EXTENDED) + avg = (double)Resi.Bcount(PARALLEL); + else if (betaDetail_ && (SStype)ss == BRIDGE) + avg = (double)Resi.Bcount(ANTIPARALLEL); + else + avg = (double)Resi.SScount((SStype)ss); + //mprintf("DEBUG:\t\tCount for type %i is %f\n", ss, avg); + avg /= (double)norm; dsspData_[ss]->Add(idx, &avg); } ++idx; @@ -596,18 +1104,26 @@ void Action_DSSP::Print() { for (int resi = min_res; resi < max_res+1; resi++) { if (startRes == -1) startRes = resi; // Convert residue name. - resLine += Residue::ConvertResName( SecStruct_[resi].resDataSet->Meta().Legend() ); + SSres& Resi = Residues_[resi]; + resLine += Resi.ResChar(); // Figure out which SS element is dominant for res if selected - if (SecStruct_[resi].resDataSet != 0) { + if (Resi.Dset() != 0) { int dominantType = 0; int ssmax = 0; - for (int ss = 0; ss < NSSTYPE; ss++) { - if ( SecStruct_[resi].SSprob[ss] > ssmax ) { - ssmax = SecStruct_[resi].SSprob[ss]; + for (int ss = 0; ss < NSSTYPE_; ss++) { + int sscount; + if (betaDetail_ && (SStype)ss == EXTENDED) + sscount = Resi.Bcount(PARALLEL); + else if (betaDetail_ && (SStype)ss == BRIDGE) + sscount = Resi.Bcount(ANTIPARALLEL); + else + sscount = Resi.SScount((SStype)ss); + if ( sscount > ssmax ) { + ssmax = sscount; dominantType = ss; } } - ssLine += dssp_char[dominantType]; + ssLine += DSSP_char_[dominantType]; } else ssLine += '-'; total++; diff --git a/src/Action_DSSP.h b/src/Action_DSSP.h index bdb1d09401..a4a919d3f1 100644 --- a/src/Action_DSSP.h +++ b/src/Action_DSSP.h @@ -1,8 +1,31 @@ #ifndef INC_ACTION_DSSP_H #define INC_ACTION_DSSP_H +#include +#include +#include #include "Action.h" +#include "NameType.h" +#include "CharMask.h" #include "Timer.h" -/// Calculate protein secondary structure using DSSP algorithm. +class DataSet; +/// Do protein secondary structure assignment. +/** Based on protein secondary structure definitions given in: + * Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure: + * Pattern Recognition of Hydrogen-Bonded and Geometrical Features. + * Biopolymers (1983), V.22, pp.2577-2637. + * This is the second major version of this algorithm in CPPTRAJ; it + * has been completely rewritten by DRR. It correctly implements + * detection of Extended and Bridge regions, including beta bulges. + * The various secondary structure types that can be assigned (in order + * of decreasing priority) are: + * H - Alpha helix (4) + * B - Single bridge (ladder of length 1) + * E - Extended beta (ladder length > 1) + * G - 3-10 helix (3) + * I - Pi helix (5) + * T - Turn (3, 4, 5) + * S - Bend (Angle CA i-2, i, i+2 > 70 deg.) + */ class Action_DSSP : public Action { public: Action_DSSP(); @@ -16,54 +39,155 @@ class Action_DSSP : public Action { int SyncAction(); # endif void Print(); - // Enum and static vars - //enum SStype { ALPHA=0, ANTI, PARA, H3_10, HPI, TURN, BEND, NONE }; - enum SStype { NONE=0, PARA, ANTI, H3_10, ALPHA, HPI, TURN, BEND }; - static const int NSSTYPE; ///< # of secondary structure types. - static const double DSSP_fac; ///< DSSP factor for calc. Hbond energy. - static const char dssp_char[]; ///< DSSP 1 character SS names - static const char* SSchar[]; ///< PTRAJ 1 character SS names - static const char* SSname[]; ///< Full SS names - /// Hold SS-related data for each residue - struct SSres { - std::vector CO_HN_Hbond; ///< 1 if this res CO bonded to res X NH. - DataSet* resDataSet; ///< DataSet for SS assignment each frame. - int SSprob[8]; ///< Hold count for each SS type - SStype sstype; ///< Assigned secondary structure - int C; ///< Coord idx of BB carbon - int O; ///< Coord idx of BB oxygen - int N; ///< Coord idx of BB nitrogen - int H; ///< Coord idx of BB hydrogen - int CA; ///< Coord idx of BB alpha carbon - bool isSelected; ///< True if calculating SS for this residue. - bool hasCO; ///< True if both C and O atoms selected. - bool hasNH; ///< True if both N and H atoms selected. - }; - std::vector SecStruct_; ///< Hold SS-related data for all residues - // Class variables - int debug_; - DataFile* outfile_; ///< Output Data file - DataFile* dsspFile_; ///< Sum output file - std::string dsetname_; ///< DSSP data set name - CpptrajFile* assignout_; ///< Assignment output file. - AtomMask Mask_; ///< Mask used to determine selected residues - int Nres_; ///< Current total # of residues - unsigned int Nselected_; ///< Current # residues selected. - int Nframe_; ///< # of frames, for calculating SS avg. - bool printString_; ///< If true print 1 char per residue indicating ss type - ActionInit Init_; ///< Hold pointers to master DSL/DFL - DataSet* totalDS_[8]; - NameType BB_N_; - NameType BB_H_; - NameType BB_C_; - NameType BB_O_; - NameType BB_CA_; + + /// Class that will hold SS info for each residue + class SSres; + /// Secondary structure types + enum SStype { NONE=0, EXTENDED, BRIDGE, H3_10, ALPHA, HPI, TURN, BEND }; + static const int NSSTYPE_ = 8; ///< # of secondary structure types. + static const char DSSP_char_[]; ///< DSSP 1 char names corresponding to SStype + static const char* DSSP_name_[]; ///< Full secondary structure names corresponding to SStype + static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype + /// Turn types + enum TurnType { T3 = 0, T4, T5 }; + static const int NTURNTYPE_ = 3; + /// Beta types + enum BetaType { B1 = 0, B2, S }; + static const int NBETATYPE_ = 3; + /// Bridge direction types + enum BridgeType { NO_BRIDGE = 0, PARALLEL, ANTIPARALLEL }; + static const int NBRIDGETYPE_ = 3; + + static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" + static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol + + typedef std::vector SSarrayType; + typedef std::vector Sarray; + typedef std::pair HbondPairType; + typedef std::set HbondMapType; + + int OverHbonds(int, ActionFrame&); + + void AssignBridge(int, int, BridgeType); + + /// Map resi (CO) to resj (NH) hydrogen bonds +# ifdef _OPENMP + std::vector CO_NH_bondsArray_; +# else + std::vector CO_NH_bonds_; +# endif + int debug_; ///< Action debug level + unsigned int Nframes_; ///< Number of frames processed, for total SS normalization + SSarrayType Residues_; ///< Hold SS data for all residues. + Sarray SSname_; ///< Hold full secondary structure names + NameType BB_N_; ///< Protein N atom name ('N') + NameType BB_H_; ///< Protein N-H atom name ('H') + NameType BB_C_; ///< Protein C atom name ('C') + NameType BB_O_; ///< Protein C-O atom name ('O') + NameType BB_CA_; ///< Protein alpha C name ('CA') + CharMask Mask_; ///< Mask used to determine selected residues. + DataFile* outfile_; ///< Output Data file + DataFile* dsspFile_; ///< Sum output file + CpptrajFile* assignout_; ///< Assignment output file. + std::string dsetname_; ///< DSSP data set name + DataSet* totalDS_[NSSTYPE_]; ///< Hold total SS each frame for each SS type + ActionInit Init_; ///< Hold pointers to master DSL/DFL + bool printString_; ///< If true print 1 char per residue indicating ss type + bool betaDetail_; ///< If true use para/anti in place of extended/bridge Timer t_total_; Timer t_calchb_; Timer t_assign_; - // Private fns - inline int isBonded(int, int); - inline void SSassign(int, int, SStype, bool); - static inline bool HasPriority(SStype, SStype); +}; + +// ============================================================================= +class Action_DSSP::SSres { + public: + SSres(); + SSres(SSres const&); + SSres& operator=(SSres const&); + int SScount(SStype t) const { return SScount_[t]; } + int Bcount(BridgeType t) const { return Bcount_[t]; } + SStype SS() const { return sstype_; } + int Num() const { return num_; } + char ResChar() const { return resChar_; } + bool IsSelected() const { return isSelected_; } + int C() const { return C_; } + int O() const { return O_; } + int N() const { return N_; } + int H() const { return H_; } + int CA() const { return CA_; } + int PrevIdx() const { return prevIdx_; } + int NextIdx() const { return nextIdx_; } + int Bridge1Idx() const { return bridge1idx_; } + BridgeType Bridge1Type() const { return b1type_;} + int Bridge2Idx() const { return bridge2idx_; } + BridgeType Bridge2Type() const { return b2type_;} + DataSet* Dset() const { return resDataSet_; } + + bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } + bool HasCO() const { return (C_!=-1 && O_!=-1); } + bool HasNH() const { return (N_!=-1 && H_!=-1); } + bool HasCA() const { return (CA_!=-1); } + + bool HasTurnStart(TurnType) const; + bool HasBridge() const; + bool IsBridgedWith(int) const; +// char StrandChar() const; + void PrintSSchar() const; + + void SetTurnBegin(TurnType); + void SetTurn(TurnType); + void SetTurnEnd(TurnType); + /// Set a bridge between this res and other res index into Residues_ + void SetBridge(int, BridgeType); + + void AccumulateData(int, bool, bool); + + void SetSS(SStype); + void SetNum(int i) { num_ = i; } + void SetResChar(char c) { resChar_ = c; } + void SetSelected(bool b) { isSelected_ = b; } + void SetC(int i) { C_ = i; } + void SetO(int i) { O_ = i; } + void SetN(int i) { N_ = i; } + void SetH(int i) { H_ = i; } + void SetCA(int i) { CA_ = i; } + void SetPrevIdx(int i) { prevIdx_ = i; } + void SetNextIdx(int i) { nextIdx_ = i; } + void SetDset(DataSet* d) { resDataSet_ = d; } + /// Deselect this residue and reset coordinate indices. + void Deselect(); + /// Reset hbonds, pattern and SS type assignments + void Unassign(); + /// \return Relative priority of currently assigned SS type + int SSpriority() const; +# ifdef MPI + void SyncToMaster(Parallel::Comm const&); +# endif + private: + /// \return Relative priority of given SS type + static inline int ssPriority(SStype); + + DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. + double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] + double bend_; ///< Angle CA[i-2, i, i+2] + int SScount_[NSSTYPE_]; ///< Hold count for each SS type + int Bcount_[NBRIDGETYPE_]; ///< Hold count for Beta types + SStype sstype_; ///< SS assignment for this frame + int num_; ///< Residue index in Topology + int C_; ///< Coord idx of BB carbon + int O_; ///< Coord idx of BB oxygen + int N_; ///< Coord idx of BB nitrogen + int H_; ///< Coord idx of BB hydrogen + int CA_; ///< Coord idx of BB alpha carbon + int prevIdx_; ///< Index in Residues_ of previous residue + int nextIdx_; ///< Index in Residues_ of next residue + int bridge1idx_; ///< Index in Residues_ of res this is bridged to + BridgeType b1type_; ///< Type of bridge1 + int bridge2idx_; ///< Index in Residues_ of res this is bridged to + BridgeType b2type_; ///< Type of bridge2 + char resChar_; ///< Single char residue ID + char turnChar_[NTURNTYPE_]; ///< Character if part of N turn + bool isSelected_; ///< True if calculating SS for this residue. }; #endif diff --git a/src/Action_DSSP2.cpp b/src/Action_DSSP2.cpp deleted file mode 100644 index 044171232c..0000000000 --- a/src/Action_DSSP2.cpp +++ /dev/null @@ -1,1142 +0,0 @@ -#include // sqrt -#include // std::fill, std::min, std::max -#include // SET -#include "Action_DSSP2.h" -#include "CpptrajStdio.h" -#include "DataSet.h" -#include "DistRoutines.h" -#ifdef DSSPDEBUG -#include "Constants.h" -#endif -#ifdef _OPENMP -# include -#endif - -/** From the original Kabsch & Sander 1983 paper. Obtained via - * q1 = 0.42e - * q2 = 0.20e - * f = 332 (approximate conversion factor to get energies in kcal/mol) - * fac = q1*q2*f - */ -const double Action_DSSP2::DSSP_fac_ = 27.888; - -/** DSSP default cutoff for determining hbonds, from 1983 Kabsch & Sander paper. */ -const double Action_DSSP2::DSSP_cut_ = -0.5; - -/** DSSP 1 character SS assignment. */ -const char Action_DSSP2::DSSP_char_[] = { ' ', 'E', 'B', 'G', 'H', 'I', 'T', 'S' }; - -/** Used for output to STRING data set. */ -const char* Action_DSSP2::SSchar_[] = { "0", "E", "B", "G", "H", "I", "T", "S" }; - -/** Full SS names. */ -const char* Action_DSSP2::DSSP_name_[]={"None", "Extended", "Bridge", "3-10", "Alpha", "Pi", "Turn", "Bend"}; - - -// ----- SSres ----------------------------------------------------------------- -Action_DSSP2::SSres::SSres() : - resDataSet_(0), - chirality_(0), - bend_(0), - sstype_(NONE), - num_(-1), - C_(-1), - O_(-1), - N_(-1), - H_(-1), - CA_(-1), - prevIdx_(-1), - nextIdx_(-1), - bridge1idx_(-1), - b1type_(NO_BRIDGE), - bridge2idx_(-1), - b2type_(NO_BRIDGE), - resChar_(' '), - isSelected_(false) -{ - std::fill(SScount_, SScount_ + NSSTYPE_, 0); - std::fill(Bcount_, Bcount_ + NBRIDGETYPE_, 0); - std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); -} - -Action_DSSP2::SSres::SSres(SSres const& rhs) : - resDataSet_(rhs.resDataSet_), - chirality_(rhs.chirality_), - bend_(rhs.bend_), - sstype_(rhs.sstype_), - num_(rhs.num_), - C_(rhs.C_), - O_(rhs.O_), - N_(rhs.N_), - H_(rhs.H_), - CA_(rhs.CA_), - prevIdx_(rhs.prevIdx_), - nextIdx_(rhs.nextIdx_), - bridge1idx_(rhs.bridge1idx_), - b1type_(rhs.b1type_), - bridge2idx_(rhs.bridge2idx_), - b2type_(rhs.b2type_), - resChar_(rhs.resChar_), - isSelected_(rhs.isSelected_) -{ - std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); - std::copy(rhs.Bcount_, rhs.Bcount_ + NBRIDGETYPE_, Bcount_); - std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); -} - -Action_DSSP2::SSres& Action_DSSP2::SSres::operator=(SSres const& rhs) { - if (this == &rhs) return *this; - resDataSet_ = rhs.resDataSet_; - chirality_ = rhs.chirality_; - bend_ = rhs.bend_; - sstype_ = rhs.sstype_; - num_ = rhs.num_; - C_ = rhs.C_; - O_ = rhs.O_; - N_ = rhs.N_; - H_ = rhs.H_; - CA_ = rhs.CA_; - prevIdx_ = rhs.prevIdx_; - nextIdx_ = rhs.nextIdx_; - bridge1idx_ = rhs.bridge1idx_; - b1type_ = rhs.b1type_; - bridge2idx_ = rhs.bridge2idx_; - b2type_ = rhs.b2type_; - resChar_ = rhs.resChar_; - isSelected_ = rhs.isSelected_; - std::copy(rhs.SScount_, rhs.SScount_ + NSSTYPE_, SScount_); - std::copy(rhs.Bcount_, rhs.Bcount_ + NBRIDGETYPE_, Bcount_); - std::copy(rhs.turnChar_, rhs.turnChar_ + NTURNTYPE_, turnChar_); - return *this; -} - - -void Action_DSSP2::SSres::Deselect() { - isSelected_ = false; - C_ = -1; - H_ = -1; - N_ = -1; - O_ = -1; - CA_ = -1; -} - -void Action_DSSP2::SSres::Unassign() { - sstype_ = NONE; - bridge1idx_ = -1; - b1type_ = NO_BRIDGE; - bridge2idx_ = -1; - b2type_ = NO_BRIDGE; - std::fill(turnChar_, turnChar_ + NTURNTYPE_, ' '); -} - -/** Accumulate SS data. */ -void Action_DSSP2::SSres::AccumulateData(int frameNum, bool useString, bool betaDetail) -{ - SScount_[sstype_]++; - int idata = (int)sstype_; - const char* sdata = SSchar_[sstype_]; - if (sstype_ == EXTENDED || sstype_ == BRIDGE) { - if (b1type_ == PARALLEL || b2type_ == PARALLEL) { - Bcount_[PARALLEL]++; - if (betaDetail) { - idata = (int)EXTENDED; - sdata = "b"; - } - } - if (b1type_ == ANTIPARALLEL || b2type_ == ANTIPARALLEL) { - Bcount_[ANTIPARALLEL]++; - if (betaDetail) { - idata = (int)BRIDGE; - sdata = "B"; - } - } - // TODO bulge? - } else - Bcount_[NO_BRIDGE]++; - if (useString) - resDataSet_->Add(frameNum, sdata); - else - resDataSet_->Add(frameNum, &idata); -} - -/** Set turn beginning. */ -void Action_DSSP2::SSres::SetTurnBegin(TurnType typeIn) { - if (turnChar_[typeIn] == '<') - turnChar_[typeIn] = 'X'; - else - turnChar_[typeIn] = '>'; -} - -void Action_DSSP2::SSres::SetTurnEnd(TurnType typeIn) { - if (turnChar_[typeIn] == '>') - turnChar_[typeIn] = 'X'; - else - turnChar_[typeIn] = '<'; -} - -void Action_DSSP2::SSres::SetTurn(TurnType typeIn) { - // Do not overwrite an existing end character - if (turnChar_[typeIn] == ' ') { - if (typeIn == T3) turnChar_[typeIn] = '3'; - else if (typeIn == T4) turnChar_[typeIn] = '4'; - else if (typeIn == T5) turnChar_[typeIn] = '5'; - } -} - -int Action_DSSP2::SSres::ssPriority(SStype typeIn) { - switch (typeIn) { - case ALPHA : return 8; - case BRIDGE : return 7; - case EXTENDED : return 6; - case H3_10 : return 5; - case HPI : return 4; - case TURN : return 3; - case BEND : return 2; - case NONE : return 1; - } - return 0; -} - -int Action_DSSP2::SSres::SSpriority() const { - return ssPriority(sstype_); -} - -void Action_DSSP2::SSres::SetSS(SStype typeIn) { - // TODO check if the priority check is necessary - //if (ssPriority(typeIn) > ssPriority(sstype_)) - sstype_ = typeIn; -} - -bool Action_DSSP2::SSres::HasTurnStart(TurnType typeIn) const { - if (turnChar_[typeIn] == '>' || - turnChar_[typeIn] == 'X') - return true; - return false; -} - -void Action_DSSP2::SSres::SetBridge(int idx, BridgeType btypeIn) { - if (bridge1idx_ == -1) { - bridge1idx_ = idx; - b1type_ = btypeIn; - } else if (bridge2idx_ == -1) { - bridge2idx_ = idx; - b2type_ = btypeIn; - } else - mprinterr("Error: Too many bridges for %i (to %i)\n", Num(), idx+1); -} - -bool Action_DSSP2::SSres::HasBridge() const { - if (bridge1idx_ != -1) return true; - return false; -} - -bool Action_DSSP2::SSres::IsBridgedWith(int idx2) const { - if (bridge1idx_ == idx2) return true; - if (bridge2idx_ == idx2) return true; - return false; -} - -/*char Action_DSSP2::SSres::StrandChar() const { - // TODO ever b2? - return ssChar_[B1]; -}*/ - -void Action_DSSP2::SSres::PrintSSchar() const { - static const char btypeChar[] = { ' ', 'p', 'A' }; - mprintf("\t%6i %c %c %c %c %c(%6i) %c(%6i) %6i %6i %6i %c\n", num_+1, resChar_, - turnChar_[T3], turnChar_[T4], turnChar_[T5], - btypeChar[b1type_], bridge1idx_+1, btypeChar[b2type_], bridge2idx_+1, - Bcount_[NO_BRIDGE], Bcount_[PARALLEL], Bcount_[ANTIPARALLEL], - DSSP_char_[sstype_]); -} - -#ifdef MPI -/** Sync residue SS and beta counts to master. */ -void Action_DSSP2::SSres::SyncToMaster(Parallel::Comm const& commIn) { - int SSprob[NSSTYPE_ + NBRIDGETYPE_]; - int Buffer[NSSTYPE_ + NBRIDGETYPE_]; - std::copy( SScount_, SScount_ + NSSTYPE_, SSprob ); - std::copy( Bcount_, Bcount_ + NBRIDGETYPE_, SSprob + NSSTYPE_ ); - commIn.ReduceMaster( Buffer, SSprob, NSSTYPE_ + NBRIDGETYPE_, MPI_INT, MPI_SUM ); - if (commIn.Master()) { - std::copy( Buffer, Buffer + NSSTYPE_, SScount_ ); - std::copy( Buffer + NSSTYPE_, Buffer + NSSTYPE_ + NBRIDGETYPE_, Bcount_ ); - } -} -#endif - -// ----- Action_DSSP2 ---------------------------------------------------------- - -Action_DSSP2::Action_DSSP2() : - debug_(0), - BB_N_("N"), - BB_H_("H"), - BB_C_("C"), - BB_O_("O"), - BB_CA_("CA"), - outfile_(0), - dsspFile_(0), - assignout_(0), - printString_(false), - betaDetail_(false) -{} - -// Action_DSSP2::Help() -void Action_DSSP2::Help() const { - mprintf("\t[] [out ] [] [sumout ]\n" - "\t[assignout ] [totalout ] [ptrajformat]\n" - "\t[betadetail]\n" - "\t[namen ] [nameh ] [nameca ]\n" - "\t[namec ] [nameo ]\n" - " Calculate secondary structure content for residues in .\n" - " If sumout not specified, the filename specified by out is used with .sum suffix.\n"); -} - -// Action_DSSP2::Init() -Action::RetType Action_DSSP2::Init(ArgList& actionArgs, ActionInit& init, int debugIn) -{ - debug_ = debugIn; - Nframes_ = 0; - // Get keywords - outfile_ = init.DFL().AddDataFile( actionArgs.GetStringKey("out"), actionArgs ); - std::string temp = actionArgs.GetStringKey("sumout"); - if (temp.empty() && outfile_ != 0) - temp = outfile_->DataFilename().Full() + ".sum"; - dsspFile_ = init.DFL().AddDataFile( temp ); - DataFile* totalout = init.DFL().AddDataFile( actionArgs.GetStringKey("totalout"), actionArgs ); - assignout_ = init.DFL().AddCpptrajFile(actionArgs.GetStringKey("assignout"), "SS assignment"); - printString_ = actionArgs.hasKey("ptrajformat"); - betaDetail_ = actionArgs.hasKey("betadetail"); - temp = actionArgs.GetStringKey("namen"); - if (!temp.empty()) BB_N_ = temp; - temp = actionArgs.GetStringKey("nameh"); - if (!temp.empty()) BB_H_ = temp; - temp = actionArgs.GetStringKey("namec"); - if (!temp.empty()) BB_C_ = temp; - temp = actionArgs.GetStringKey("nameo"); - if (!temp.empty()) BB_O_ = temp; - temp = actionArgs.GetStringKey("nameca"); - if (!temp.empty()) BB_CA_ = temp; - // Get masks - if (Mask_.SetMaskString( actionArgs.GetMaskNext() )) return Action::ERR; - - // Set up the DSSP data set - dsetname_ = actionArgs.GetStringNext(); - // Set default name if none specified - if (dsetname_.empty()) - dsetname_ = init.DSL().GenerateDefaultName("DSSP"); - // Set up Z labels - if (outfile_ != 0) { - if (betaDetail_) - outfile_->ProcessArgs("zlabels None,Para,Anti,3-10,Alpha,Pi,Turn,Bend"); - else - outfile_->ProcessArgs("zlabels None,Ext,Bridge,3-10,Alpha,Pi,Turn,Bend"); - } - // Create data sets for total fraction SS vs time. - for (int i = 0; i < NSSTYPE_; i++) { - const char* aspect = DSSP_name_[i]; - if (betaDetail_ && (SStype)i == EXTENDED) - aspect = "Para"; - else if (betaDetail_ && (SStype)i == BRIDGE) - aspect = "Anti"; - SSname_.push_back( std::string(aspect) ); - totalDS_[i] = init.DSL().AddSet(DataSet::FLOAT, MetaData(dsetname_, aspect)); - if (totalDS_[i] == 0) { - mprinterr("Error: Could not create DSSP total frac v time data set.\n"); - return Action::ERR; - } - // For now dont add 'None' so colors match up. - if (totalout != 0 && i > 0) totalout->AddDataSet( totalDS_[i] ); - } -# ifdef _OPENMP - // Each thread needs temp. space to store found hbonds every frame - // to avoid memory clashes when adding/updating in map. -# pragma omp parallel - { -# pragma omp master - { - CO_NH_bondsArray_.resize( omp_get_num_threads() ); - } - } -# endif - - mprintf( " SECSTRUCT: Calculating secondary structure using mask [%s]\n",Mask_.MaskString()); -# ifdef _OPENMP - mprintf("\tParallelizing calculation with %zu threads.\n", CO_NH_bondsArray_.size()); -# endif - if (outfile_ != 0) - mprintf("\tDumping results to %s\n", outfile_->DataFilename().full()); - if (dsspFile_ != 0) - mprintf("\tSum results to %s\n", dsspFile_->DataFilename().full()); - if (betaDetail_) - mprintf("\tWill print parallel/anti-parallel beta in place of extended/bridge\n"); - if (printString_) { - mprintf("\tSS data for each residue will be stored as a string.\n"); - for (int i = 0; i < NSSTYPE_; i++) - mprintf("\t\t%s = %s\n", SSchar_[i], SSname_[i].c_str()); - } else { - mprintf("\tSS data for each residue will be stored as integers.\n"); - for (int i = 0; i < NSSTYPE_; i++) - mprintf("\t\t%i = %s\n", i, SSname_[i].c_str()); - } - if (assignout_ != 0) - mprintf("\tOverall assigned SS will be written to %s\n", assignout_->Filename().full()); - mprintf("\tBackbone Atom Names: N=[%s] H=[%s] C=[%s] O=[%s] CA=[%s]\n", - *BB_N_, *BB_H_, *BB_C_, *BB_O_, *BB_CA_ ); - mprintf("# Citation: Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure:\n" - "# Pattern Recognition of Hydrogen-Bonded and Geometrical Features.\"\n" - "# Biopolymers (1983), V.22, pp.2577-2637.\n" ); - init.DSL().SetDataSetsPending(true); - Init_ = init; - return Action::OK; -} - -static inline void PrintAtom(Topology const& top, NameType const& name, int idx) { - if (idx > -1) - mprintf(" '%s'=%-12s", *name, top.AtomMaskName(idx/3).c_str()); - else - mprintf(" '%s'=%-12s", *name, "NONE"); -} - -// Action_DSSP2::Setup() -/** Set up secondary structure calculation for all residues selected by the - * mask. A residue is selected if at least one atom in the residue is - * selected by the mask. The coordinate indices (i.e. atom # * 3) for - * the C, O, N, H, and CA atoms are set up if those atoms are present. - * The residue is only initialized if it has not been previously selected - * and set up by a prior call to setup. - */ -Action::RetType Action_DSSP2::Setup(ActionSetup& setup) -{ - // Set up mask for this parm - if ( setup.Top().SetupCharMask( Mask_ ) ) return Action::ERR; - if ( Mask_.None() ) { - mprintf("Warning: DSSP: Mask has no atoms.\n"); - return Action::SKIP; - } - - // Deselect any existing residues - for (SSarrayType::iterator it = Residues_.begin(); it != Residues_.end(); ++it) - it->Deselect(); - // Set up for each solute residue - Range soluteRes = setup.Top().SoluteResidues(); - mprintf("\tSetting up for %i solute residues.\n", soluteRes.Size()); - if ((unsigned int)soluteRes.Size() > Residues_.size()) - Residues_.resize( soluteRes.Size() ); - MetaData md(dsetname_, "res"); - DataSet::DataType dt; - if (printString_) - dt = DataSet::STRING; - else - dt = DataSet::INTEGER; - unsigned int nResSelected = 0; - SSarrayType::iterator Res = Residues_.begin(); - for (Range::const_iterator ridx = soluteRes.begin(); ridx != soluteRes.end(); ++ridx, ++Res) - { - Residue const& thisRes = setup.Top().Res( *ridx ); - if (Res->Num() != -1) { - // Residue has previously been set up. Check that indices match. - if (Res->Num() != *ridx) { - mprinterr("Error: Solute residue index %i does not match previously setup\n" - "Error: index %i\n", *ridx, Res->Num()); - return Action::ERR; - } - } else { - // Set up Residue. TODO also molecule index? - Res->SetNum( *ridx ); - Res->SetResChar( thisRes.SingleCharName() ); - // Determine the previous and next residues - int prevresnum = -1; - int nextresnum = -1; - for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) { - if ( setup.Top()[at].Element() != Atom::HYDROGEN ) { - for (Atom::bond_iterator ib = setup.Top()[at].bondbegin(); - ib != setup.Top()[at].bondend(); ++ib) - { - if ( setup.Top()[*ib].ResNum() < *ridx ) { - if (prevresnum != -1) - mprintf("Warning: Multiple previous residues for res %i\n", *ridx+1); - else - prevresnum = setup.Top()[*ib].ResNum(); - } else if ( setup.Top()[*ib].ResNum() > *ridx ) { - if (nextresnum != -1) - mprintf("Warning: Multiple next residues for res %i\n", *ridx+1); - else - nextresnum = setup.Top()[*ib].ResNum(); - } - } - } - } -# ifdef DSSPDEBUG - mprintf("\t %8i < %8i < %8i\n", prevresnum+1, *ridx+1, nextresnum+1); -# endif - // Here we assume that residues are sequential! - if (prevresnum > -1) Res->SetPrevIdx( Res-Residues_.begin()-1 ); - if (nextresnum > -1) Res->SetNextIdx( Res-Residues_.begin()+1 ); - } - // Determine if this residue is selected - if (Mask_.AtomsInCharMask(thisRes.FirstAtom(), thisRes.LastAtom())) { - Res->SetSelected( true ); - ++nResSelected; - // Determine atom indices - for (int at = thisRes.FirstAtom(); at != thisRes.LastAtom(); at++) - { - if ( setup.Top()[at].Name() == BB_C_ ) Res->SetC( at*3 ); - else if ( setup.Top()[at].Name() == BB_O_ ) Res->SetO( at*3 ); - else if ( setup.Top()[at].Name() == BB_N_ ) Res->SetN( at*3 ); - else if ( setup.Top()[at].Name() == BB_H_ ) Res->SetH( at*3 ); - else if ( setup.Top()[at].Name() == BB_CA_ ) Res->SetCA( at*3 ); - } - // Check if residue is missing atoms - if (Res->IsMissingAtoms()) { - mprintf("Warning: Res %s is missing atoms", setup.Top().TruncResNameNum( *ridx ).c_str()); - if (Res->C() == -1) mprintf(" %s", *BB_C_); - if (Res->O() == -1) mprintf(" %s", *BB_N_); - if (Res->N() == -1) mprintf(" %s", *BB_O_); - if (Res->H() == -1) mprintf(" %s", *BB_H_); - if (Res->CA() == -1) mprintf(" %s", *BB_CA_); - mprintf("\n"); - } - // Set up DataSet if necessary - if (Res->Dset() == 0) { - md.SetIdx( *ridx+1 ); - md.SetLegend( setup.Top().TruncResNameNum( *ridx ) ); - // Setup DataSet for this residue - Res->SetDset( Init_.DSL().AddSet( dt, md ) ); - if (Res->Dset() == 0) { - mprinterr("Error: Could not allocate DSSP data set for residue %i\n", *ridx+1); - return Action::ERR; - } - if (outfile_ != 0) outfile_->AddDataSet( Res->Dset() ); - } - } // END residue is selected - } - mprintf("\t%u of %zu solute residues selected.\n", nResSelected, soluteRes.Size()); - - // DEBUG - print each residue set up. - if (debug_ > 0) { - for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) - { - mprintf(" %8i", it->Num() + 1); - PrintAtom(setup.Top(), BB_C_, it->C()); - PrintAtom(setup.Top(), BB_O_, it->O()); - PrintAtom(setup.Top(), BB_N_, it->N()); - PrintAtom(setup.Top(), BB_H_, it->H()); - PrintAtom(setup.Top(), BB_CA_, it->CA()); - mprintf(" Prev=%8i Next=%8i", it->PrevIdx(), it->NextIdx()); - mprintf("\n"); - } - } - - return Action::OK; -} - -void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn) { - // By convention, always make idx1 the lower one - int idx1, idx2; - if (idx1in < idx2in) { - idx1 = idx1in; - idx2 = idx2in; - } else { - idx1 = idx2in; - idx2 = idx1in; - } - - SSres& Resi = Residues_[idx1]; - SSres& Resj = Residues_[idx2]; -# ifdef DSSPDEBUG - if (btypeIn == ANTIPARALLEL) { - mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); - } else { - mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); - } -# endif - // Do not duplicate bridges - if (Resi.IsBridgedWith(idx2)) { -# ifdef DSSPDEBUG - mprintf("\t\tAlready present.\n"); -# endif - return; - } - - Resi.SetBridge( idx2, btypeIn ); - Resj.SetBridge( idx1, btypeIn ); -} - -/* -void Action_DSSP2::AssignBridge(int idx1in, int idx2in, BridgeType btypeIn, char& currentStrandChar) { - // By convention, always make idx1 the lower one - int idx1, idx2; - if (idx1in < idx2in) { - idx1 = idx1in; - idx2 = idx2in; - } else { - idx1 = idx2in; - idx2 = idx1in; - } - if (btypeIn == ANTIPARALLEL) - mprintf("\t\tAssignBridge %i to %i, Antiparallel\n", idx1+1, idx2+1); - else - mprintf("\t\tAssignBridge %i to %i, Parallel\n", idx1+1, idx2+1); - SSres& Resi = Residues_[idx1]; - SSres& Resj = Residues_[idx2]; - // Do not duplicate bridges - if (Resi.IsBridgedWith(idx2)) { - mprintf("\t\tAlready present.\n"); - return; - } - // Determine if we are already part of a ladder. - char ladderChar = ' '; - char resiLadderChar = Residues_[Resi.PrevIdx()].StrandChar(); - if (resiLadderChar == ' ') - resiLadderChar = Residues_[Resi.NextIdx()].StrandChar(); - char resjLadderChar = Residues_[Resj.PrevIdx()].StrandChar(); - if (resjLadderChar == ' ') - resjLadderChar = Residues_[Resj.NextIdx()].StrandChar(); - if (resiLadderChar == ' ' && resjLadderChar == ' ') { - // If both are blank, new ladder. - ladderChar = currentStrandChar; - ++currentStrandChar; - } else if (resiLadderChar != resjLadderChar) { - // They do not match. New ladder. - ladderChar = currentStrandChar; - ++currentStrandChar; - } else - ladderChar = resiLadderChar; - //else if (resiLadderChar != ' ') - // ladderChar = resiLadderChar; - //else - // ladderChar = resjLadderChar; - // Set the bridge; adjust character case if needed - if (btypeIn == ANTIPARALLEL) - ladderChar = toupper( ladderChar ); - mprintf("\t\tResi strand %c, resj strand %c, ladder char %c\n", resiLadderChar, resjLadderChar, ladderChar); - Resi.SetBridge( idx2, ladderChar ); - Resj.SetBridge( idx1, ladderChar ); -} -*/ - -// TODO use Num()? -static inline int AbsResDelta(int idx1, int idx2) { - int resDelta = idx1 - idx2; - if (resDelta < 0) resDelta = -resDelta; - return resDelta; -} - -static inline void SetMin(int& resGapSize, int& sres0, int& sres1, int Nres, int Pres) -{ - int itmp = AbsResDelta(Nres, Pres); - if (itmp < resGapSize) { - resGapSize = itmp; - sres0 = std::min(Pres, Nres); - sres1 = std::max(Pres, Nres); - } -} - - -int Action_DSSP2::OverHbonds(int frameNum, ActionFrame& frm) -{ - t_total_.Start(); - t_calchb_.Start(); - // ----- Determine hydrogen bonding ------------ - int resi; - int Nres = (int)Residues_.size(); -#ifdef _OPENMP - int mythread; -#pragma omp parallel private(resi, mythread) -{ - mythread = omp_get_thread_num(); - CO_NH_bondsArray_[mythread].clear(); -#pragma omp for -#else /* _OPENMP */ - CO_NH_bonds_.clear(); -#endif /* _OPENMP */ - for (resi = 0; resi < Nres; resi++) - { - Residues_[resi].Unassign(); - if (Residues_[resi].IsSelected()) - { - SSres& ResCO = Residues_[resi]; - if (ResCO.HasCO()) - { - const double* Cxyz = frm.Frm().CRD( ResCO.C() ); - const double* Oxyz = frm.Frm().CRD( ResCO.O() ); - for (int resj = 0; resj < Nres; resj++) - { - // Need to consider adjacent residues since delta (2 res) turns possible - if (resi != resj) { - SSres& ResNH = Residues_[resj]; - if (ResNH.IsSelected() && ResNH.HasNH()) - { - const double* Nxyz = frm.Frm().CRD( ResNH.N() ); - const double* Hxyz = frm.Frm().CRD( ResNH.H() ); - - double rON = 1.0/sqrt(DIST2_NoImage(Oxyz, Nxyz)); - double rCH = 1.0/sqrt(DIST2_NoImage(Cxyz, Hxyz)); - double rOH = 1.0/sqrt(DIST2_NoImage(Oxyz, Hxyz)); - double rCN = 1.0/sqrt(DIST2_NoImage(Cxyz, Nxyz)); - - double E = DSSP_fac_ * (rON + rCH - rOH - rCN); - if (E < DSSP_cut_) { -# ifdef DSSPDEBUG - mprintf("DBG: %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -# endif -# ifdef _OPENMP - CO_NH_bondsArray_[mythread].insert( HbondPairType(resi, resj) ); -# else - CO_NH_bonds_.insert( HbondPairType(resi, resj) ); -# endif - } -//# ifdef DSSPDEBUG -// else if (resDelta < 6) -// mprintf("DBG: No hbond %i-CO --> %i-NH E= %g\n", resi+1, resj+1, E); -//# endif - } // END ResNH selected - } // END residues spaced > 2 apart - } // END inner loop over residues - } // END has CO - } // END ResCO selected - } // END outer loop over residues -#ifdef _OPENMP -} // END pragma omp parallel - for (unsigned int thread = 1; thread < CO_NH_bondsArray_.size(); thread++) - for (HbondMapType::const_iterator hb = CO_NH_bondsArray_[thread].begin(); - hb != CO_NH_bondsArray_[thread].end(); ++hb) - CO_NH_bondsArray_[0].insert( *hb ); - HbondMapType const& CO_NH_bonds = CO_NH_bondsArray_[0]; -#endif - t_calchb_.Stop(); - - t_assign_.Start(); - // ----- Do basic assignment ------------------- - for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) - { - int riidx = hb0->first; - int rjidx = hb0->second; - SSres const& Resi = Residues_[riidx]; - SSres const& Resj = Residues_[rjidx]; -# ifdef DSSPDEBUG - mprintf("Res %8i %c -- %8i %c", Resi.Num()+1, Resi.ResChar(), - Resj.Num()+1, Resj.ResChar()); // DBG -# endif - // Spacing between residues i and j - int resDelta = Resj.Num() - Resi.Num(); -# ifdef DSSPDEBUG - mprintf("(%4i)\n", resDelta); -# endif - // Check for H bond from CO i to NH i+n - if (resDelta == 3) { - // 3-TURN - Residues_[riidx ].SetTurnBegin(T3); - Residues_[riidx+1].SetTurn(T3); - Residues_[riidx+2].SetTurn(T3); - Residues_[riidx+3].SetTurnEnd(T3); - Residues_[riidx+1].SetSS( TURN ); - Residues_[riidx+2].SetSS( TURN ); - } else if (resDelta == 4) { - // 4-TURN - Residues_[riidx ].SetTurnBegin(T4); - Residues_[riidx+1].SetTurn(T4); - Residues_[riidx+2].SetTurn(T4); - Residues_[riidx+3].SetTurn(T4); - Residues_[riidx+4].SetTurnEnd(T4); - Residues_[riidx+1].SetSS( TURN ); - Residues_[riidx+2].SetSS( TURN ); - Residues_[riidx+3].SetSS( TURN ); - } else if (resDelta == 5) { - // 5-TURN - Residues_[riidx ].SetTurnBegin(T5); - Residues_[riidx+1].SetTurn(T5); - Residues_[riidx+2].SetTurn(T5); - Residues_[riidx+3].SetTurn(T5); - Residues_[riidx+4].SetTurn(T5); - Residues_[riidx+5].SetTurnEnd(T5); - Residues_[riidx+1].SetSS( TURN ); - Residues_[riidx+2].SetSS( TURN ); - Residues_[riidx+3].SetSS( TURN ); - Residues_[riidx+4].SetSS( TURN ); - } - // Look for bridge. Start with the premise that this bond is part of one - // of the 4 potential bridge patterns, then check if the compliment exists. - HbondMapType::iterator hb; - // Here we want absolute value of spacing. - if (resDelta < 0) resDelta = -resDelta; - if (resDelta > 2) { - // Assume (i,j). Look for (j,i) - hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); - if (hb != CO_NH_bonds.end()) { -# ifdef DSSPDEBUG - mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); -# endif - AssignBridge(hb0->first, hb0->second, ANTIPARALLEL); - } - } - resDelta = AbsResDelta(hb0->first+1, hb0->second-1); - if (resDelta > 2) { - // Assume (i-1, j+1). Look for (j-1, i+1) - hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); - if (hb != CO_NH_bonds.end()) { -# ifdef DSSPDEBUG - mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); -# endif - AssignBridge(hb0->first+1, hb0->second-1, ANTIPARALLEL); - } - } - resDelta = AbsResDelta(hb0->first+1, hb0->second); - if (resDelta > 2) { - // Assume (i-1, j). Check for (j, i+1) PARALLEL - hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); - if (hb != CO_NH_bonds.end()) { -# ifdef DSSPDEBUG - mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); -# endif - AssignBridge(hb0->first+1, hb0->second, PARALLEL); - } - } - resDelta = AbsResDelta(hb0->second-1, hb0->first); - if (resDelta > 2) { - // Assume (j, i+1). Check for (i-1, j) - hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); - if (hb != CO_NH_bonds.end()) { -# ifdef DSSPDEBUG - mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); -# endif - AssignBridge(hb0->second-1, hb0->first, PARALLEL); - } - } - } // END loop over Hbonds - - // ----- Do SS assignment ---------------------- - // Priority is 'H', 'B', 'E', 'G', 'I', 'T', 'S' None - // 8 7 6 5 4 3 2 1 - for (resi = 0; resi < Nres; resi++) - { -# ifdef DSSPDEBUG - mprintf("Residue %i\n", resi+1); -# endif - SSres& Resi = Residues_[resi]; - int prevRes = resi - 1; - int nextRes = resi + 1; - int priority = Resi.SSpriority(); - if ( Resi.HasTurnStart(T4) && prevRes > -1 && Residues_[prevRes].HasTurnStart(T4) ) - { - // Alpha helix. -# ifdef DSSPDEBUG - mprintf("ALPHA helix starting at %i\n", resi+1); -# endif - Residues_[resi ].SetSS( ALPHA ); - Residues_[resi+1].SetSS( ALPHA ); - Residues_[resi+2].SetSS( ALPHA ); - Residues_[resi+3].SetSS( ALPHA ); - } else if (Resi.SS() != ALPHA) { - if (priority < 6) { - // Priority < 6 means not alpha or beta assigned yet. - // Check for Beta structure - bool prevHasBridge = (prevRes > -1 && Residues_[prevRes].HasBridge()); - bool nextHasBridge = (nextRes < Nres && Residues_[nextRes].HasBridge()); - if (Resi.HasBridge()) { - // Regular Beta. Check if previous is assigned EXTENDED in case it - // was assigned via a Beta bulge. - if ( prevHasBridge || nextHasBridge || Residues_[prevRes].SS() == EXTENDED ) - { -# ifdef DSSPDEBUG - mprintf("Extended BETA bridge at %i\n", resi+1); -# endif - Resi.SetSS( EXTENDED ); - } else { -# ifdef DSSPDEBUG - mprintf("Isolated BETA bridge at %i.\n", resi+1); -# endif - Resi.SetSS( BRIDGE ); - } - } else if (prevHasBridge && nextHasBridge) { - // Potential Beta bulge. Check other strand. - int presb1 = Residues_[prevRes].Bridge1Idx(); - int presb2 = Residues_[prevRes].Bridge2Idx(); - int nresb1 = Residues_[nextRes].Bridge1Idx(); - int nresb2 = Residues_[nextRes].Bridge2Idx(); - int prest1 = Residues_[prevRes].Bridge1Type(); - int prest2 = Residues_[prevRes].Bridge2Type(); - int nrest1 = Residues_[nextRes].Bridge1Type(); - int nrest2 = Residues_[nextRes].Bridge2Type(); -# ifdef DSSPDEBUG - mprintf("Potential bulge? Prev res bridge res: %i %i Next res bridge res: %i %i\n", presb1, presb2, nresb1, nresb2); -# endif - // The largest allowed gap in the other strand is 5 residues. - // Since we know that next res and previous res both have at - // least 1 bridge, Next B1 - Prev B1 can be the gap to beat. - // Need to also make sure the bridge types match. - int resGapSize = -1; - int sres0 = -1; - int sres1 = -1; - if (nrest1 == prest1) { - resGapSize = AbsResDelta(nresb1, presb1); - sres0 = std::min(presb1, nresb1); - sres1 = std::max(presb1, nresb1); - } - if (presb2 != -1 && nrest1 == prest2) - SetMin( resGapSize, sres0, sres1, nresb1, presb2 ); - if (nresb2 != -1) { - if (nrest2 == prest1) - SetMin( resGapSize, sres0, sres1, nresb2, presb1 ); - if (presb2 != -1 && nrest2 == prest2) - SetMin( resGapSize, sres0, sres1, nresb2, presb2 ); - } -# ifdef DSSPDEBUG - mprintf("Min res gap size on other strand = %i (%i to %i)\n", resGapSize, sres0, sres1); -# endif - // Minimum allowed gap is 4 residues in between, so 5 residues total. - if (resGapSize > -1 && resGapSize < 6) { -# ifdef DSSPDEBUG - mprintf("Beta bulge.\n"); -# endif - if (Residues_[prevRes].SS() != ALPHA) - Residues_[prevRes].SetSS( EXTENDED ); - Resi.SetSS( EXTENDED ); - if (Residues_[nextRes].SS() != ALPHA) - Residues_[nextRes].SetSS( EXTENDED ); - // Set extended on other strand as well - for (int sres = sres0; sres != sres1; sres++) - if (Residues_[sres].SS() != ALPHA) - Residues_[sres].SetSS( EXTENDED ); - } - } - } // END check for Beta structure - } // END not alpha - } // END loop over residues - - // Check for 3-10 helices. Do this separately so we dont assign regions - // that are too small because other residues have already been assigned. - for (resi = 1; resi < Nres-2; resi++) { - if (Residues_[resi ].SSpriority() < 6 && - Residues_[resi+1].SSpriority() < 6 && - Residues_[resi+2].SSpriority() < 6 && - Residues_[resi ].HasTurnStart(T3) && - Residues_[resi-1].HasTurnStart(T3)) - { - // 3-10 helix -# ifdef DSSPDEBUG - mprintf("3-10 helix starting at %i\n", resi+1); -# endif - Residues_[resi ].SetSS( H3_10 ); - Residues_[resi+1].SetSS( H3_10 ); - Residues_[resi+2].SetSS( H3_10 ); - } - } - // Check for PI helices, similar to 3-10 helices. - for (resi = 1; resi < Nres-4; resi++) { - if (Residues_[resi ].SSpriority() < 5 && - Residues_[resi+1].SSpriority() < 5 && - Residues_[resi+2].SSpriority() < 5 && - Residues_[resi+3].SSpriority() < 5 && - Residues_[resi+4].SSpriority() < 5 && - Residues_[resi ].HasTurnStart(T5) && - Residues_[resi-1].HasTurnStart(T5)) - { - // PI helix -# ifdef DSSPDEBUG - mprintf("PI helix starting at %i\n", resi+1); -# endif - Residues_[resi ].SetSS( HPI ); - Residues_[resi+1].SetSS( HPI ); - Residues_[resi+2].SetSS( HPI ); - Residues_[resi+3].SetSS( HPI ); - Residues_[resi+4].SetSS( HPI ); - } - } - // Check for bends. Only do if no other assignment. - for (resi = 0; resi < Nres; resi++) { - if (Residues_[resi].IsSelected() && Residues_[resi].SS() == NONE) - { - int im2 = resi - 2; - if (im2 > -1) { - int ip2 = resi + 2; - if (ip2 < Nres) { - SSres& Resi = Residues_[resi]; - if (Residues_[im2].CA() != -1 && Resi.CA() != -1 && Residues_[ip2].CA() != -1) { - const double* CAm2 = frm.Frm().CRD(Residues_[im2].CA()); - const double* CA0 = frm.Frm().CRD(Resi.CA()); - const double* CAp2 = frm.Frm().CRD(Residues_[ip2].CA()); - Vec3 CA1( CA0[0]-CAm2[0], CA0[1]-CAm2[1], CA0[2]-CAm2[2] ); - Vec3 CA2( CAp2[0]-CA0[0], CAp2[1]-CA0[1], CAp2[2]-CA0[2] ); - CA1.Normalize(); - CA2.Normalize(); - double bAngle = CA1.Angle(CA2); -# ifdef DSSPDEBUG - mprintf("DEBUG: Bend calc %i-%i-%i: %g deg.\n", resi-1, resi+1, resi+3, bAngle*Constants::RADDEG); -# endif - // 1.221730476 rad = 70 degrees - if (bAngle > 1.221730476) { - Resi.SetSS( BEND ); - } - } - } - } - } // END selected and no assignment - } - - // ----- Store data for each res. Get stats ---- - int totalSS[NSSTYPE_]; - std::fill( totalSS, totalSS + NSSTYPE_, 0 ); - int Nselected = 0; - for (resi=0; resi < Nres; resi++) { - SSres& Resi = Residues_[resi]; - if (Resi.IsSelected()) { - if (betaDetail_ && (Resi.SS() == EXTENDED || Resi.SS() == BRIDGE)) - { - if (Resi.Bridge1Type() == ANTIPARALLEL || - Resi.Bridge2Type() == ANTIPARALLEL) - totalSS[BRIDGE]++; - else if (Resi.Bridge1Type() == PARALLEL || - Resi.Bridge2Type() == PARALLEL) - totalSS[EXTENDED]++; - } else - totalSS[Residues_[resi].SS()]++; - Residues_[resi].AccumulateData(frameNum, printString_, betaDetail_); - Nselected++; - } - } - for (int i = 0; i < NSSTYPE_; i++) { - float fvar = (float)totalSS[i]; - fvar /= (float)Nselected; - totalDS_[i]->Add(frameNum, &fvar); - } - - - t_assign_.Stop(); - t_total_.Stop(); - return 0; -} - -Action::RetType Action_DSSP2::DoAction(int frameNum, ActionFrame& frm) -{ - OverHbonds(frameNum, frm); - Nframes_++; - // DEBUG - Print basic assignment - if (debug_ > 1) { - for (SSarrayType::const_iterator it = Residues_.begin(); it != Residues_.end(); ++it) - it->PrintSSchar(); - } - return Action::OK; -} - -#ifdef MPI -int Action_DSSP2::SyncAction() { - // Consolidate SScount data to master. - for (SSarrayType::iterator res = Residues_.begin(); res != Residues_.end(); ++res) - res->SyncToMaster( Init_.TrajComm() ); - - // Calc total number of frames. - int total_frames = 0; - Init_.TrajComm().ReduceMaster( &total_frames, &Nframes_, 1, MPI_INT, MPI_SUM ); - if (Init_.TrajComm().Master()) - Nframes_ = total_frames; - return 0; -} -#endif - -// Action_DSSP::Print() -void Action_DSSP2::Print() { - if (dsetname_.empty()) return; - t_total_.WriteTiming(1,"DSSP total"); - t_calchb_.WriteTiming(2, "Calc Hbonds", t_total_.Total()); - t_assign_.WriteTiming(2, "Assignment ", t_total_.Total()); - - // Try not to print empty residues. Find the minimum and maximum residue - // for which there is data. Output res nums start from 1. - int min_res = -1; - int max_res = -1; - for (int resi = 0; resi != (int)Residues_.size(); resi++) { - if (Residues_[resi].Dset() != 0) { - if (min_res < 0) min_res = resi; - if (resi > max_res) max_res = resi; - } - } - if (min_res < 0 || max_res < min_res) { - mprinterr("Error: No residues have SS data.\n"); - return; - } - // Calculate average of each SS type across all residues. - if (dsspFile_ != 0) { - std::vector dsspData_(NSSTYPE_); - Dimension Xdim( min_res + 1, 1, "Residue" ); - MetaData md(dsetname_, "avgss", MetaData::NOT_TS); - // Set up a dataset for each SS type. TODO: NONE type? - for (int ss = 1; ss < NSSTYPE_; ss++) { - md.SetIdx(ss); - md.SetLegend( SSname_[ss] ); - dsspData_[ss] = Init_.DSL().AddSet(DataSet::DOUBLE, md); - dsspData_[ss]->SetDim(Dimension::X, Xdim); - dsspFile_->AddDataSet( dsspData_[ss] ); - } - - // Calc the avg SS type for each residue that has data. - int idx = 0; - unsigned int norm = Nframes_; - for (int resi = min_res; resi < max_res+1; resi++) { - SSres& Resi = Residues_[resi]; - if (Resi.Dset() != 0) { - //int Nframe = 0; - //for (int ss = 0; ss < NSSTYPE_; ss++) - // Nframe += Resi.SScount((SStype)ss); - //mprintf("DEBUG: Total frames for residue %i is %i\n", Resi.Num()+1, Nframe); - for (int ss = 1; ss < NSSTYPE_; ss++) { - double avg; - if (betaDetail_ && (SStype)ss == EXTENDED) - avg = (double)Resi.Bcount(PARALLEL); - else if (betaDetail_ && (SStype)ss == BRIDGE) - avg = (double)Resi.Bcount(ANTIPARALLEL); - else - avg = (double)Resi.SScount((SStype)ss); - //mprintf("DEBUG:\t\tCount for type %i is %f\n", ss, avg); - avg /= (double)norm; - dsspData_[ss]->Add(idx, &avg); - } - ++idx; - } - } - } - // Print out SS assignment like PDB - if (assignout_ != 0) { - int total = 0; - int startRes = -1; - std::string resLine, ssLine; - for (int resi = min_res; resi < max_res+1; resi++) { - if (startRes == -1) startRes = resi; - // Convert residue name. - SSres& Resi = Residues_[resi]; - resLine += Resi.ResChar(); - // Figure out which SS element is dominant for res if selected - if (Resi.Dset() != 0) { - int dominantType = 0; - int ssmax = 0; - for (int ss = 0; ss < NSSTYPE_; ss++) { - int sscount; - if (betaDetail_ && (SStype)ss == EXTENDED) - sscount = Resi.Bcount(PARALLEL); - else if (betaDetail_ && (SStype)ss == BRIDGE) - sscount = Resi.Bcount(ANTIPARALLEL); - else - sscount = Resi.SScount((SStype)ss); - if ( sscount > ssmax ) { - ssmax = sscount; - dominantType = ss; - } - } - ssLine += DSSP_char_[dominantType]; - } else - ssLine += '-'; - total++; - if ((total % 50) == 0 || resi == max_res) { - assignout_->Printf("%-8i %s\n", startRes+1, resLine.c_str()); - assignout_->Printf("%8s %s\n\n", " ", ssLine.c_str()); - startRes = -1; - resLine.clear(); - ssLine.clear(); - } else if ((total % 10) == 0) { - resLine += ' '; - ssLine += ' '; - } - } - } -} diff --git a/src/Action_DSSP2.h b/src/Action_DSSP2.h deleted file mode 100644 index c02fd6896d..0000000000 --- a/src/Action_DSSP2.h +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef INC_ACTION_DSSP2_H -#define INC_ACTION_DSSP2_H -#include -#include -#include -#include "Action.h" -#include "NameType.h" -#include "CharMask.h" -#include "Timer.h" -class DataSet; -/// -/** Based on protein secondary structure definitions given in: - * Kabsch, W.; Sander, C.; \"Dictionary of Protein Secondary Structure: - * Pattern Recognition of Hydrogen-Bonded and Geometrical Features. - * Biopolymers (1983), V.22, pp.2577-2637. - * The various secondary structure types that can be assigned (in order - * of decreasing priority) are: - * H - Alpha helix (4) - * B - Single bridge (ladder of length 1) - * E - Beta strand (ladder length > 1) - * G - 3-10 helix (3) - * I - Pi helix (5) - * T - Turn (3, 4, 5) - * S - Bend (Angle i-2, i, i+2 > 70 deg.) - */ -class Action_DSSP2 : public Action { - public: - Action_DSSP2(); - DispatchObject* Alloc() const { return (DispatchObject*)new Action_DSSP2(); } - void Help() const; - private: - Action::RetType Init(ArgList&, ActionInit&, int); - Action::RetType Setup(ActionSetup&); - Action::RetType DoAction(int, ActionFrame&); -# ifdef MPI - int SyncAction(); -# endif - void Print(); - - /// Class that will hold SS info for each residue - class SSres; - /// Secondary structure types - enum SStype { NONE=0, EXTENDED, BRIDGE, H3_10, ALPHA, HPI, TURN, BEND }; - static const int NSSTYPE_ = 8; ///< # of secondary structure types. - static const char DSSP_char_[]; ///< DSSP 1 char names corresponding to SStype - static const char* DSSP_name_[]; ///< Full secondary structure names corresponding to SStype - static const char* SSchar_[]; ///< PTRAJ 1 character names corresponding to SStype - /// Turn types - enum TurnType { T3 = 0, T4, T5 }; - static const int NTURNTYPE_ = 3; - /// Beta types - enum BetaType { B1 = 0, B2, S }; - static const int NBETATYPE_ = 3; - /// Bridge direction types - enum BridgeType { NO_BRIDGE = 0, PARALLEL, ANTIPARALLEL }; - static const int NBRIDGETYPE_ = 3; - - static const double DSSP_fac_; ///< Original DSSP factor for calc. H-bond "energy" - static const double DSSP_cut_; ///< Original DSSP H-bond energy cutoff in kcal/mol - - typedef std::vector SSarrayType; - typedef std::vector Sarray; - typedef std::pair HbondPairType; - typedef std::set HbondMapType; - - int OverHbonds(int, ActionFrame&); - - void AssignBridge(int, int, BridgeType); - - /// Map resi (CO) to resj (NH) hydrogen bonds -# ifdef _OPENMP - std::vector CO_NH_bondsArray_; -# else - std::vector CO_NH_bonds_; -# endif - int debug_; ///< Action debug level - unsigned int Nframes_; ///< Number of frames processed, for total SS normalization - SSarrayType Residues_; ///< Hold SS data for all residues. - Sarray SSname_; ///< Hold full secondary structure names - NameType BB_N_; ///< Protein N atom name ('N') - NameType BB_H_; ///< Protein N-H atom name ('H') - NameType BB_C_; ///< Protein C atom name ('C') - NameType BB_O_; ///< Protein C-O atom name ('O') - NameType BB_CA_; ///< Protein alpha C name ('CA') - CharMask Mask_; ///< Mask used to determine selected residues. - DataFile* outfile_; ///< Output Data file - DataFile* dsspFile_; ///< Sum output file - CpptrajFile* assignout_; ///< Assignment output file. - std::string dsetname_; ///< DSSP data set name - DataSet* totalDS_[NSSTYPE_]; ///< Hold total SS each frame for each SS type - ActionInit Init_; ///< Hold pointers to master DSL/DFL - bool printString_; ///< If true print 1 char per residue indicating ss type - bool betaDetail_; ///< If true use para/anti in place of extended/bridge - Timer t_total_; - Timer t_calchb_; - Timer t_assign_; -}; - -// ============================================================================= -class Action_DSSP2::SSres { - public: - SSres(); - SSres(SSres const&); - SSres& operator=(SSres const&); - int SScount(SStype t) const { return SScount_[t]; } - int Bcount(BridgeType t) const { return Bcount_[t]; } - SStype SS() const { return sstype_; } - int Num() const { return num_; } - char ResChar() const { return resChar_; } - bool IsSelected() const { return isSelected_; } - int C() const { return C_; } - int O() const { return O_; } - int N() const { return N_; } - int H() const { return H_; } - int CA() const { return CA_; } - int PrevIdx() const { return prevIdx_; } - int NextIdx() const { return nextIdx_; } - int Bridge1Idx() const { return bridge1idx_; } - BridgeType Bridge1Type() const { return b1type_;} - int Bridge2Idx() const { return bridge2idx_; } - BridgeType Bridge2Type() const { return b2type_;} - DataSet* Dset() const { return resDataSet_; } - - bool IsMissingAtoms() const { return (C_==-1 || O_==-1 || N_==-1 || H_==-1 || CA_==-1); } - bool HasCO() const { return (C_!=-1 && O_!=-1); } - bool HasNH() const { return (N_!=-1 && H_!=-1); } - bool HasCA() const { return (CA_!=-1); } - - bool HasTurnStart(TurnType) const; - bool HasBridge() const; - bool IsBridgedWith(int) const; -// char StrandChar() const; - void PrintSSchar() const; - - void SetTurnBegin(TurnType); - void SetTurn(TurnType); - void SetTurnEnd(TurnType); - /// Set a bridge between this res and other res index into Residues_ - void SetBridge(int, BridgeType); - - void AccumulateData(int, bool, bool); - - void SetSS(SStype); - void SetNum(int i) { num_ = i; } - void SetResChar(char c) { resChar_ = c; } - void SetSelected(bool b) { isSelected_ = b; } - void SetC(int i) { C_ = i; } - void SetO(int i) { O_ = i; } - void SetN(int i) { N_ = i; } - void SetH(int i) { H_ = i; } - void SetCA(int i) { CA_ = i; } - void SetPrevIdx(int i) { prevIdx_ = i; } - void SetNextIdx(int i) { nextIdx_ = i; } - void SetDset(DataSet* d) { resDataSet_ = d; } - /// Deselect this residue and reset coordinate indices. - void Deselect(); - /// Reset hbonds, pattern and SS type assignments - void Unassign(); - /// \return Relative priority of currently assigned SS type - int SSpriority() const; -# ifdef MPI - void SyncToMaster(Parallel::Comm const&); -# endif - private: - /// \return Relative priority of given SS type - static inline int ssPriority(SStype); - - DataSet* resDataSet_; ///< DataSet for SS assignment each frame for this res. - double chirality_; ///< Dihedral CA[i-1, i, i+1, i+2] - double bend_; ///< Angle CA[i-2, i, i+2] - int SScount_[NSSTYPE_]; ///< Hold count for each SS type - int Bcount_[NBRIDGETYPE_]; ///< Hold count for Beta types - SStype sstype_; ///< SS assignment for this frame - int num_; ///< Residue index in Topology - int C_; ///< Coord idx of BB carbon - int O_; ///< Coord idx of BB oxygen - int N_; ///< Coord idx of BB nitrogen - int H_; ///< Coord idx of BB hydrogen - int CA_; ///< Coord idx of BB alpha carbon - int prevIdx_; ///< Index in Residues_ of previous residue - int nextIdx_; ///< Index in Residues_ of next residue - int bridge1idx_; ///< Index in Residues_ of res this is bridged to - BridgeType b1type_; ///< Type of bridge1 - int bridge2idx_; ///< Index in Residues_ of res this is bridged to - BridgeType b2type_; ///< Type of bridge2 - char resChar_; ///< Single char residue ID - char turnChar_[NTURNTYPE_]; ///< Character if part of N turn - bool isSelected_; ///< True if calculating SS for this residue. -}; -#endif diff --git a/src/Command.cpp b/src/Command.cpp index 65a779aa1c..e6bafb2193 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -141,7 +141,6 @@ #include "Action_XtalSymm.h" #include "Action_Time.h" #include "Action_DihedralRMS.h" -#include "Action_DSSP2.h" // ----- ANALYSIS -------------------------------------------------------------- #include "Analysis_Hist.h" #include "Analysis_Corr.h" @@ -305,7 +304,6 @@ void Command::Init() { Command::AddCmd( new Action_DNAionTracker(), Cmd::ACT, 1, "dnaiontracker" ); // HIDDEN Command::AddCmd( new Action_DistRmsd(), Cmd::ACT, 2, "drms", "drmsd" ); Command::AddCmd( new Action_DSSP(), Cmd::ACT, 2, "dssp", "secstruct" ); - Command::AddCmd( new Action_DSSP2(), Cmd::ACT, 1, "dssp2" ); Command::AddCmd( new Action_Energy(), Cmd::ACT, 1, "energy" ); Command::AddCmd( new Action_Esander(), Cmd::ACT, 1, "esander" ); Command::AddCmd( new Action_FilterByData(), Cmd::ACT, 1, "filter" ); diff --git a/src/cpptrajdepend b/src/cpptrajdepend index 1b2d5e045f..cce298300f 100644 --- a/src/cpptrajdepend +++ b/src/cpptrajdepend @@ -21,8 +21,7 @@ Action_Contacts.o : Action_Contacts.cpp Action.h ActionState.h Action_Contacts.h Action_CreateCrd.o : Action_CreateCrd.cpp Action.h ActionState.h Action_CreateCrd.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_CRD.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_CreateReservoir.o : Action_CreateReservoir.cpp Action.h ActionState.h Action_CreateReservoir.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_DNAionTracker.o : Action_DNAionTracker.cpp Action.h ActionState.h Action_DNAionTracker.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h -Action_DSSP.o : Action_DSSP.cpp Action.h ActionState.h Action_DSSP.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h -Action_DSSP2.o : Action_DSSP2.cpp Action.h ActionState.h Action_DSSP2.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h +Action_DSSP.o : Action_DSSP.cpp Action.h ActionState.h Action_DSSP.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Density.o : Action_Density.cpp Action.h ActionState.h Action_Density.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h OnlineVarT.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Diffusion.o : Action_Diffusion.cpp Action.h ActionState.h Action_Diffusion.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h DistRoutines.h FileIO.h FileName.h FileTypes.h Frame.h ImagedAction.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h Vec3.h Action_Dihedral.o : Action_Dihedral.cpp Action.h ActionState.h Action_Dihedral.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Constants.h CoordinateInfo.h CpptrajFile.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_Coords.h DataSet_Coords_REF.h DataSet_double.h Dimension.h DispatchObject.h FileIO.h FileName.h FileTypes.h Frame.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h Residue.h SymbolExporting.h TextFormat.h Timer.h Topology.h TorsionRoutines.h Vec3.h @@ -158,7 +157,7 @@ Cluster_ReadInfo.o : Cluster_ReadInfo.cpp ArgList.h ArrayIterator.h AssociatedDa Cmd.o : Cmd.cpp Cmd.h DispatchObject.h CmdInput.o : CmdInput.cpp CmdInput.h StringRoutines.h CmdList.o : CmdList.cpp Cmd.h CmdList.h DispatchObject.h -Command.o : Command.cpp Action.h ActionFrameCounter.h ActionList.h ActionState.h Action_Align.h Action_Angle.h Action_AreaPerMol.h Action_AtomMap.h Action_AtomicCorr.h Action_AtomicFluct.h Action_AutoImage.h Action_Average.h Action_Bounds.h Action_Box.h Action_Center.h Action_Channel.h Action_CheckChirality.h Action_CheckStructure.h Action_Closest.h Action_ClusterDihedral.h Action_Contacts.h Action_CreateCrd.h Action_CreateReservoir.h Action_DNAionTracker.h Action_DSSP.h Action_DSSP2.h Action_Density.h Action_Diffusion.h Action_Dihedral.h Action_DihedralRMS.h Action_Dipole.h Action_DistRmsd.h Action_Distance.h Action_Energy.h Action_Esander.h Action_FilterByData.h Action_FixAtomOrder.h Action_FixImagedBonds.h Action_GIST.h Action_Grid.h Action_GridFreeEnergy.h Action_HydrogenBond.h Action_Image.h Action_InfraredSpectrum.h Action_Jcoupling.h Action_LESsplit.h Action_LIE.h Action_LipidOrder.h Action_MakeStructure.h Action_Mask.h Action_Matrix.h Action_MinImage.h Action_Molsurf.h Action_MultiDihedral.h Action_MultiVector.h Action_NAstruct.h Action_NMRrst.h Action_NativeContacts.h Action_OrderParameter.h Action_Outtraj.h Action_PairDist.h Action_Pairwise.h Action_Principal.h Action_Projection.h Action_Pucker.h Action_Radgyr.h Action_Radial.h Action_RandomizeIons.h Action_Remap.h Action_ReplicateCell.h Action_Rmsd.h Action_Rotate.h Action_RunningAvg.h Action_STFC_Diffusion.h Action_Scale.h Action_SetVelocity.h Action_Spam.h Action_Strip.h Action_Surf.h Action_SymmetricRmsd.h Action_Temperature.h Action_Time.h Action_Translate.h Action_Unstrip.h Action_Unwrap.h Action_Vector.h Action_VelocityAutoCorr.h Action_Volmap.h Action_Volume.h Action_Watershell.h Action_XtalSymm.h Analysis.h AnalysisList.h AnalysisState.h Analysis_AmdBias.h Analysis_AutoCorr.h Analysis_Average.h Analysis_Clustering.h Analysis_ConstantPHStats.h Analysis_Corr.h Analysis_CrankShaft.h Analysis_CrdFluct.h Analysis_CrossCorr.h Analysis_CurveFit.h Analysis_Divergence.h Analysis_FFT.h Analysis_HausdorffDistance.h Analysis_Hist.h Analysis_IRED.h Analysis_Integrate.h Analysis_KDE.h Analysis_Lifetime.h Analysis_LowestCurve.h Analysis_Matrix.h Analysis_MeltCurve.h Analysis_Modes.h Analysis_MultiHist.h Analysis_Multicurve.h Analysis_Overlap.h Analysis_PhiPsi.h Analysis_Regression.h Analysis_RemLog.h Analysis_Rms2d.h Analysis_RmsAvgCorr.h Analysis_Rotdif.h Analysis_RunningAvg.h Analysis_Spline.h Analysis_State.h Analysis_Statistics.h Analysis_TI.h Analysis_Timecorr.h Analysis_VectorMath.h Analysis_Wavelet.h ArgList.h Array1D.h ArrayIterator.h AssociatedData.h Atom.h AtomExtra.h AtomMap.h AtomMask.h AxisType.h BaseIOtype.h Box.h BufferedLine.h CharMask.h ClusterDist.h ClusterList.h ClusterMap.h ClusterNode.h ClusterSieve.h Cmd.h CmdInput.h CmdList.h Command.h ComplexArray.h Constraints.h Control.h CoordinateInfo.h Corr.h Cph.h CpptrajFile.h CpptrajState.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_2D.h DataSet_3D.h DataSet_Cmatrix.h DataSet_Coords.h DataSet_Coords_CRD.h DataSet_Coords_REF.h DataSet_GridFlt.h DataSet_Mat3x3.h DataSet_MatrixDbl.h DataSet_MatrixFlt.h DataSet_Mesh.h DataSet_Modes.h DataSet_RemLog.h DataSet_Vector.h DataSet_double.h DataSet_float.h DataSet_integer.h DataSet_integer_mem.h DataSet_pH.h DataSet_string.h Deprecated.h DihedralSearch.h Dimension.h DispatchObject.h DistRoutines.h Energy.h Energy_Sander.h EnsembleIn.h EnsembleOutList.h Ewald.h Exec.h Exec_Analyze.h Exec_Calc.h Exec_CatCrd.h Exec_Change.h Exec_ClusterMap.h Exec_CombineCoords.h Exec_Commands.h Exec_CompareTop.h Exec_CrdAction.h Exec_CrdOut.h Exec_CreateSet.h Exec_DataFile.h Exec_DataFilter.h Exec_DataSetCmd.h Exec_GenerateAmberRst.h Exec_Help.h Exec_LoadCrd.h Exec_LoadTraj.h Exec_ParallelAnalysis.h Exec_ParmBox.h Exec_ParmSolvent.h Exec_ParmStrip.h Exec_ParmWrite.h Exec_PermuteDihedrals.h Exec_Precision.h Exec_PrintData.h Exec_ReadData.h Exec_ReadEnsembleData.h Exec_ReadInput.h Exec_RotateDihedral.h Exec_RunAnalysis.h Exec_ScaleDihedralK.h Exec_SequenceAlign.h Exec_SortEnsembleData.h Exec_SplitCoords.h Exec_System.h Exec_Top.h Exec_Traj.h Exec_UpdateParameters.h Exec_ViewRst.h FileIO.h FileName.h FileTypes.h Frame.h FramePtrArray.h Grid.h GridAction.h GridBin.h HistBin.h Hungarian.h ImageTypes.h ImagedAction.h InputTrajCommon.h MapAtom.h MaskArray.h MaskToken.h Matrix.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h OnlineVarT.h OutputTrajCommon.h PDBfile.h PairList.h Parallel.h ParameterHolders.h ParameterTypes.h PubFFT.h RPNcalc.h Random.h Range.h ReferenceAction.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h ReplicaInfo.h Residue.h Spline.h StructureCheck.h SymbolExporting.h SymmetricRmsdCalc.h TextFormat.h Timer.h Topology.h TrajFrameCounter.h TrajectoryFile.h Trajin.h TrajinList.h TrajoutList.h Trajout_Single.h VariableArray.h Vec3.h molsurf.h +Command.o : Command.cpp Action.h ActionFrameCounter.h ActionList.h ActionState.h Action_Align.h Action_Angle.h Action_AreaPerMol.h Action_AtomMap.h Action_AtomicCorr.h Action_AtomicFluct.h Action_AutoImage.h Action_Average.h Action_Bounds.h Action_Box.h Action_Center.h Action_Channel.h Action_CheckChirality.h Action_CheckStructure.h Action_Closest.h Action_ClusterDihedral.h Action_Contacts.h Action_CreateCrd.h Action_CreateReservoir.h Action_DNAionTracker.h Action_DSSP.h Action_Density.h Action_Diffusion.h Action_Dihedral.h Action_DihedralRMS.h Action_Dipole.h Action_DistRmsd.h Action_Distance.h Action_Energy.h Action_Esander.h Action_FilterByData.h Action_FixAtomOrder.h Action_FixImagedBonds.h Action_GIST.h Action_Grid.h Action_GridFreeEnergy.h Action_HydrogenBond.h Action_Image.h Action_InfraredSpectrum.h Action_Jcoupling.h Action_LESsplit.h Action_LIE.h Action_LipidOrder.h Action_MakeStructure.h Action_Mask.h Action_Matrix.h Action_MinImage.h Action_Molsurf.h Action_MultiDihedral.h Action_MultiVector.h Action_NAstruct.h Action_NMRrst.h Action_NativeContacts.h Action_OrderParameter.h Action_Outtraj.h Action_PairDist.h Action_Pairwise.h Action_Principal.h Action_Projection.h Action_Pucker.h Action_Radgyr.h Action_Radial.h Action_RandomizeIons.h Action_Remap.h Action_ReplicateCell.h Action_Rmsd.h Action_Rotate.h Action_RunningAvg.h Action_STFC_Diffusion.h Action_Scale.h Action_SetVelocity.h Action_Spam.h Action_Strip.h Action_Surf.h Action_SymmetricRmsd.h Action_Temperature.h Action_Time.h Action_Translate.h Action_Unstrip.h Action_Unwrap.h Action_Vector.h Action_VelocityAutoCorr.h Action_Volmap.h Action_Volume.h Action_Watershell.h Action_XtalSymm.h Analysis.h AnalysisList.h AnalysisState.h Analysis_AmdBias.h Analysis_AutoCorr.h Analysis_Average.h Analysis_Clustering.h Analysis_ConstantPHStats.h Analysis_Corr.h Analysis_CrankShaft.h Analysis_CrdFluct.h Analysis_CrossCorr.h Analysis_CurveFit.h Analysis_Divergence.h Analysis_FFT.h Analysis_HausdorffDistance.h Analysis_Hist.h Analysis_IRED.h Analysis_Integrate.h Analysis_KDE.h Analysis_Lifetime.h Analysis_LowestCurve.h Analysis_Matrix.h Analysis_MeltCurve.h Analysis_Modes.h Analysis_MultiHist.h Analysis_Multicurve.h Analysis_Overlap.h Analysis_PhiPsi.h Analysis_Regression.h Analysis_RemLog.h Analysis_Rms2d.h Analysis_RmsAvgCorr.h Analysis_Rotdif.h Analysis_RunningAvg.h Analysis_Spline.h Analysis_State.h Analysis_Statistics.h Analysis_TI.h Analysis_Timecorr.h Analysis_VectorMath.h Analysis_Wavelet.h ArgList.h Array1D.h ArrayIterator.h AssociatedData.h Atom.h AtomExtra.h AtomMap.h AtomMask.h AxisType.h BaseIOtype.h Box.h BufferedLine.h CharMask.h ClusterDist.h ClusterList.h ClusterMap.h ClusterNode.h ClusterSieve.h Cmd.h CmdInput.h CmdList.h Command.h ComplexArray.h Constraints.h Control.h CoordinateInfo.h Corr.h Cph.h CpptrajFile.h CpptrajState.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_1D.h DataSet_2D.h DataSet_3D.h DataSet_Cmatrix.h DataSet_Coords.h DataSet_Coords_CRD.h DataSet_Coords_REF.h DataSet_GridFlt.h DataSet_Mat3x3.h DataSet_MatrixDbl.h DataSet_MatrixFlt.h DataSet_Mesh.h DataSet_Modes.h DataSet_RemLog.h DataSet_Vector.h DataSet_double.h DataSet_float.h DataSet_integer.h DataSet_integer_mem.h DataSet_pH.h DataSet_string.h Deprecated.h DihedralSearch.h Dimension.h DispatchObject.h DistRoutines.h Energy.h Energy_Sander.h EnsembleIn.h EnsembleOutList.h Ewald.h Exec.h Exec_Analyze.h Exec_Calc.h Exec_CatCrd.h Exec_Change.h Exec_ClusterMap.h Exec_CombineCoords.h Exec_Commands.h Exec_CompareTop.h Exec_CrdAction.h Exec_CrdOut.h Exec_CreateSet.h Exec_DataFile.h Exec_DataFilter.h Exec_DataSetCmd.h Exec_GenerateAmberRst.h Exec_Help.h Exec_LoadCrd.h Exec_LoadTraj.h Exec_ParallelAnalysis.h Exec_ParmBox.h Exec_ParmSolvent.h Exec_ParmStrip.h Exec_ParmWrite.h Exec_PermuteDihedrals.h Exec_Precision.h Exec_PrintData.h Exec_ReadData.h Exec_ReadEnsembleData.h Exec_ReadInput.h Exec_RotateDihedral.h Exec_RunAnalysis.h Exec_ScaleDihedralK.h Exec_SequenceAlign.h Exec_SortEnsembleData.h Exec_SplitCoords.h Exec_System.h Exec_Top.h Exec_Traj.h Exec_UpdateParameters.h Exec_ViewRst.h FileIO.h FileName.h FileTypes.h Frame.h FramePtrArray.h Grid.h GridAction.h GridBin.h HistBin.h Hungarian.h ImageTypes.h ImagedAction.h InputTrajCommon.h MapAtom.h MaskArray.h MaskToken.h Matrix.h Matrix_3x3.h MetaData.h Molecule.h NameType.h NetcdfFile.h OnlineVarT.h OutputTrajCommon.h PDBfile.h PairList.h Parallel.h ParameterHolders.h ParameterTypes.h PubFFT.h RPNcalc.h Random.h Range.h ReferenceAction.h ReferenceFrame.h RemdReservoirNC.h ReplicaDimArray.h ReplicaInfo.h Residue.h Spline.h StructureCheck.h SymbolExporting.h SymmetricRmsdCalc.h TextFormat.h Timer.h Topology.h TrajFrameCounter.h TrajectoryFile.h Trajin.h TrajinList.h TrajoutList.h Trajout_Single.h VariableArray.h Vec3.h molsurf.h ComplexArray.o : ComplexArray.cpp ArrayIterator.h ComplexArray.h Constraints.o : Constraints.cpp ArgList.h Atom.h AtomExtra.h AtomMask.h Box.h CharMask.h Constants.h Constraints.h CoordinateInfo.h CpptrajStdio.h FileName.h Frame.h MaskToken.h Matrix_3x3.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReplicaDimArray.h Residue.h SymbolExporting.h Topology.h Vec3.h Control.o : Control.cpp Action.h ActionList.h ActionState.h Analysis.h AnalysisList.h AnalysisState.h ArgList.h AssociatedData.h Atom.h AtomExtra.h AtomMask.h BaseIOtype.h Box.h CharMask.h Control.h CoordinateInfo.h CpptrajFile.h CpptrajState.h CpptrajStdio.h DataFile.h DataFileList.h DataIO.h DataSet.h DataSetList.h DataSet_Coords.h DataSet_Coords_REF.h Dimension.h DispatchObject.h EnsembleIn.h EnsembleOutList.h FileIO.h FileName.h FileTypes.h Frame.h FramePtrArray.h InputTrajCommon.h MaskToken.h Matrix_3x3.h MetaData.h Molecule.h NameType.h Parallel.h ParameterTypes.h Range.h ReferenceFrame.h ReplicaDimArray.h ReplicaInfo.h Residue.h StringRoutines.h SymbolExporting.h TextFormat.h Timer.h Topology.h TrajFrameCounter.h Trajin.h TrajinList.h TrajoutList.h VariableArray.h Vec3.h diff --git a/src/cpptrajfiles b/src/cpptrajfiles index 710b0eed09..c02caaa64e 100644 --- a/src/cpptrajfiles +++ b/src/cpptrajfiles @@ -26,7 +26,6 @@ COMMON_SOURCES= \ Action_CreateReservoir.cpp \ Action_DNAionTracker.cpp \ Action_DSSP.cpp \ - Action_DSSP2.cpp \ Action_Density.cpp \ Action_Diffusion.cpp \ Action_Dihedral.cpp \ From a5d91f828e2225dda6d9584e7e8b74ffde590059 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Thu, 29 Aug 2019 15:44:14 -0400 Subject: [PATCH 56/62] DRR - Cpptraj: Add 'betadetail' keyword to tests for backwards compat. --- test/Test_DSSP/RunTest.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Test_DSSP/RunTest.sh b/test/Test_DSSP/RunTest.sh index 4536962531..5e8a1f1fa4 100755 --- a/test/Test_DSSP/RunTest.sh +++ b/test/Test_DSSP/RunTest.sh @@ -18,7 +18,7 @@ if [ $? -eq 0 ] ; then noprogress trajin ../DPDP.nc secstruct out dssp.gnu sumout dssp.sum.agr totalout total.agr \ - assignout DSSP.assign.dat + assignout DSSP.assign.dat betadetail EOF RunCpptraj "Secstruct (DSSP) command test." DoTest dssp.gnu.save dssp.gnu @@ -29,7 +29,7 @@ EOF # Test 2 cat > cpptraj.in < cpptraj.in < cpptraj.in < Date: Fri, 30 Aug 2019 14:56:36 -0400 Subject: [PATCH 57/62] DRR - Cpptraj: Update manual. --- doc/CpptrajManual.pdf | Bin 802203 -> 803493 bytes doc/cpptraj.lyx | 48 ++++++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/doc/CpptrajManual.pdf b/doc/CpptrajManual.pdf index 25bd9be9402d0cc1ae5450f07bbe8a60f62662d0..f4a3bc090ca708ae9c9e06f1ee0d29c167b9afc6 100644 GIT binary patch delta 138139 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%0Dm}G=E7N+ z!W71k1d{1tN2-+`$C~s)XI(dxvJkZ`PMA6ot`f=$f)Reo%07aYsK}Y&*xg~PQ*5aASLBL5TB(l8MmvcJmBmW4T#V-E!Pay^jF3e0b#ru+1xRv)nCmV zW^qdPupqUQ)uuGl>wgA%6DqYch@(Uu^|bHTTu5ju1nNCHI2IiorOl?_nM%Z%kDl+b zdZza`xXncOIjtFYE=QVFk?Y-S68H%$s~!cQlCeW2Ey9w9EmV=ChlOOkY9edw;5@bD zh^2!vXoE+@U|De#Jw0WPaaG2c1Knj=4bDXQHmAIV5M>W(5`T@gNHXmR0Y9{1m{cH) z{<1d=6yA&NI(08H4vfgi&xq_zvmCV|>@TL@VU;{?&sXSl5mbzd|MyWUp_;%|sHYtx z>@-qj4vomQ)ccYwCR$Djd?^Gp zdnjJaTgTC8YkwHjs7jt6HU^kBj;&?^;2lx_pzkF)3-Pk^>$)sCnQ%=2l*t&a9ra~? zDtQh06fME|lp?q0pv$b+U@A?iQ~h z+5mU6dD?X33*7fzKIPqE+**&XzL#W(6Xze_s+nkXv45tkcB3g)APt>Z&Lz+iZtRVO zxKc;J-S(Oqot-W%Vsg;C}e=5`L%oEo90XhV9gRFb*f@@n&%VJ>yUE6mjr2Zz0V z7`d){^BQoq)@3nzGP&O4I}=UAg{$vwCNCu7;nBkitcw0Hr=-v0j_pdio4cr!GohRw;FkxW2Y6O2AU56)iE)=e(KnH%} z#`c|01j%H{o8`0r0qR(6FqdIc1r(PJl?4?8I5#twak~a8e@k!MMi9Q|SLh}Qi8kB^ zUyTu*1Re))aOvNyJ5!>-sSn-{gMT4T{1G#Cx=`l=U2Q7+RwX|E?a-d^IyOvabE z8PjHo>#pRYOxI~n>yDgZXcQ=y5kn&xn_XQ(5Cb#$0x0eF^&l*vC$yA54GUNZ!oUo_ zTRUDXf1;xh)c=L9^;Bssq-UC2Rei`<)hEyQ&H&{QmXJ5vi~4cCj~@T%?)haa=lNwT z>iOk&&>;oH98^u_o0NmTEA0&dQCH^xO_s~FPK!Lr=>7^LA}5evb(*gkxK*2}y(~@g zCC7}F0odo0X1h!3^ty=#WH;*AaMEmXldSW!f8H6Ym)n>WzG+_e`e{wmll}aq4dj8D82p^K-4nS{A1ytZ~nbW zwPkiM2c0L0ei(RRpJQi>0BZo+Pxr!B=b#?R})Ry`?Ss(`ra-f7VM) zn;b$}1qvZaEXEZlzH&_&M1HZ0*||0ViVv@Frs9IuLCCo2Aet1hLlGIbfvT_ z7+CTJ<`eE+Y2d94a;&7YeP#KVN!8pdSQzS}j;=cKR)K_$-u7xtfHSD5P(-SS`0XZj zA>NSiEw1+!_W%{ez?rh|_R$%we<%Aqg+k_5ZHxhg-5WEhQ4)^8c)6n64nTMwk*oOf z2IoI~wQw^*eNy{S^ngDFD$@ifgg3l^2V-`eO6PR7_jg| zP0iENwwrrK1HYBnr0~tc=J?J*iEq$nJO>-9{s}fVK5B=wt`o{fvl^>-e=0Zws;`CL zUV|x|x*be-u`m1mn>Att&uTO<@q42~I%Eb08lamTdnliFig@MRYk+*6y485X?ToN$nEc{WIpmcr<;UlarA zDB&B~frk>fK>|CKoSf4GfBwrb{GY)HVeU6UAB<&6AF4$*h==wC(9j7U$}nN#!U=*P zqab(S=oc5==;Iz^jZcbx_oNlZ5yEA~EP|Ap;@7KXu|FuAKxjXBxNcnFR4;H)H!={O zJ{c1osTpNB#n29-16W|0^Ctt|z)uEuuqYg^aG$%2BQLyyIe<5kf8DMuc4eB>EFH+S zu3jLIh9IY;LlVp_hHPlt9Byx&^epSQC@%P=#c<^fZKlk>bwkp9Lg>EJM4EJ~RpZVLu@;TOc1XTZZ#7JpPTt`KZHyJ%B|w z>u~Sd4?HRBbQn4Se^<0@LILm3rj>+gUPtmL)2dUCGe6Ya7D0B~{b`Gt4Q8Y+QJSs` zF)`7}OrKUK@HGEEAVAo$JmI~dH@y|L=0_t?l;ti<@?nAo^Mck<8tqhQ+N5(^AEz}u z?RHGub4{20%$F&4cz%f`;+~ETLi)oYvq@FYs&9M9M^?o{e?rBL+R1Iq2G2s6Z%w}< z>P=bf*D&oU&6%RK-;#~~0r)XUQ2Ghj8yG0(jg}7fsENrkr6gtT60ZBqXJ<0BSHmpt^LLdDyxy(1^Jo77v|!LFmtj%` z6qgj81r!4^F*1`8@+yBAU60$i@jbu7k2ZSNiIgbGhafTw=?bP^WC3EO5cBf5bA=AN}`Qaa1!o+ zidFnK2Sk!&S!@3buF_>=R$^(kW(&uHK>z4mYY4CV$_r#Di@-48xZM>3NC)Q*k&Xn> zo=`PyAMnVOKs&mUd7}E7g2!+m{IVG*S&A2c2mk$E5h zih|SfKm&joISl~1bG0XsHx&pN6orE?DDa8aZ_#Gr9igGNt@*XLt?_hBx>YVHUanEd z#X89%g{~9PMsd{I&Ki#XF*x=CGrnCo*1}}e-<$dt~=Udh&WR%NmV^THuD(Jp$>y%z+D3Qik6D`RWQ2`T4G}Q}HVPY0#blM> z<}0MZ({#+6Gn@GV{A?bfuc)jCTexw&$|r&z`$u`Dhnj>K3Wk24x6P-t^5C=wDo zO|mihzXJhO&SI-%`Bk@D5*s!gOt9*(3Msj4T?c;-HDW05pF0M(;OrA zIRi)^k;z*wDWWVd1_am62`Qr_8N=}Of1vU70*z5o-LgHe8_0L$ADPK01qS4yj4pxq zpdf!FnP8v80?mHpI7_GH2~IhpU^RR_7P&2$SOL?!fl6z3;K>HlogpNZv(&_=G)Cr= z9uIqR&~=r7?Dy0HshXyt(f})N_rT+M1I`>pOYtx#(G^7i0Ozjc3W z;-i+|!krVHfsoK%d5sD<(nLauLS3{!%?15~_5Ikyv7(81`LRJmLbVHglXxm1fLTB| z@unltSM3Qx1EawLfN&_zrqy8%!&7SF07pxhGabxmSJ&7pE`{WJQfj28@_{&QdLO95 ziu#6B1=CmgDDs6%0QyUSo*q{;X3&2fi~?_lpo$1|8QSK5+$embR6r#)trHoL##fgL_ic!JDQk~pAK)l}2&413qweMloD5t@2N6X<`8%z;{( za}CA?)gAP6M`w~pzzNpC1J~z=NC6i8`F9^F1H;*eRgo?SI_9YBLxnv7lu~gPEQ?rW zYr!F};P*CrwL;?id(Br0~4WfxMzVF<+bZu)Ye#<1A4MkTBrKxq^KR zUz(Ya@+gkQGcoA-9dCakm0xGk7+xaD zkb#v0q9;u`<^rt{;P-Ds@5N=KYxC~&0LB4l+(D0g2%aJ`12}(-9=LkDvz3|L-`X9G z4-=Gk;pd4(0RzuQO`G$5t&bHJ1VV0s7~c0I#m;x;z{eO4xV>Q4LWmE$CU9=+&o`&W zQHmVK#+;=fbKvhOtj?6e#L_wb5;s0^{xH^=iA8Tuu1o@E^(fh>K?b({4c01!k#S`G z9y@WEKWHonWz2ukH-s_TgF>0g9=VZ|0uo$`t68C^7=cn10w$CzyoyYIe*fc{%}~F1 z_uqcWgXiu>6j%C&=$d(#+_XTJMdJL@{-?jzE$nQwqhe9(vyel>;us4cddb0?GLj;j z+TsAs(li}R3<8vgMT|V9BCx z6-I$Hu8S6ETA)UHBzd4EYL|!(L!!L4+y3{?a7e8rZ?At^PA!No889WeD}&< zeZRSVb$JuAP44AMkZf+N4U01`N#jitr(O`oo7-~p-g)1RYU_qs=v;m9!>AT5Z^YLh zw;ykRm{?uj1lf#F&OCoVN?}Jc__dcxY~47Bop-O_(4ZRILdkCHGN;9r$Wa~bTvfOz zjTmnwW}&WYQmH3$1Z+Qv*_ZU2uY1BVi=g!ZA7|YIGTOq|(V;Iu&UeI8k zF<}-trr-f(yE7ETpzBN70-a^hngeRDVUH%my`~j~bWdrE1)2n7<8?UT{qa zp_P9XetjbY78?oUD-8Zm{OU|sDRh5=wkQ@mx+rp}mf+})$Y|0FO0y2gdmUFi zhU!#|ut4Q()kOYj_(6FzN5rEXjzxD6 zx=wyg;{T%BgJ7R8`Lx{jV^?rQzY=I_1PoJUzk0ktj!zfQR_8*r%GNm8#hs5dBY9co zWqG_xMtug6baI4-jmZ&rV^vK|X<~o5{%t?HTgY=Sj8|RK`+LDZ6{D%tS9*l^xw0%Y zK{dTdt{$JBarUBxUG+&e0N9w!aF(7`XWPZZi~VqFer>aU^m@1Mcf{G91?-^@jXcUf};bed9u9v1) z8t!>9r6?Bj*EoTDpa3HVs^;@1X!6Q$?gb5ghXFN<0o=nC7`H~u9{`CB8ct8*;bQ7d zv44~g0{Za(>kq2_XJZX*dz!qk9{UP=Cu=c(@by}~hC+G3@m|_C{`jU`yf7>%x&b6s zyt3cC`v@2)?qBu>ID!#9pb3AB&i+RcdwDoLi*ED*^FlGmcX(;r4Dfoc(Naos2Brqi z@1D#nFYt>N5oH3^%=LAzTPlSnxZ%Uk%<JS!iBA#tYqzqUr{iQ^2Y;Uiay-kHz&;S41T4$>wf0F7$@2 zn3wa~_(?n+;Ju(7bm|@Nj>W@_LFI~}2Xcel%WU_;l3;P>+uK+F0Jz6bmtjf>6qg;W z1vmjSm(c?XDSuhpjvF@&efL-Bv3=2qq9{thB0w(KwrC3kNq}A!dDv`>cF|y6>;*{w zzK3IbVSC1#nehb3%dRv-Qsm*GhLV+v3r=HnDZ3a_aW&$LiGfjTC8Bgt_l8&oIJ zo?w<^f^K#>SEmd+BnwCsr>$saS5rF>M}bWyVNjAiihqs>Dj2Dw7;Q0FAzs_g7^$f$ zeGki{Xl0wD+ z+DYO<1#YFa{pzBu?L#@Y_6tnw+7|3rQl|hXRz#!-okK891jz;(Mimeii?ovCsAZuL zWsVG(GJggL8&c3rWL7IQh0K4Xf-_q_D&(}4LttFU>{i&Vh86{KDa7_!!6?@So@JK^ z&|<_c#nQ^*ZL*YUz`)E=5SPT3C4!M`%Th=#sV$2Hxm45?@Tih05L9bMnh9FcLQH@_ z3|$4x5+g7L${>lY30H;5jN!Q?sAGh;0%sIn34c;*Vqle2GY0=egM|PBN-<&(qLd1A zT7%00p5hT>B&|Yqnc8637>Sq_5a!LI1tT#V;6YAm%QFZo8EF7o*+MQMVgRWU)MZ8d z5Q6=d2n3?!lFeAc>J^kJ@IV8gD@LRs<_}}p&;|fLp`O4~0&#$&y+su$w|FGTrIwBf zx_|4C6DmXDd?6cXE0zsJTNKu{8i`5FQ{<&Y8>lYM&KAupcY(64DB?G+`TdVSQ8(;0 zTV(195#Bs}`t*MB?Ag=OoWv^-+mk+D-Q2sgGuND3-7?Es?;N0#MZKQEaWbZ_XJn-= zTS-mN#M_Svt?kJpzkR-fHPz=Mo;=3J#zFYDJ(akf0mNLldTOt9`zqM%QOzb>xndJO4YM42Fn=p2 zU{;4=)+4;8n{e`k&2^YGn>5&WBp**vjt%C@U&g%JB=_cRKi|hoVS0K}H$6E{Pfz9% z>Dd=v1D;fULBuB)z%E$R;VC`vzW@{?^X^;`R?xF!yR<5S2v!bkL7zqYaVA*y!C1ctcYk;STL_>2iXVaA%D))`J5p! zM9{|;@zq+m7`%WnTr*hsMQ6y%t}iW_OBliYec|n=37rrc%VE;CQ%bBurKx%jG*Frq zIf><14G!IR4{LC@cBj{1PZw6w^cviBI5Slov-*gvuojRVhZquH&{8uh5Qm8YEa%W= zmW*MR^ktuYtuj#B3f21lS%1I59WLzb)swkUd%DX~rhmN3nC`Ol9eoHzS#_YaqzZDB zVcq4>gV1y^r3+@|>4jC}X@!L*d3VD`TafS#BMrX2!ol_jL{Mfa^_SJv_5ITSeEH$- z>hHpxn%bU*=4r*gcIQE%hv{`7(<#Ib(`y|Y{s9~J13n7P^nkS!uzy_{jS6^&jdm9` zBO6o5-mYyZMQ?XqiHgs5c0*esLdW^|^5HrL>d?_H%?9s02f~}ZMA+vPhh^`nYm_+- zPDHVrJqjmeFU{H(2YjTpy~gv^uK7yxj6=8W-lfoh?yuxd28l*wa9??^hv<7R1HIr0(sZFzmSysfpf zhr~1{lc}fC#|b|k-fm9H<=*Y(EV<;+lgptk4ENB!jsdHu{eO-H{{w%M#OEI3GfxML z$1sa~n3ZGkeJ99X;beDsxek=(Vy(--OZK5x;`>>do`>3#2R$JVWlSDQ{|Pfi9w?1Q z-`nqagD!Foj_XhSxVGQ@C;I_B*VBMb-Fs;J?Q$~EDb9MnL-_po)6Q`)riWj8!q4Nv z-~G@fne|r7?nRFXUl;TV_=IxCUXaCp()X-K`sGct+-;PTjwpeB;SH&C^GBxFwVNwMYmrt_=6#+PxaVZBXmy72G41Y4P zW+YOQr6MShHcik1X%98hB%2&)iS`naC07scI={X%98$I=t%Ecdi=xQk%s1aWXn(ey z`Lj3A{K4~=)wAcXMKDWQ8uNIz+RTC|VevAW#nF;+5zSWl?9OZSS2bUVM0hVAZ;$gd z_HMuaJpa7caS1`j_>y|*2{H!ZFT zy*0il8q)`5_sgOxw~fx#K$A?Bt*XuW{^00(+rfz~8$fgc^n$~VAc37B3xA`8c7n{g z;2z5T9=H>q`9X9Y?h~e{xUse-a}~Zk?hBpiwm33E6lmb5%S*fdtXg&Zq!-C*gR#YQ zhc6u=9C>TIZeOcLmF>{Kx|xD9lZ`SCi+t~u+Q_bO8=HY5Rjs!uE%1H>_ML3LSm<$h z74^VR*mBvkeC0GD83akyIe!{H-0SRqhu zJLTlY5LT;vNUmIMP|nHg)+uH@f}v6FymfU@Xl_rHi9@?Sz#Rv5Yk#fY7cx`d&!Tnr z$rqvzUH5uDav^cWsiV+K>*;Q@aj@pPX=RzYxO2L`EVq=s>T1{6Z|fuRC>EeoaB1xn z`&7PF%{MbJQV@@FG|UPmt#0a|YMW+cTkWP$9tkGOoI(U0SJh9r`?O4NrngV~*3$Nj z0_8~KT$|uiS*xqk#ec=>jCm(D4zlRfwe!Si|G5iYFzIbs^@P=%{~t>e0o#YrL^!Nf zffA|*$^hR>>pk$9drE%66q$9cr8kT!B=}W&ry=u~;{>Toz3X-kX(-L69v#VEa?8J+ z?y+hsBKDHAAmW1pQI%Mm9dPt0s!SG+LXQT&{ydu|Zw$ibY=29LJ(zei?mDLE)wTQX zloxR$jW)VCYa`GJbjlf*pi)Oa=m$VOEc$-z_?6zT`ypm2G)R|e2DDX|_P+n{aj~f& zyjo|!L<4L&&kT#oR_d~GSdw2N(|!yJcfGF4v*d6=a=^$H6)W5@V4QvSm3FOyOC=P; z6K*;4`h{c9D1YF3zm+-71?CwLcBWLBY8vTsg)YJ0Q%3?b2kp>aI-Z%tV$H60I~&u+ z@6TTYTxaeMq4IMCFkw94B>IiPAdFKlm#w6cZ0wUBhdEEYs_0y;$GM%{V>$2zm zgMa1_o_|sn4ehFBt~!0K$4W~^vPIYxG?YkL$B%N$lnDxDLS7-9$^Y$W3-GYAQD z(*m9-dTI9bmTAmlDyLZuhT7FtX(eI!2DT5saU3<6ePYl zAf*O5cXkJ1;zK8e90zb8q0=G`!rRtHU(iP%-UwS7U}E3YhL*UIZNaSxs6xPF%imy% zhd2yVF<~O^deH4Xjckz_lmOwUEIM5-j207_gF|Ld2C4Up@#4{-Q`Pyv>IFcvqT?i>h(?Kv_T(J)jA!^peg{w?=m27i8U#{Iywm&Bx(ezoyQmmv|Qo=IMj&Gb0H z9NN%O55L(PFO0UD;@_^Y33?1Hlqe?ra15;xHHJ5ao+s|%CE^>c>_IZmwL>*ZSu6&B zJG3^#Bcx|{EO^s4Nt`MM&O&FeT&fo*GM=Xx@w9aTT*Jfuzm)#G2@a-AEc=DFu78Vg zMyhiP&(LQ}0WB-XKg5nsQ-C3&i8Nh_Hx*OO zRACuuo2YJR;Vg(l@KTI9nkCL#K`bs-{kD4c9~w7*l$T*r1r(Qvx&;*iH8Pjc&ju=g zS4)rEHW0q&R|x1uK3FS~dZs}DH;)22G(qd4huuABW!e@hiH;ue2FQ=^3^}AMuj6#v zi^S(}zK`=_=v z0`jGyz$kays;Vh=(#o*PeUa^;wY#6e1`62;+@+P62Du=(AYpMOz2Ik zK{ZAL+(&w=u9N)%)lu!HYIkOMQ08xrF>STg_?B@Jgy#sW7U-8fk$V@$VJAP2D7EV3 z2Kp?x3Xy{>mVwMT$Ri?6-<8OO|LQIjk-M!cXmb{Xk%Rkj91ixn@wQD1rCisAq|9^y z;}9H8mHT+(TB{3V&I-P#Aq(SwAQjxI_+}i`c1tQMGNoZJtcz2 zs@B(0jAjHs8$iG_q%KpTC&c};$G6;l)jDSrkeoqjGsI`y;wq(EE0 zIi@Vg!n3G9VY+$G;!`M5V(fq*>DB9f1&2jE^p!RzvUpc;*jg zUdI5O)`Me%--pE#z6eoE+ma-l(q(`dKUTvYVPU&4OG?=dg&iYblV(awe}Pdv4*`dB zsOSqISFlV|g@3IXHfG6qwnTLs9T0J~GXG8~FgU^-(jH>QOs^g7k{ zmBxkw)nJ0~LMJGkC%SZ#fBxSg*65P?juGbn3|NIATA<-#H+Im5@P8Nb@a10%NF)Ag z0SRXf115ZX4C=$^EFBu-<5B7tr7=DpCB7X~+d;ObNm!wE=qe0$*wnCLJz)UH6{o2T z3N(=?*8R#_^;il$bqkTey3DOpO`|UvPpC*EvY6@66ySqRH&rVse}IM=ln~iGy6hFQ zcf<`GqP6DoEA|+=MQYsw`$eZlb1Fl(zzw#_!=Zk!#(HDMCnJDPC((5z?KH|UI14m~ zMaHf4%$_7UYsTFG^aj|xWB}H@;ZmIoV>fcf{Jl$EA*76J-^IT%YP3RK6;ul>YH>>V zEHz68V+YWh=!cFSe|+=jTU24SglhAlmUgzC&$`nv2=!o1y*b3{@#fQ~B}L6kruh5n z0|m|bQwfOSkejlTM^a9Yq{pGC`>##&oaYflY>`pyb^Fc3#pHA5zcw}0PUmzr9_-uc zP?S`V?7&(M_VrY%y4Jbb^dL8wjn(GfynO@O(f6Ku(S(JH`1RYXzk~ z0)^BFOLPBYV_AgbaH2dfg&ce4$q3ux(0}(>G>2Ngyku^el0I>cq%vf3YN33WpHAqQ zWd{Z&ah$;0B@V%XDX_C7n(cYFe(?{b*mvU!Wo~41mu6B04*@rqu{;SB12Z=>lkp!Y ze|=b4kK4!*e)q2skQWW$q1n7dB8vrP4=)lRh+)jb0`@+%$fnd@9!is(v4Z^g)YT-V zrTs$o#jfhAufD2gxI68_-EVKgyZ2AeZ{EF+)vgGNEXsDz^-iUEkR8%pmL7sAPIu4M z?$iEL**A?T$3Gtac>d@7@w@j?zWOqWe}gzuFhigG`!d?T)6K(U8m0TX(I;+=J-^TT z#c&JPs68!A_Crsd7t@VAd(m#RWn+f7D0}x9sUQjC-J?=Lk&1d`7~!d9*3DaJ)*$Lv!-pz(1yMpm-cM9&%af2(|It^5qc|@7+A&$U~ zy{*0rt27D};89@pkD4V97gO5Wf11i;sP9hRV z{@NbCCBV$bpG^mgP<;=h8HTp2?2D}?eG45qx*q%1!=rbV?KCND20~v_0Q$(N1E0WR zXIKGuF!j{jIpR-aF5rPJC%)>@yKcXPN;SDCNe|KV>xeisl?5(ODhfFR=IERgvj3WF@a z_vq-Ra-5vG<)CRWcXq7INY-(!?W{*s>8``*nM~;9`h9Z&OBL$gT%DE!b{bAd%}*2r zM{gVIEOVt_Uw+>rD$9c?e@whZo;}HQQ+xEj?)m+n-I?k3xNzTS z7p;AmfmPt7>n zaf2n4&qe>z-E{+pw;mam^9aHP;zLV$Y4%yiN|2#CDAXd#)HQIu^~J@jzRoefnN9&C zK14W;p_r4TQ>q$9e_I1RiWmc#;f^yJ?dD2=ys{++eMFS;?!8KHh&2oH2H6qQ13eC8-r$I5+Jq?LJvk`R4;p}XRAP2Le~uklu|K&7qtHSA<(_<~ zA$6Wn4blRTpQi_=vE&f$OEJg1CsZsGu^3Co{OR@Bu>Am0V_Kxrh|q#>%`Z~;fC{4t z2w)GSB=K%v%i@mTzdu5e2kKwt<0Yxk*0KtXMGeU7_64_>1#|df2aW?3m4S$ei)7CU zaQJ2omV{U=e_Sk`Ib^26w;*Gwa*_G4&+I@mZO{H*#S?Z6d%`;Dvz0)fB#N2maI`Wn zcJNX!jqWyeWVodT)mDVelcY}^1SKua=Tr|P2D$2?ePNUg-*bBlutt`h(Tmt3OdSYQ z&?8i^92tPx+;)1iZoe@U_yG-tcq^3P4g5Y_bZL0{f8=^pC?WsCFE_)c8rfIkr0VZuS@`&)s^94qzxQ_WHfo19R1iT`xcHn2FAIXYFGq2J49s?h#8j8(^n2%icQf3!4t-VS$L4!V_=mZ=Y`kCqmi z3%Kq$ZRQWS)IjjY3d#oyWD#S=)-d2C`Z%f@uM!3MzFK+!KqqbK=V9a#8*ZWG@ut1vL0C{0M%I38=YZJ08k z)7XKdGLfq|4x)qg99zpE->xO#@W5EC2=S^t)f{-c5?(1SGOD6L%M*%?t((DDz@5B5e_bEIX_#_U%t?T1fQ}d9RSd6T^c`>l8y_ZK z!B%+Ll@D#r!@6VsT(&$@#1$T;WjQ%VaSYp8kpfhS2#lFtHjr?1>^oSX8L|B|O26ZG ze}PLCC3fgQ3e=4~b?p+MFnZL3m)whFvB+T#J!tyS!%?|sdJ&sB!#Kt##7cI^e@YhX%{>5FFfwnj%k;cNy+Aikq3)(|MP9G z+`a-ay&>f$rKbW+jMXIRmr7a6f5%p)fc&^bz;pNn?im7tbjAf|WWu|`TOqFF)6q6W z^QEj~TPIjy4RCQ3QQ(P<+zciAVE!{L_(Q@fS3E{U!42J92jed$afmDvCs`yv?BXa$ z^H}bq5vZxcaQ{Rfah}V-ibyo*XIO`>C@`5ALlrd6W zo`{-g^U5#q^;*7v!gk3q`dFwSi|_uXc=IPYQ%^z#5xy?YVvxn)&ySN#@jwpV*46Dt zQV7uJY`pmHM^IiIyhXrrR0SLd9=akM{MCeb{apSW&1nP$U}++Mod6g+hrgb%@$t7e zyHB(t4^$xvi+UIQu}ZBZyZK`Un{y3vGx!K2G3Wy+B*=3CiOSNORlh#J`9Euelb6BA z2@{u1%LNw#I5C&ufCedlwOdPf+qf0p`&Vce>YNBeJV+2{R%tSso_3mPV|SIzf~Fvw zi9~uxDvtZ>doLc8NKtZFxEl*3!219`+!x?@akq#Uzh1@e_b)eBKfR~PLJ^g+baAs; zP|k@=vqhRn!g#W{SuZ|C|E{*>*Cop$z5L_mZ$G`~?C1;52$d9n$eE!JU9(JRlz*`! z2eerXBvU`~^>QVIjNVlF_Ngsfcw!fJ7{*6yjSCiqbrlZ^Iciib-qc|sD|k6qW6 zZdG^KNTN#XwSCpOuG{6x=xH7v8okL2Kjg9O?kx+J9kEzZl@X~FG$>Jto3dBA)9clQ zR0$(Y^;1TGykyaT%{_4Cf<^j!p&z=kuIwX9#s!bMdz>VE<9{7#&Ihw238$H-ZKU{* zQ)icVP5FI79+?nHh5zQ2<5F3^b+gvZu_!EBm!D(Kb)%~#lTlYLX|zJJixm?I;fxRG zBJYf5G%7p%jiXId@9dlIUQZ~IN|GeKJZHt`l4MHyVQ&zBpoO0|>Y?;AK8a}G>ZY|F zd84fux+>~@li%S28)0hu9>yM|GmT0=TK#C85RuTKevZ?GA~+i|>ET!AcW0GJG9pBe ziLz=tov(@Inq?%DY{>LCzav+IW=G1mxUaa19+xW`f&3sm4pC8lXuh#cYTf}#v5AY= z+U^3HP9;Ns+-y$s+wQf0*A4q_5eZMWy@p-0-?{<$dac)P@&r*rI5kqMhS_)2O|_ux zvTQ8+bFcT>e&Xb#P6qc`4P!eyR)5#UzSC~hzQQ@`%B}l)(;=*JT-W5wRgy$sbkWt# zlkIma4x2;xp zt?kQk8ow=*G|J0ue!F!aZ|cVNAFL(3_P_0Yjk-Nn_R*>~A*fX}JyJAN6N!43OKpS+GyPbyp-s$a=?fzEgwrASptrA68GBTXazUmkJRF{;JjP=IF8=)2F z+$_)-2xKYr) z#WE^4_ElB;4_4=`jHSg==4i3hEUR7L&i0^a_}f0%RV~oT2^_Gt9k6hfZRL)Zw{u8; zuCBCw`&e$b_EirL^Uc1i!T6U&zC}o2f6?gN00#SwPn>}Lrpb4@wF?8|-2xlu%rRU6 z4o?$!l8U4U7m5)Q*rhK%pc=YS;3l#Wj#{(aSHIj`{dq+ldVFjMo0Nb%Dt1?&{)iWA z=mV(T0gkPs)N$t5B%+|H$?Vd7m*kK-kXa|J|` zBBO*R%+A5vgF_$A0E1wc3H3j4KBgf^Dqci*rX*2J4TXQF+oCCL0C}PScw~|VDj*nz zNq7+jgoufvbg00!Mq^-n3un<2Y)AOfXwq8aTTYZps3HktK}z+w1R6X|1HmnS!3N1B z5ZwvnInQasU@SeJ-eEGA0@N~?m=xSzEL}9d@0`Q0n`R2)7M|hUnGi6Z54pe1x5a*I zGXk(<17S40FTFe3?5pDFskQ-)U?dhp1Gz&J2xho^*gB%lUvL{(O&Btd;BXxwgC+4u z#Sc~r`p*8g#Wrs)fOf7B6L1565@|4ILx~v7vJ^76GzhbhjHVn&;ZnnQ7PGj<-|o(% zHJmRKoKP-?e8*@tVqny&F>Qp<;I+e92ks(ooO51l2=)-WuMK7yNisRYY+1c_Bz?%X z%fDM%HgIx5oNw1hoNa)840Qe+(8H%kz-CTHvvUIU{4&z#N-+UaAi)!V8Hn^yA_nO( zMgm!=b!L#h2r7xplncKAae$^ypT~MQX9ht$3?DEaLEXsU!p>Vrrq;t`;ja8 z5RHB2M8M`9fZb-dM_?DG!qAkErIz^x)P^hSQG2ZDT-45$Vghh6daW!FwV^~1wH%@w z#Z?$;GlG;9tTg0-7PQ-ced|gNlaPG@woGyo!=gF})xLWIlC1kFXbz zOMr&)cs^|M>MZ;RD-GbyI0FtzI~``H&p~jun5XDL5>kXAFqDP?V8X$>hs6irr(sS~ zMW|#xXKk9-=g}9;6C>Fe((3`!$w=1tYKOm~sat3GPNQ2N&qBt35+_4@NrzwlFdrfTpGyz0ajR0)u2ypv zM6-oBU6}w9{cHx%==U+u5Hc`b2qt1ymOh(oKktqbu)k`-IU_30{F&fA#xL#p)eL9M z1gr=|Zv(~?K~&j)c4g5XcD*$Xb%iN|IgoJG*S{KjkU(fUKI!@4ogc0ghWAp9lmgMl z7N{?swih=3TyZ8q1|(=2814rXL8LNp@A2iF;d!Jg*t+S0#~GMcAMQ7K_4T~#dI7e0 zA}hyiM>sW7wDiG&D$W_ZA4j-l{RZNL6e?7o1a z0u@CQf2=5*7wpd#V*)8y%8XfW2D$MI*XRstWVb=fJ8ru0W|!kWmyac zh==X`U?OHc!6Zu+7P4Y>P{HI%M!3IIQ#9+(F;Dizh4l<)ja99fjaZK(*RI}`g|9go zk@hAiY7cdPsE_}+e(#I#KomURD9P`uR!FUN#R6Bf!W_+ZcID17z1_OHFLr;uWEsLI zL5PfGF&#~UJzzX<3g+^iz#ouERAAN>OvHc~{JF?P7>J=8GFJwZ8MD-b7*d>z=Lj#r z8RB&!kkm2flgg|sqMF+^4t+G**ru%$?|(B z6?4_8fnGDKQ?sR*zy^zflnOb^oKIrPQi0nBuff2&Q0npJ(lRhhX@R$Z+>J9|n6$lz zs6*@H`lKAzCx0Xi37Ct1Ndjo}n;vNJQ12H=1F=C>N*D|EeNqBaKx3Kuddy80r!)tVSjJTG*)=;tgOIXxLnN(+j~gSb>^a!snS@Bf=8X{sE#vL zBloA0=OGxbw1CW1jg|gUgEE)>DejO$42~iQyum~adL=ct3qs(VC?dF8&`c5m%qH$} zPD3}GHG^DSWo*cLYIM)hvud4kjU;lgo_E<%ykeCnHj{yDm{dRks+oWLin=Ws+K6O|-kKUMbcPHusCWnrH zFNBB<2;#yByo{*$@O1EpF&eI% zOGpN~%}hvUOEG~i=#JH_+jHmtpNXj!j8M=iQzk&Ou~3>0E;xfC{{Qrm(kA#DSxe-TbG+S5`f?PSMcaQXRYWic$`TlyE~W6Zqjd)c_23J<^-D_z{%#{ zZ|R0D40IUr5{ONgs!AWFs*;S8LxPik-eGtB>yE$`CnO<^Atlp93WYcm$?kaf$DcUa z!MapT27gXv z^6;1t$q>dMil-=*DtXu^e{7!ebGxsbkC?IhKOcU+`|06oaLPE6e2uXwS199yv0vxa z?p)iZ30&jVlc)T!bC=4EcEl>7npw`Qim}R@(=4#eOn)z{5Z{Xyb_dC7@UPl z40wLG-IvGW)Ry%ruZFq>By%7+RUyfalK|)e#DDD!yzwS-&`R*oLZ=jQ@V2|W%9|N< zmufLW9fo&@s7Q2Ug=lm|%8|kZUC0yJoP4m~65LiD=40T#NEhoR0PV79QTaXukh; z%8%u)orX2uO2`u7-3g3ztp5A`Pmb0v`hVRlc3s`fiZHW2$zkrkwPkA^a&F7h!OrnP&$o5ZY20s`2je<`V^?fKksK&ycLF|kSkJ=g@X>#G=IMg z40AIZNshxbOvjAuHeCx6GfQI+Cfm5LKHG?ODqK3a*V$pG!Tjd)Ksks+ZC65xP&YHM zuHYvfcm(62pNL!d8*2_-xN0s|>y|p60=n{Vg)s-wVhLlZK1Z0VL|{nX2(iBTp=xVa zwL*@+-51Yhu_#V^x5U^le>qpxCx4qTect|Bl%J}?)vgGVkq{t9SOZIsuXL{i9p8K{ zyC?s74cS)H^EM4fga`dZMu_ksNQ=OcIee@P!;{q4xC~yRjIx9)a1N$+>Zta60O)?L0Oy2-XJ|}WS`J8?%J=c`#JOmFqEPwMEd%UGC zVGj&ln6NKZVgxp+uOyP#SRn>k9gzizYnY@6xJ@|Z&?Iv?R8{L4|7xba?RMwJ}-q`Xwf;EghBWrf4(~&-HqC;Zkm?0kzgZu+_I3{Y&B2>aNL0 zn{XcMcEze?`GIt!&N>$U3+R_d|Ix? zExB3-Wh@T=XdwoFsSu$oE?dkH3$i!t-;VY9kVa@LTvJ; ztk3Or#Zk1E?ye<$3x88AC7o-NISG1a?ee}9=wPsY^%kb%qFn~kz%SUBKKa4z)R{*E zL-cwHY`I70x9nE%8|P37!}o#ThK5eR0ZSDaVb@d`kv*SiA?5|(6bfD*mwLi|27ZAQ zlnK{YIBE*d5N*F>4Tx94WSuP3;O0Fz)u(L*VK#TYjzPMpvww*0vO4q|(0XH*h3`af zTp#1h)APAowtKgN*)E(1W-krrOSKr`5JX5KB4LgdVqk`KO<)#CxQ@y7b63_uJD{>nM}ZL(+abkQt){+nsa)rB654nhJ+#** zE`@ff7PqL-41cu9L`2tdv4rUZ28>`tnDd427pI$Vc47TvPDOai&sT&9A0M;`mPMX!i56nW(b1NX$XybQuu=(y(NgNpT|7UP z&uuvwUe*{_m*f&&zF^#8sdRFs^mA2rmtB7sD1b3ng@5M<_aW%*N}!Y39!@|{Ifymd zu&ZRWI{QrW!teDGs@s>Qd!yOkH}+%=zw|621JgS`3woAoc#qQ*5t4=8d)GVRRd3|J zd@8ioGfCufeMWHBJ-_)<>^t6xSRv+}(7~M2xP@0^Lrp3tq}v#8ibGSh(>2rz<5I5s z-y|IOCVzLl&KsRlH-~0|0Bm}c5*G78o!?U)^g2E2-3xoxE7MyP{p zN+UY}(LxNmI72G300MM5yvvjd2us4Fd#viiG`aD^Qb7?1zJg%~!ThH7*Ig(`9Q7HO zLHxCml@WLgNyuzdJXhr|_l?`bG`=(B#@oyF@qc2WhHf6{HcjnP4MuE7+T&^`Qb65tgypd6d^Ezn>9us=fm{E7`c2}!SeIKewFrtMbL?m#25(6aj@-nDye5M*lqiWLOMAR+3VYbqO*74Q{P6lv zQVO@h@B_Ai={~JiH)*xju&C_eT$@!ZHM8HzO~>lGoF5~a&z~e&!EmmB2Lbt1X!uL; zE**D-I|&Yt-BODdV&J75rLkLT3LF9z4u63)p81`crtbFT<21D^jH|JQnG44qU<+#a zv$d|;(LdrjUlL+_CeCCNh|$?Y;$D_z)BR57a(3mr_8CM5b2c~%;8w1ob=Yhae%Cg` z8s3DZ2W3j!%;MFF`Ndhi1{EWe5NQUfqVqSA%2?q43I{Ct zKd-(Ml153zxoOH!ncwg}K>o1#J6 zw2jdg*z5yW*4hGeBszAp{q>#UVN3GSIP!}{(bVvpnU8ajdTWn*KV4D#{CLISNj>H< z-zQ-Zd4UXx@THd&SGRZ6Tfv`iJWBjH@}Bh4!t*0ZV(G&;_ugIoF%lA^5P153{G<@- zaahZ&h#!_$*RPrMV)&c~-txg?G9sY}y+A}H3PNwW@@{8!SC(11R&yTB9@q1`<(sP? zm%aVDFGwIRu{{?t;b4Zr_ODc)bh&EN`NH>ohsL}Brf~L%z|vZ+?TUx0HiLC${wfi( z=M%=bw_ps+D2?cqYL!{qq-}$LBQfimt@PdZ`t>O*R~1e?nUziUZ%X#fAvh!aBIXc6 z5GjV%_|0HpHx_rIa#inxkSr#W^Yc+0;3hm70QzEXUPOo{IE@Kq!6br*lmxyPaImb5 zj7gghc~+*!c|CnsE+k>G9Bw<}b<*8uiFK+5)C|}x#%$EwMP7%T&?p>#5*z7!f|}Dc za@6cAbS^c|MR9;t6ix;UCsK2&5TvGz1PN(4E%tIEHQy*WDYlMmRr|HICk(Tu&64IQ zRWE>oi;#psqXCK$RUhY4&QzVv*{tep*zBP@5QM{73X4q~h7`jhaViiWXYK_QbjPSJMusf243v%_`8md zz`MeD@h64cWRRwn=ANeM$4;$k)poUx_uhhYNC(l7j9H12+H?&Iv#LBDhHvoYuj`v% zY*fY_alh6KK8MrO&0#s+x5l}YJ{QCRHi8ItTEu5ida4jjX+I!;bQ+_9UlCYEGN1=@ zj4slos_Wytp1duW0f}Ka*mo3sRo7P6NnJI~0@0+oAt|z%$Hb5PA(RQ(k-`k*;(L!J(aY9AGX(%G#@r;oxy1~uNzetRS8-5M0}Z`@z(AWWNeQ%I%Z0b zdl4bX0TIV=x`U~WvOxbaNqpCv?fh4$c5^gNTwr@_l;b!UZf_D`y~}_ag(i^8Vx#0` z&c8l#!=54t35QdAMo)18r34hfu^$eV;(eqLvQEc`vr+7SGYmxpQ`_CiLNrYwol7>Y zrvOLN0Gt#vLZz*CpO6CPHb+}EPut^MosNMAl0?vR4>0U;^$DRkP>}&;@?EL&=O$~m zL^gFB$ci0710yr3<_q|=s#jSF>C$Dqx+)HkU(8;;ef#IlyKRMbg4)wMETnoc^Ggj)|XP9Wnk#-%QEz&CK znVlqr*yXoX*V+#svOKktkevb#-RP?EHdpIHeX<{az>C}UwybcAqk8!{ip6y2!BeP6G;A}w28jUY%=b(kNyH+k)27QEJm~!N6F_UXgX~@63Ly8EUYJb;Rdq_2Bd8DdS1tD(L31Y%{JW^*%?DG{dd_Rd? zK(`Y);J>O*SW!0dhwUnyAjGT^}7`8YjBlvxs2V#cj`ji!5D7I#}u?m+?jbzvU=~s*ssAi_h zBs~q!N*=&F99o8eC=rw&1q5a>gV(Q@%^@$D!;>19Bs#in`_ymk2b-+1K?CaO@onIL zQ|A2Q;zZFU8|&4)dP-}pH>k@8>IP8z$*otks3mT+5YnHyr|y9D1Oh9;u9DBzX??w_ zy8Ar6hGQqIxkRFwb-M_LZ&jH;qY{+uBt53M9xGG3sL>Otd1*JPMeFy|RV)Z}v(Cyd z@OEJ?<5_|~kg{GJ($IZ0uvUV5RYjqH%9SC`JbET`BO`Tqz7L6Z9-bK9#f` z9Fph3I#Z;XD_|s{`STv9?ZQ?1pdj>^u!$4y7Q()n5*{%1u?6FV;Rg{MVqMe0Nycyp zXIanQW3l*b##NcxL2aIej_rOBJ5gp@nPjG~_MnehNQfKDjs14GX@QHnrZrMHHodA; z!=^)Hwi|mh)0nITqpxhS`p|2u^vGyaWm_2iW2kSPd~CadfI!7)YoZ_;tb$VUKdv(b zmtn;S6qicp1s0bcGy*GsrC3XI+cprs`&aO0X(oaRg3mPLnKZ6D>7hAT$+YA6z>1K} zsYI$I<=Fl8-Nh~+U}?HeFX9r9#kb!+u$1fU9M}2z%;o2g>$8h%+Bg zA_`+C3?kvFz*%R`?LvCOTdt(^f)U?lwYcFu zLO1mtqH{b~BrrTTciig+fnSc7bWhkn+(+7S;hfDa$wY|QyYmyu~o*H-!4b{ zEHVO5xPAaLX2Vr~+N5+cZ2am5ZVf+@Mw*hIn{>Z9?NdVwV{YzQNWt24$1mHm60?x` z?3D~e;xiE)t23DWP`D5sjQQhs1R@(6Q8TcLCySEZl(@|G>ndKW&fdpx>6R;1YMb1D-FG_hq&xf1=ox>b|o{}NPbL937gNKR6oa@>r zf>j3w%%<2FkT28-oXwN%fl?XUWSf{(v0A+Q3WS=douNJSh4N%e`!z(l9T?xLybXv- z*Qb5UeN)aOW4(k-w$PT!TRh+o|+WmlfYY&X<~EVEb%{1-nq^&W!%Rw%TWRk!i~LGlds^W zP)R&WBi?5V)<*yZSZE2^1mowfn^V$`uVD>E5Poz2&ZP()FOHvFKvR59$77l4c$m>wbo*7m>vxjMW^nA#txn zM!Kis9e?L9p*RwM zO}E?@U&?B1^|pHPR$%}Wk#Tc1U-lsRR5Vk6ZVbM@+`M2-%RY3lC>hPusUa9f(pxZV@7T8>pTap7KQ8oct zDkRy?VzK{yhfk4;D%Z`4q-f2|H{ZNu-`jY;_x-|G>z|JopKim*!x-gp9C(kFhrlozy3wsc~(&y{DuQylMB^gLh2iWacbHndpN8WCp%oT<^{v7W47B2D4 z%YRY^LWo=Eg$YXh$XiQzQvFlAACxA`ii2DRwo?x(R@0joxqoW7BBHOZL3;xyxaLay ziTq;U9q6b8j{NO|csYMsRs3Mf7Dh~L>Kz;Alnfnd`tdC|a=A^<2G%zH41}WHH-lvr z9EI24S!J^dv4Jf%*T$bI&>BfoloP`&oTX0EHj(Pah##T|M{p(NeWev^l&6VYOt^vO z45KJdTyKz~XuFa%8%1I(*9|++Ms7&O1;?|nV6 zA2prxzs*uXl#*9!>Ajjrr=;Lox5|P+M3jy`BVtPqv-)=0meEo!KwBH-t2=(Kr1agM zAKtwIzg9+d^Pqmvf>n%`S92z}B3@J!@A!V-4=(VY46Nq@*zzyvTH8YAy-?NSS z@vx;9>6(kXYI=WbvXTGJvWzKKO5q?LN~NPRnR6&ldl3{@Qpm8iB=$0NG*?Gf*Dh>n zzCo=_r3{Si=HR+y?8s0Eb!aK|xQGO0c_2mbOk2vSFh+Th>Xf=S&azX(ki50COfe_j z_ok!k!LHHZWKnENR)E)JT0*bL(|0-2=^Rt5ulZbK1)hHrU9Fz|z~6&jNtv=pWeMEj zMfX5mKuHKoi1JYN8B>Us92I9MPh(Si$e>G(E)Uvc`<^aapd`5_{8|OfBIHMgt=o?5 zbZt4ZVXG_HJJSxN7HOzR5e&Vys(`GM7^-UOM!jmLPGNebb$e7@K^Ur|T+|RL`qfGY zBJ#nGcpiU@%f5+x6i1mG!mflu-WvwoH@(^tw? zt;R4e??l_1M=6F^h}U7ZgqYhiAjcTRNpvoyVx|_=P`e;UNi-QwpP}C|WG0!dF19<; z{G)&7bgSUI?bs^2N8z|$&0?a|_Y^)fN|2A8WU!oOjp@9CJkUX@`O;A(z5L_U*uh5$ zg~imaC1)n^{TP|>d2s9Pn`&LX1${XJlsG2>j2%y3G}$MsX`UK>@Atb`J(0H6G6~L{ zek6+NG)I0sA-YwBoFC95oZHJhS#_jXM3aAGG-82ip%@cPqZy0L;&+F^ljJB1f_Xe2 zfO9YEJ+}pNuL$yilgOdWsoV-$V9gmGha*l=hkf-XW~W{CQH zpdH!ReNar%nx~pJ8$Ih)ZD-*WSTbn0Niy>hI)#E`+XScpae@I7@SA1ozns)}-uVdj zc=11)9+t$HVNwMY0Xdh^mk1OAGne6G0V;pRSKDsmHV}Q^uQ1Su$bij|B6Uj_L9%tT zZGol>#19K>fckJ1qzeSF9Fem~6L zonA>Xidf{k{%F1&2`^-R;EjAQV6OB=^JMhJd0*)|k2m`K%kS~?I$P?y@!kBl*2{nC zl^b?l@PGwEz?LxvlWeoyjHjOKIcQvGg={whbKGG45#wyNV=v%M(2M5j%d zsZcEN!~IOJ;qTEjqlhUPHCf_u8~T=e{Qa-9bNChA6g@}x)5C<1r$`}Q$nZ0Pfml?> z5p_8WrD}8CZtiv2&#lhD$xxb@rAB|}kWR=V;X^utMIK#kUB-Ep)mf37dJ=mX#;Tx; ziBV`=7WJwL{fawsA(9AR1Rx$u*Lh68-%*0IhzCvmmyf!PQ$5^K3iB4u%Uxx$2my;Q z>SJ-Qmqkf#2^Ta6Y_dEhb+M1}8vuz80NK1|yoATNl5Za`Y6{7O>;1n#9Cm+3{=MlH z++!*V56Aa#Seq6%J)KXi5(?bFaM)gu%nSPn+;W%SU=^3s^BYqKrPnD+ua=<3Jzy3Kgw@OQS`3h9Ow3q$$R*b`|iO9Riwc1vom z`%&jTy6fJ1oPYc8`@jAt_ym8lz5RJx%?uCD6T4`O zu%ame&lP2YH({OH-=|eZM-A}3HkAS%ZohqRTDIK3pXOYzysrkVZ%<=4z_^&c?>kRM zi=-<3UpS^9V?(Ahm~WebyV)7Y5c< z<^k_@@_L~uUAgYe-;RI;tR z9Gk>-hsxSbBQf*6mXo93go+W9ve*gw(P&D&Qu5r3LT zQnWAXE*RlbZu5Vk8R~ULKpaQC3+=8v0birob!MKdqOy66r$H1pX``M-1(%#OJ76qdAcO-yUX5dDz6gGnP-<4J#M4;X$#Sq(MpWu8_wWQc%z zp|@jT-f|Bt1!*e#+ocx}W1|+9+xd>$x#wqr99@3ytEZP<-OMxV($?WS7mGX|?~8Xo z9_>$76Mypdna|%}E}y-8Bg085(nv*<%jHCBE#fGdL}4tH4kwrS;mreP5^ecLYUjN6=>`?Kjh3Y0guJET{z z;4Q3u^mR~~dSaXi#iicgIFv-rTB5D9sY<-NqFU|kf65Ek zS1ovBm2FH;3uqE9qE+CyQr^vU4iZd&8Z>suyTQ^!uyh}+3pP>4eSM*H!-xMU}Vlvb6+a%`|^!ScIrhzeEoPb zhm4V0k#&o#TyPSEDzy1P-oiA1oWKd@#SAT-!I{nAL}nS1tJO^VGYl2({3vx`-4B&e zsT@solZAL{1tE1r9#EeKxtWOj3bZ;;$?E-exP7DAQmsmb_9Q05Zi4D?&f=W5s@QSVxWDmYqF{>PzIuiO1))MZ)nM63-oSH^d5wK!X+R< z38>h6ph-=J;ksi5DuyI$e{U(X79Fi$wZFQ^U6bV`EkDrv7-$973j7XcY8_fLsA_Pw z!+vB560f>_OQ6VuAoku>tYS^b7eF*yrWLE4a)*lmF z*K&$9R=_?$VZH4}0u=;8O2^j&%{pT+1MuHu-IGuT_=Swa{gA(Shg~S0(|h_{+?mGS zVmSFTyC1GwLX;;S=D!wPE%TlkTEQ@J9AedkKcNkDiTa zN}!9r&UyVcH0g9@}1DoLcOGK$?3`)3rMWW$-rRsc8(c0mr)=B|DeBd zu^)*?Dw1^9R*kp!+R%XdM|X07Q_^~Gne~+p+4HM?rdYM4F>yzehvimu;?kWvL9P0h36dOJYcnPK87Fe`&tu#4^ zbapTZ6MC1ym(L*_TAncJ9YnHqu;s=j?4J;a9;#GGzc=4@o?}Pki&*YCNj9*3*s&AI zCJ<0?iM$TsbY(QsAB{Z8_#S#w06vxv_%Oq#o>aT%f9)q0J|8S@YCpCrk-FC-;=ife2NNlxz8Sgf<&QWXLZbT7j^oxvtz+RI;#^<&1duqgXtj$8gD--csbE4f z6l#O6OT}<+kU)5r!$V9;D9}Z@Z96D}@2Gw_GMyy+rv`7g#eTJDa(p@3MiO6AuNF9I ze{$}7O?OVCuk@+vMP0*|Yl3iJ0Q4W+_T{Q>if+BJ_v;x$<_P1}y1+_Yl{K4UZmBCS z@qY)TyfWw4&P+0zo9|auzfcy{XNzId`Mi(wy;`tgBvtp6MC;% zrPbBdwkVt#pHTw--NekF9rNdFqaX*0Tco@Fq6U2DVLi#2Md4D7YC?W%d#$B0|Q;N4(r1T ztl5)i1dK%6Y$Q?>DY;pb|GvYkE^(apB{Fa4@}2V?9%eq-Pv(=~U(N0J*XvhrKQ7YA z3a#QrJh|RYf@FoF0GvQ$zf~}aqXaF&XmXuTHq%XB5qu{uX3-*=l1k9xVirs}QxnVS zr5cd6S=}&B1=-qvLEh#&S`ah3%n1`z-ruz9fVj!2kT+_1y}j68f11pKIa)?3d;_$K zoa*erxgZs%>JXFGi0>t44KB>!!F+Hd`J)jv9|=*aFw01$gfy~ew_3>>x%L(t8#A-X z7_H4Ulk3y+>xSWWZ%^ofIZwB6nzCmRKkucOHM3 z5L=(6J1Vz-+rjZoUXtEnIOx}v6=8tFBpN3cme?hVAacImyirQ^AXvXa2yM5I#)*^X z1tqOR>Gd#lcA%Y5yL!=k4W>Cy3Xby%(>m7#5z@^I+!8b2&DpWq*ylhmJc~J61@Xv8 zb?gYN*6hLAfjAD5asVLXj66DBltFgpL_27>o8cmV^OhWIV#ZGcY!mErBP+*3of1qN zekLV0+_N@gP1#CXn$qO7?rRJ+fET>>C^cn?>wCwFrZhCD?+46P>Fj7>GusmPR57qJ zMWw5xClrjS9EdeDH8xh-fM}38NSHy@!$3h7b+yqVUqDRH1?-jxS(D0!DKlO*1<<)A z4;f*9vgR~@7|!1?u4DDsPaqvPkR-aqJL$46L{Nlu*#p@7#Ajf(&Frj|M+&)pw97Ei zHP}rE`k${8CmqWwUbO@H>U`0=d}=6p)hH|LQe)>CLwCfb#`Xk?vFB#tLPv`OZ!{WG zTHRfFt7@{N-+WY|%FsQ4wiAxdWT#GcR|=GWDZd*QRjsiruIxOU;vYB%1~cre&>0_0 z$3!_tlHS!p>W(q8#$R1AupU_x?bcoCx9Wg&_hlRj*u1wQIU-9 zs1qvP#gZH7a8t;EIw$Z{J94CjW$UefmC^_&xM2km!YR+VZY}^nGjqVg9VBdI7&;#q z&>twzBeYuDxG)eNE~c6J09nP^ctkN;1d)r!6(1o4KG^hn3d8{8cXp1Neo!}4rt4Uu z#VQ#_rzEnbnQ*9M=}7Z$LrpVhYlJU0b}#fL{T0k4IYmO=ySbmn1#HB(I=?J`zC9p& z8UDW5^P0*-+1>ij1!6u3!GS`i?)L>hi#t@tg~$CLNY5*>Qwaw@17qjk4tpg~gIZ?? znz=`ZR#EC6xwTj?QWVWcSS!)DTuNTfnyM3yD_yp04($|r$+s9#No;=U*Oz*EY2uUf zY)=*_4LsXFQbxsLQ2tG0W*;tp$AioN5_Xo*9cZtjB?{6}(aQ%7`z?`2Vv2;R7`lhC zErERy*zX=53R1yoQ_m^{N6u3%NKqWZCxkGkqo%`R!Ct^3K}$gKSuAwo)2CSY7nN6M zP)Sibj&)5+Oxe#nQc!kx5icR7$XOSbF`9Qt`$b=rv%K_5V0EEypB*_q#7YcxBuAf0-z^1Y9 z5YAq=l0xMRzyhdaiPan=;o!(`*RTEulMNvimvP(#69O_Zmr)rB6qn~|1uK88SXqyo zNDzMaui()m#hr%c)JBnVYftSaUhRW38ewSLC|v0VPqN2<-|7nkCXBtaPnf1%{Z-Xh zUlm|$Z(-}*0*|gQ*NdlbiETwF@*Uq=?<_mS$cu>OdjWEYXRT9fv)s^{XL(iDdDYyk zNJy5?K4RkL^!e@0?fQq#+|z%zPT04F9fZRW9!V>Iu&kjsE6?$kWm{}1Q@^X7x+Zzc z#dCYfxLP@KVclAV1i4;lt!x`bo`$3msf|sguFrNEU_v8I1OHZbhHFC;|+0^G`&mGj5iNB2fy9GIQP9Wp>PO8G*!QMEEYZ$R6CVv%)5WAjC18*WFuk+ zL;qAXbch6)xD;j*d4ju4#7&$hZ5}t73ByP1VF~%odhvN-12k;e7V$h3dcNh`K7jYE zq*!cjv6aHZA1sVW6k7LkYhl3-+ZQh7)`!J!iX8)|iRU9630n~&L7<=(@z;q{0~>j+ zd&#LD3UJgN+Be~&ARvDi0mmpJ;T-TFK-eZj;9EL@I~2UPAB8(|kb6hUi{M67bXfPEB9(G&i56l(xDhi(VKr$>Cr~js2v~zKnFD% z=V+rri2$;_j^z7y3wY|d1HiCyJLfpoE}Npt${M8BSymtzaA~*}-tzfe96|^AK^KRg zvob5%;@g^ASDA=M*=TWM^mCgrBV8b?(s$oQ7=k{#WadINm>>+^!m_$ z5^YoS>eKwB;uGYBUD>>&B^5_106(sj9m%0wfUFvYSkkT1C3T>A-Nj9D%LU&@Ws_21 z5wNSvNrNNT(WKN4&gBVE#OhRL9;4 zWqeWYIh1h0ujgM`Wqe-zh>u9v^LCS{0Rq|w?l%791e{VQdbD>|YW(cV^?xA7H>~u|~ zIT&>_lmz)Of$lzKnb2IWOY)?Cvr>P@UUZnUBhR~tk)mj6c|8SKTgD=j$GuMOc;TYX z=V0VH!FW^{2&^u3nORltPqap18->y6Yg;!oCi2PkG>ZA`6 zW4g>GU^@P;DWY`TpJe0g8>j8{;uD21O4`$wx=k%dxow!)wb1t7I=R~eY(YHjf>QcX1GcHJ1QMpI3$O@FFMX!QbsK#{9;q}ukd66u-4A*}Nww806Yf;mU?HwIlj#V+z2qj747h~^? zeA6NfoO(IH2GZ4i`v74@=YaJKxQJQ2GSDxn1%zb`nIKAt(aQFBmDnEG|5`sa|-+Dy2<4UVZtci3zLrTd383L zSacx5iiY0B>WG%57~w-%G6}a5_wtIh<944d(tncPy!eN54Jhnl-wKO;swqcYm zby{`DPM6aBwjM}JjX)F`3X|B6t22t%^xejPW~the&WfwVtTO0uYaua-;+U?wU(IJ^ zuwqVCW2m+^?-DUJU6Zjfpu=H!8ZuJ}!lp)`bmFV{Ca$Q@|%?0^eSHnb70A_QHZjV$zHl5rgC(z9GoP?_(0%hU;#7T4H+ z{HA9LQzIQvnW3W0eK}p)_N_j+reaV@s5JEclKD+kM`J_xXiWwK zhet-NB`y|gX11;RegFizetvWPX*q3wy+`N*g4A*!V>$r{HiWR;tfi7;mJhU2`Cl{Z z5NPu;>--7iSPV~#s#o0}1lq;0cTT1cxP{ zX*)V~t|fJC3nKjKhW|iL_4qrR$D{;#a?tz7osMKVsDI>9jZ2{qKyzua(m+#k=aKzc zWIDk{IhvTL5SpApUY-1Bq=;Qx`sAG|CNAxB!c+s;{A@yA zYd)!mwt2W?nEWS1PnjuroB7Ae9YCdO`s&`i%UbFnBCR#r^4+8->Y1L_KaiRS!Ujpu zm;7mzAskK4)rW)U$PfR?X?i)qZ(b`m$)h8m+fA5`*n`z2U&=-r6HFr7Z++}22JIl7- zUf6@zS2q{Wu3c zxpZguJ-yS$s)QYre5c-=s1EqR%56~!{~6Pq7kTr(4GCAu}nSvqh7%EZRZ0IK+4&_b3B`*7l$4qb|d#C zjrV^;f+cU*cMu5&IyTvA4Ny)sX}NlZ5DUqt1ksaBOBC3a)+ra9J=O3QjgD^hM?l30 zh42NsPJ1B=i%N&yES4gS_rPwWhQbghKh~rtY1(0C#F2=-)a0*gEw{CH%>Lvh%Os_k=#wO6U2z?G`VF)cgBBX1*X+gou{nQ`Z|>|dw$1>CSprX#Z5h$R7K*`eC>Ua>!EUvBEl5)TTil_!Y9ZW=`%es2IaP3N#uC09l)sRjRO031%O%O< zwB9gL;cc-$i)z_XSphnAV9w3_46{lq9cDh zNf(G>0ZuB;N)6YMNp>0zWVml`F8;W{5SMLX%k^Q9*x2#{g1m@Wadz?Xvu$nQpMWQF zqtH6ZuVxnZ5%vRkOs&<$&uT*U5*D^XPuL1jWQSVt^vj4>Xb(;pKEx|QfgLqYyCwi$ z96Ts+8%1)~+#5L93P{ioe9cDU22Ot?*a5yFZqFvjvAu_w03EuKKM}XE<#}E|Zs2?b zaU6ne;#!{Xf-1kst1v|(VG8O4OiHh!kvwvBz-AJOf;yG#Y(Wv2DL!rrYTN4xVi99! zcdB9k*c7JZ#O#+9=Q_yu+(Fl-Jl+o{c3qY%H+_)FJ*7VADFYMVI8%wqdM$r)qski#QjU)|p(&c#^S~)6?4aP~b!c*?+-5Jq(wQ& z0IWf{J1YQmb3riP&ru;VVoz0iCylJ$vOF6(cZP^Erwx6lUT}+XT!6f*gTyJ{+}Q+V zO4@r}nZjFt8Joku{rL8mtL2-Pm!5*mQm_;O$s|mUl}aX+v$5Qn&q04BdJ)8=-G@i~ z?vf+R2US}&nw1$C=R%Eg2A@&mh?g;KDx*_!l-p&p%kSvBFU1j&%eec}uv_=*l;*cz zd55dBjEdbJ%EujI+(FSFEA(>g(NGVMmiCqP@@18|67H1&~% zCYl)vE}sna;mxk4$lHIV=^9E+JzX7l?Nx8ZVG~u4OX$~Sk&G^jaAqkEU7qc@K5P?# zI;4VP<>b`Du(tj4Pn)2 zaI}ZgwhP5lSTJ+@U*`pb5zA|GoqsE+cO3xEC!n0kIY}jo@D3Lli~LFBsSDvNOS?HU?rq?703pF!6EUq&sWQEeP%YMVWnafPB|0+N*HTq+R#B~T9Cf%zmAAmzc16neND zq9{^sjD`>vIsB=RC_v9jFQL1E%sQ8+`MA6kpd`Z~QnbG>-FU*F!*rak+<_)QD=H231p z?a)Vl=zqZ%acxg(ye4OM=!Me^uHxd%cBd)d38p1`S&4Pa)*IT0vuVC-T2hObwiHhS z(ta`JmhA^<=6ROwx+n>yjb*!0H{^9Ru(XHM4ghBmgNz6zek5dkrNs)g`LRE}WXo@k zALQif`xxXnM7~D+rKYXKTeUU*+s-Ki_75@OVt)$iEpBO9XrLTjVo4 zpc6XNh4vO~GbPfi8_ZrB@v>rTuH}NucBE8^_w-g6M2j^$c)>&m$@Fr0rS0Xy;E2?9 z`6NHBYP=;)PiWW|Dla_B3mYs&;63~1PJtP=AVo^-wzN}Ha2C`h&E(zdzaFkkDiA_O z&3|_{j~DVPER9!BtLM2o^XK#Y%IKEsd|g5$H`@#8>`M$g+&H#tHx)_gil&D^8g*U% zP74`%2(^I%&WF%_CRI%u@Dh)3OZTK33T~V6y>S|jcci!^8sDD0t`A$;v~bgXnzhGOv}y(%&Kv*RtHygxs|@{CDkCp0L*sJ zyDT%qWL)Lm?pLvt^i9Qi{#I5{mE;@TjubhqnPme{6Uc`1E~nq)&O5xPq_J}=>))15}6i8hkt2f zI@eG8Zr4XN<#}B(YW@bfK@l-#R+^U$Y1*+!@Z_f%NO1=VO>nvyv>lO%_w0j#d%d_H zs-kj`su)|G$y-10omSoVK!e z^Ywe0x9iVPFq^6N{meztf6436YMf%@1vf+h1(+cU!(=dx6w0+!$xz{=ZS%Uot#k=d zPN~Y8(;3Et<~8jNykhePN!t>JBz_E<6ekHZDk4+#MSlYSbduu-bTv&K7=IRw5$yN% zMJcj37ettgmM%Nu?Wx$kNFvuRL0r*+dVCl_PXaQc&+ zHg;@Sb&(ebD)v2e-G0;vpmocqBx93d+2{0Y>9!P;G7PI8io&2DtE#MaIc^CrDC3lO zV-)%D@PY9&aeHx=L@@8-e18m`@1O)$65Z+X-a5#_aciVhVNNW=!_0voj#=YHC~|e0 z=?v?Q4$9$!k_fh@FeG=XPVdKI)0t~{!YIdB_6#NQpsDB_dB4*oO5pSuJ`c^3L;-vN zlU4UP&|i_toxV}zgsi@HE@mv;z%oY{vIY4m+gV1Mw5CkpppRbv$+xw0iAc6iD-(}qxK_4W@^EAJn-k2s8@@z78bIxJ9>;01JKk8ci_jc0dr6Kj6WJ7Pb zVFyZExE)5dPj@ zVPGF(12IeL#gexoK)0?#2V@1d04>lK10zwkT3Pf&%4xRlzwh`ai;||a`I1Poc)WY= zx#y0;i`@b*etCii|9`xB^7Lg$7F)F41e?W8u^@4ZHc7nL#0d(*cyW_2-YwsWV^zwc zp$E2FhiSNcrmwF)tbab-{Ms3N`Z7rSmIy&o|y)qCFgFbFWOU)?_I! z&0}j_UBa7Scd<^vSP(AOU}zg#S27N(*Mc2sqgGSNaIFryC|%EtMeV$HqNb3a$=jl?g{E zB=I9TWTYuZn>6<09`VZP$0Ua|w7?^)E#i9Z`fIj#s@jya(Ro zecBLsLVt|>eyhq_zKWA9yY}@Shf&4qhkIB=5KaOIz?-I&*28)Ni?%NRQa-kpB!p}c zi6dWbojZ2vtzeoAC)dZErchHMyVCq>pPCqSA(eH`?>r3~kmXrX+87@J+DR&|=uPy6 z1@8Pnlz%@6UVt#^r20+IVYPVJaWpO}SeGh;VSj)T!2^Zitvt5!8=`V$kR5f)P=d8$zU|wcYf+N-gSGoDb&NnZl>=~HMMT{ z?tfh^edj&1{!DQWUif;f%T1HRlm(RBBjs_D7ow~z5kSKf7e3EZI|ABco?U{nE>C6N(rPrwPk-x@c2~QqEvnbGv)`UKDg<;+FeYXv#sJPC zT@!m%d&iKc@Lzf1jwEi{H>N$%g+DNc7)~dHURH6MJFeULSQ+>FA&mQNCflYPB5fW0 ze*MP_Kd*96CnrS+m^G}MG)ilzVnTEa^|p4%I_i}7>o992FN&|4(Orqarm)IZNq=(? z^97O;L_par=I7AN#(t-JUhe=CuCwq!@mjNH4ouFT9jN$ZT~n723^fT#aW@EG>!p3z zTQTfSJ^yKyDTUH=E3c8ELIQn08T6=E#d+Ev2yu7(G?O;dWpJK#4xL9Xv)v52XY_RM zQNm6YRC0^mU;wluF6T{N*3?z@>wi{9aBS_Cua8=Dzkiqz3Gv1l-cbV1&8zh$T7ERG zwA0bq2_XdFz{jzUPMxx*yOW)?)Yl3h5|>Uj1zAgK7_#sye?MyAOYN+Y&bOD{VXd*A zy!BJ@KVJ;Qz&~-j*G)5{ z^DdzVQVeAh4I0sCE`NTWf^QlvI`-p4oTfH>lBC|6pKqT0 z2SMED;FodS1QVC6B?cA(HZ_xxKq-HvS4)rEHW0q&SNJFcF;^6+R~jskZjz!s1OaNG z0g@cJ7Hw;zSM~7j{`<~wNZGR0?QV0is1Z4w$M?;Q{l#wKFMfOFpFMwFzqlcEQ7hMRB-@!k7gjT&%OjSFg*fU7=guELS4%>Y;~cBD}YKtCp)sdUt={d?5>6 zsZuYwcfI_+{$sr2>SK_c>X!+V0=^i7WLo+j4q6c@|oJ_yQjqJ?$NpMyH zT+I_OA!MmMi_NxNLU1lZv#o#IQuY546Q9K*bwT-PPLP2=^y7WqT5Ca-4FykF67Xp# zMnmcW9We@|_stJ|e9iP$4TaVEuDsPPE!PkL3J&b#FZTWt6Ci>7As`-}cG{OLN%#yB zEC4Fw1p$EEpXS;`A_+jDuz`qdAatWz<+NdTyD{+w3oiL&>NbD4W?z5UTm8^qzoswu zdjr4Ls`x^o%sX?ZKMi}ox4SmQ(Dg2?)o={4GM;3lAEyVD_-&Vu0-tS+ys|C7O=KrRn}lkqcDyghR438tQ)bIMj@-V9skNO^zct#JgoJ651Gw!V<5 ze3Ip#p{rnl|GY+H0kAU_Y3P~jJfz_ieD77S{?x9up#LVT-J7Qz^d~C5enyw-=KE9D z3m^86wdUqDiA~)Mg)$8TB1^J^OM71GUmOoJ{a`Urx4_`0uClX^k^+9yC-CcdZAS%g zD>%)YjG!}zWBz~4`A;+Ut}LN^IFr*sGs=y^wGcDX(vBlJwDp>YKMNP{0sH1Y9% z8N}!Vc`bkZn8iFgLorr1DSgPRaf@DtPG=B`I0{WDP^7XfM?`|bGK#%!xb5`c1B@7O zs;4cJWZ=6hHWtU7lg!=bLYmpjn>hg9;zcm43rNpBq!@0wYPHHJ;2?zCFsyi?$qk4a zmAABV`9~1Tz+K;?P7@^Uzzln#ZJ~TmZ?<{kvaf%ywryRK3Pm#dk27cRC18aR&W+1l zhmvs_x&)%-X!A^_C1+8TxQ#d_VO>Vu-~7P8X_^scpYbkG^d1z!HxA+DF|?qW+c{$6 zh_q*4Z^{hbeeUfzfI-oN5*^4h+~)d zl%;b2NAU$Ufq-SMALrG!KCX=XGDE8X${W;ziNh!HhVX3w?PJKfPa)@2hRovT9X(ZF zm&h647Bmn+gUqwRFbUp*75aoLTeQt71m1ta)F7M`S4Cd^SOOwpjrES>*jP8KxxhBI zfJz+MDq3kM=v0QKC+KkuE54~#)1r0UrRprb+8!gvU{U5(UgDOaB*760_;ywpu+CS< z8)q%HW<7Z$ys!2ccd{ci} zCos~u1;JoRE^Xt(Gm0L7rm69+>lwTm)@_M(C)H1gDKV)9Qk_v~_F=~yW)tZ-3!cSE zhfyI|5{ns9-HZ%eIo9BJGtG^B5;Js?j!xrW$qh_rV~j4?HO{oo7e}iT)2CC7NSTab z8jP`KT7L03ot*iwe)SJ<=@gU-Wp0;r+yoE-GM53(2^0e|HaC;O(J6nG8EbRfxbb^_ zh33APnuat%f)C{$)7Uxt(o8#r zIFG%*+M%n8e*N3sUoL-dR5IHhu6&gTah~zUyS?Qd@60;Nz2Dcq@~XA+Z0}z+Q2+Pvc>kBhXgQ1Zx5dq-K+Q*{@WZ-{+xUe<3n6My~*C!OR?>ho~3(*#^7DvDuGVxqPaTw%b z?v4!Bewv4@C#@np0|C%d&xq3g8pgV|B@d0DnJHK&dIeqB&g?8Q7%Okvbg~|Gg6mE? zLG-8B{6*d1ciVs2Qno(qZ6~)ARu`AUm1rWL*g$lwz3=&RG>=Fk4LwtIP2V%N0Zuyx zXDqwCW%&uL_H)2nenJj;g>0yVs8dY1WO^nfAk!gL z1HcY#NyT1S^@FJk8Ew1f$WTi*)_|?TXFzjSn%t*m@DqP8BimHYzyT;M@93Her5dZu z>#JjRFx}yaSHR7{Gi1)xCDSWEedcLALymjlMl+V<_$6l`nFFb@#~TlFcS6rN>;pPL z>0qPv@)QmqpwYIsW7+sOFiJKa96iAKRl$>}x%TH9i5P%DZQwdni}n#IVT5NWCIGUS z2R6X(8Ww*!{g$=ufjK{kV(NDGA(oLqC2GJ-VcLPuhJmOl@h*mH zx2G*Ap4zg|;pc+qiAmfoc1!BoICUlSbLne)+KzO@m{Kq^nxYW2r22wlK}<+4Oqmyo zwlJVwA=uHsN5io=d=>-Qx=Hh}a@RFu+cQjZ2wi{l1jOtVJ|UA6()kIQ3)M`}xlV&7 zg+LpeiK$=R=O>gg>{sA_sfKtoGHr+wq!5@ur9;3Z`FQ1-kg38rh@xy3Gj$Ytx3-3m zcL0}yoC3OuG7uAJ(T`Hlp*`3FT^0uhKhZf~P@Z1^)QmNXZ{X#_-VD5BhmOc`V@^s* zWUzncUaV3WT->}87i6JB*b*n&GJ0|%ypGPj8@xu-9Z{L%!Ms6i>bIKJk6F13$7LfKAy9~OLWkTh}Zrt5(yZ?!55*LKQ34S zDA^gOKxQM|MT}JPF*|lV=J zEU2cD%LA!$OeoBzerX27BMPD!1<^GcE?5(uoaQ2ESn`oS%tCY`#9o(DatkS0<1`mg z=_y&ekgD*CXOtwSb;@8p54Y^6M;=Y9PL)x-Oj3W40U}s8?FBoBY8srcclW?b5Fc+N35ps`zuBkw! z;6s68%U{KxsxB)iH!Y7yU8C7X;BL8*S9c(J5-A<@V=oB(k(63duaM%4r#{MC_i z6^v&&2sS`7r+^1*hjTW-StvXu0I+{iA!17^m@;>J7q_h|FO+1X(DN04PlCXaq)Igj zf@&X*l(bsT2J@S4k7G%Z3Ti?W(Gz1yiH!e3vY1NbXLrKTqRF0O4Zd$BQM2baQZMM4 z;(eWG3nk+$T@Ln#Je=K4yU3!S+Z!$Ic~LWGBp0@ut|TYY`T4@@T#%VkbhCeO#r}m1 z8YMx#iChM)*N`3uX}~yIU?&FRn>xaGS3h6B{Tnx2*zp{(5;*l>4=@C`;laf{=nD|T z#eZoab3Xh%3S_fkfoyUQDmq4(1Kro)ot|W}`9eKm)r^98piGL(i6_SpjHx1lZ;!Sp zYxrgW@MQuXe8x(-8F^4p*#dvS&JKKpIcGmm`e4rutc359oj{b~am^Vy6 z;T#z)OtPa);RTOvc-ep0%b`*Ns23a9Vi>7PIfV|ewAa%8jRV(n*luw|0IWUqBn+)x zX({+eb8HXx4IVSSL%HweO_a~yMCn0SZC z=i$tESBOBGDxViMo9yHz0E?Gwu<4<~WRp#mCJk!PaLM3zZAZ^Ck3R(9i~*E{sESZK z&)rCRL7=o-GalR_vTs2iRdz<_7at@PMG3nK)d~rcrg{sY!k_^$&bo!MJ`kJ|$Bcbo zdpwjpJlzWhbWDGs@fK3%Yv^+C9KlIO$s{9y#(t#M>kQuq>EZ8@$qm#a)&1H@F`|kM zkj2>jKEpb>p@;Fes;i-*BWQT)+HA{P`a{#6)H8K$0PQ+cj&3WypcSs!!~Gz#KH#UW zw~#05vyoYvEgia-HUNYp9)QCS3{hH{ubW*BhY(FV%-w%FnBKE+bfg~ErCP+8?bMHv8-A~)RhO6^V~XgctK$a~4B>UrjQHAH$_qo| zLc3hCtB(8}`-L3U{$F;T|#G2)p7PRQ$vKC`0g@V@rkxBf5G-9;L~$qiFv31B|Bf0bnQwISp6a zrx&8`vp3%;3Aszt8*fjKS%_J<8cua~2W6P7f7n-3K?QT9IK*$MJw$-s7I4lrh)H;+ zLDYZzQi_vuG+<^&{~}2|T&Y!u861JX-@bx-M9Ct-1Ln8j+c=zf*j`jf}m$7*W6qnIB1{MP}GBcO4a0M!VYmeKw z@q2!SffQJ|SSfm2+M)%n>21-XR|M#yU)myIEZSx&ORgyG<9_|ki?XC>7tMXJha8c^ zdC!pH=C}zre|{GV{NuyB5BE{FDT6YP^UcFy6D3Ko%Zp8(?SeSTHV@V2U*6L;EiB%$}0hClbGaiQ){?be62)h%Gk)Z5pC?R96G zBd;(AhW`$ewA=cTXM09;gHU@i{b~E3hrfQfPofFOaS;Y-yaRcN*Znq$Jv<9QYxRW( zra^RXdH+me-x2VmC@3=#xoPqEEcfh%*w%Zqjfgp2@rS-;c+`#VRIhP=mVuTCd8yt0 zeM>6pm1$U3z*V}jrg!=T#3PP%X9vskGb}!nX3w(2*j_bzjr@b8GggU_F!q>H6cx{7 z9O;ZkjeZu~$sAAG%4E(;n1F~`*q^lfN@82I2M*eX7acowIl;;Z44!A9_rvAVwU^GY z8erpo5c(~sEU{~BA6hVfbCQNv+599aJ=Ih^aGdRsGeqpc*=Pf>*D&e}+eW^l6sMM$ z)xfN*{-UfFlC7W~jRluwoM}c2_oi)rhbt1~|8>sosU2!0nP(FYVB$C+_Hyu@mht^b z^=_nJcw}`AC-Qi1IV($1j&b9O!jJuAdUA{Q#i`;W(YTD5S(a9RfM%!C^0;FjcgS0krjQrJFM8R^PUgswlM%%UE1V_LBJ@7iuyf#0heW^@i@YP# z7HenD+UZo+Fu}C2VjFdYKtszWujPxh4C1tK>B0$%4*P9x$ipCvO1D-NxS$u6L6VQ@ zM?{5W^RiAd%Yv|f7?)e!2PB3c`K}i;%0;fHz-s~|Q zgCnOY1mfk=cVJ@3Azx*?scTu^_xgP4dBRx)h=By;hUOsT+o7)wBq!lB4azi~vuPSG z3!FD#FwZK0c(n3i^evo8mW13cL5j$>SzCJO+VkQN@PKB2EFTKpQqrh8>R`<`eyE`c z4dmu=BE<aRfJf4R`@5w>$>)Ph%C-&iOZJ|Fhkdg=}4S}>Cm79_c@<+Q|T&LabY+o&Vr&$ zCSI37pnep8=gCv=>nHSSWx78Zis~YvVpz*cNao;qEAEaW6hTrH6Vh=Jjik{pKaoQ_ z()iVphSE$*LEIP9s1DsaE=zU-#Oi0Y#{|SPVS~n%mb~6nLIFJs;nT^jadp?YPhhzf z#m|E%iLOPz;wiC%gHb0m&O$KwUZz@g7)Csq9BdhXb5cU9X&FMm?=?mzPROGG-dLj_ z(be^hl;1QdNUJMszpmq?yy_Uo5NRDbA&j|Yz>#8*GWbHJxR)I#ttt`*#>*B5ajX&i zNHL%~weK;95boyV%^JTq4>xFq(!2Rd28Xt;TO{yAGFOcKB8dgZ{fWyxO%~=XJt|fM zbyBW>ctl=DT7#yA*FePYaoK-)I?Jwc8B$`z52&x>(8*CPHaPV8^xEOZzSgZ_NDfwW z+_I(sQ*$yHo_TsBDdmQz7{C(ii4aMVi_0J}KGLBM+6p%1g{-okG8-lh%jgq?5A=AT z6mwSfEq~O(F}Oq4$7_OvAMWE~+Id4M2%%zs#UNfJ0sb3sH%N|c#nA5=K1VYDJHwO# zd+66sqhlg63`4YONRSpM3^Uo43h^*rZRc0G`bRs{QF;xH?3>VluVZMymYG=*!R^Jw zZb~|_S%Zdpd{*a+BV$D4F;}LwD*E6EN*4vI5Bf<^av8Wfawn#&wB2`R(g;sH0diq~ zXP%jqH9zZF#YmqL?oA#fK%{#T1x#^bt3iQ)50w`rcmmJ*~-H86{{Ma?4BuqkxTs; z)ned5dfU0Gawje2nJn@goL10+Dkx%I1;sH3I}9A#I36g~_TIC)X!0qn01%zvd7m{$%JRv5+zCzv(a2_*| zCZxQk&mE;tXeERcSr8!FTby)%$L)Ao%DoCcK3{6HH$C20Ul_%TAm$QeEWQxAr*_c2{pu3`Bb|mPVG;g4 z1GJ`dpve2&wNLW>x6AL7Z$5(v4UIpZeqiu_7k93k`S_&BlM7UIzZzeEukb3l<-!3U zN9DnoA_XA5Hvk+i7g4z1;hTs&ci_zv33W!{O9lJ-f)uvS)EMHVUvJ7QqmC^l5`5)A zwYWzVdF7CF;Gd;)=VvnXerUK+OXxvq!H-L+>qF9P()YoHT!M&RpKC}`eoV=D%8{IBk+`VbwVk3V0%Bd?p{_O!Sv~<6Jy~)C- z={U_7-8vv*2Hg5#E{^e?`@mRk@?kK^$xH<-ggKd~bX5g-g!m#y?sqsa2ZmC5MxU3jmZ9W5zEUFo*tqJ$ z`NRZJ3;isFOx-hVAc(74N>Xi0;a11&5#;GcD6s#!?q@ zvq3nen$oRl&wLg6(cWH+!LFkQZF z00wTRR#vqPJIo~XJ^>TrJIvm?LWP-&Bc^EVbSvDxI8pzk{MTI^#k;%vTdS&jQ)|?! z7*BLO(!hlQNHOYStYw=hPlKe$q3ue7G?sD^713&d#q@;7BRbF!Y_(#*s>iI7*x-jD@d4XH__B zSNbAcET!~AH{acNeb#r2+tr@~ANzlP?I%tu(#VgHmmi4Ke~X=^@`MT$vl{jV*jds8CI+nKUpF*#9kIdBg}8ZdKGOC^fbx;)rDGg0J;C<);U z?fcm3hqBpsf7LfWzttJaRIpJ<#hPhrw}KY*8PAdl;Ye{(U~}eF;F?U060@mKiJW)> zoN6Tgu+mUvAM5rgz=zEL=Y!)RK3kOs6+R&wmaVITDKX2R~J97B!~3+ zdXYvh2z2}UqAZ?HN0khO^pZixxf*DAN=4+2nI}k})EDvqkejBp8Lcaey-n*V2N~h~ zT82~n1(8VnaKIt)_#NWIKqiQTA+9l|gE-_P@I&4J`hT-};U^g9_z6=1Z7H`ni%dux zoGZxAv}%%k@k>h)gj8AcipR3QXK9r>u=YgPb`hqo#-FSp?$7lLViACejCsF!q1S0` z9O60BY0)c4e0qQ8te1iG+(<|XQh2FLlEcg{yVFc5Z{|#9DaB$Lf$*QFc7p9$Yj)+< zo&}HDfPb(;LnQ<=-ShO|F&tz)yVxi&(|g>elmeSkT~I2Zh@?4NERcrNwwYN_E+m#N zB(u(buBryvJ$A2urq!4N=5td$=(?YmgL3RVC2Ts=m1nJ@h?42AC+1NF`JHf z`ppC6i@PB|^_X~(+%}YoH2S}tRR;_VquE9FaCdnuIA^wwP2s)Qtv0bS%|y`prQVP% zFB(X%>KIeyi)idB&`G`msN-^KD{!=Fn=Mml$};bluOBIPs!&i%p|UEc?H&HT+p+_(b<9{ z?G1mZAT-L_u(5o1ZOQ44rBocc_+^Yi9GTE1j?!J-+x?q;P5C^l0AsNH+c8L$CK4fO zmV^=K@L6yv3Z}FM3FaJZA78`OVpbK zTjJa#jUFN*j6@I)`S$ASh)7uRM$ll6MV_2vk-EC#oXTDYO;sGv4fc0~rAft*3Rrlc zxVj+4;8n3V)~DF*d4_Hau;$adzuq#%b*eL5jGjE`nwI>IGynlTk37gTFtzW@x_=jn z)e;m<2f&8kca;<}7y=Wa6i8@E@DP8B12x~1Kp*2huZOzkE6Vn3MKdqd$J7%^G^>v< z7e^E$5x&O)k>l`w#Et_hh>jIU{)R#QTzu(C;YWkcWbnEpnU~i8p^*N85ns^74@EpS z|KHICfCjq63^@j2FacqZzMu=o$$u8~8S1Brifz+sW>e@sE33{nonT_A<{DftpaZ5Z zKmy#Jvh5DjkA@5wK;lD$;Rn+f0Y!Zg{2z2&Fhd=ejBR%XGmQUJs}sWiD<;wCM$jo< zO@gIljM`<8!@m8if2u`LN+hv)jX+(DZ@H@;y6$&RyQ<8~{*&Go=lQPLQ-6&Yo+r&E zW>?lnS59x~^H+?IH^=hq1YQRs9ADq=Ytp*2olL%?d^vel`FOMb^>)1dj&CJ266Vc- zMclL``Dkno;Le+j&qtAcK5AM@SfTqL|KWt@hCIg< z1{U}kXTsG26cBKu3-PXE>xJ+K$o`1$wbaks5yU`ex|RyYoWt$FuO@`Tgqb{7P^yXL%AO-fH9V zIAci~dr6$KNW|W1?OprLzF!0)6aEE^K7~SVRbA}Va&fczd60E}6=hRFA!kv4l*5u1 z@mpKucq&X9udjaPxj}0D#fgjkdO9qcn z=Kktl&ry){*ElZvqEq&;-M;H-bYDE0@`TF^@paa`;HwL z!Leo#!IwM(2{Fs10ly|pd`UKcy|yo+<;|=?g=9$>TZ4WaaKNXZ!Z_^Uhr32Ci*32z zk{J}4lkrG3$wMmxur37uOR zzuZ-i%ADLkRi*moKv=Qwn*a|y0{T>Kw=}v{r@TcL0PWXCW}|`MtM;jX>WuWVZMrTP zMdOD0&2GKk7I(Yi^Q;SH!t%^I@R#CqwcGCq1AIK^I^z^_NE{l6NWaDnjqySEpvr1f z*~E!r7ABEvj>1}N(}I7D^^4XVuljtE*La%1x9FhIUHI9UG_L z*;s~Prwq)P%%tlVCyWhMmG{Q2+vZ8N%sACLAZ57_BNO~;6Y&zGK*Q<2owHTwI&mbW zSwp29Vk)tQLe{y&)YQ3yXOhYz5v_&{Hn5Izc1U!s9ypL|2Tp8%Qx8ni7#dQAwT*o< zFhx~A?E8^-27-KhSdSJ7{vAQ1bv{^X(IKH;GtbKmecEQWUfN84vw<|fP}p|bfqi$1 zMSq7%2ed-xKO4waNN0{hecuP~QMw))!_YY!wiormLbRm${^1lB zq(>Qp3RD!AI17iOgDV&`MqpjL`IH1myz*-fk-8HG1q%0M=tLt!(IRy(%xY$`AtHcu)k9(l#ODTyggphuZqMz`mp|jRQlZ(qcT+xj#uH6?neOyGT zujmbhDh#N9MZ?iMdLUV#1eAwLr9R|6erdW~&_sHqdZZI04OxPDk+Li^)&TNK7sz4Q zR~7Xi7_zbdl^*&{2aq5KdM9yTNZ?4aJOnBfArn&AIM-s4KmIK0J9};TYWS$?`a-uw zrTU9rU97=47~2@k&WmViNfAHTG(-s(nvnVy;zQqma6G{opm)7~-B=pEFgFpn4#9Uf zB;%S0e{#{eFf-hJR7c!$G@Lgn@}%h;*FkAc2d=xUi|z9lTU|ygIYm+H$zFqkQm6Dw z7A|MO$e5)mKM7{)pc9y0!D9qFC1vX0x!xsanaECFpWL3VAh`2Dj5PORH-^>!iKD79 zlJ#|eeG)#)(Q=$2ufQYePWc}HO_=My9OLHa~`A-?_L{*g;dL$F+}_aohwGmvK-A76Ldhm$AMADSxe3S#R4$5PtWs;3L&WYwpFP zPz*S295g_RwsBemic`=OC9~n7kd%)m`R|?CSyG}bGfmqETYJvVeDlpg&sjO1^Ww;} zzt7H(j?biWIA@X8!Z}|$TuK)Bq2ud-38|g)g)?`#VBuuSxzKJ>FW!1mZ<2O$dH%AO z)cqcxiGR?ESmX;Ir3u9%FL0*PV^T>HHU|+++D@h##LZwrBUn0tH{45{DtA7^Nf9^i zPcG@eyxMe^mRX*bagp|0w^_NU@WL(Ko;5hh{@R|nTEUfoktH-4dvA$tNUW} zs_TDSm9_$?UO;(43Z}N6;25jcZhzI8;X+)myc2r|7w{l1a=XYF8?9fv>h8gb91mC| zySj-tfYPC)mk$aU*vY+sc|7WnKEF=s{W@(nd7G6hnr_x>V`@EZ>ntu;xjk0IFu$X$ z5CWY%J`6g{&!%#Nn^RlGZ?pEAMxzDbK$cvQbW@haT|jY7643f_4R$2tx*zMVxsm=nVV4&f0;8_tZnyb-ppp6*$e9=tK7*qM?Ob zj(7hoKS|(rG`hN_ApG^sl>!vDcVO`_%74|3Y0xI{ksEWR32w%AgCjPpn z0OW3NDm&dxDoPgsk)LGtRzrJNwAGsS$Mi*byCj)Vdgzo%IIx#Y4^z6N;B^1RwVxo`@w8v&}YMTmiA_JaAgGgY#xcN&F-8MkqkH@&=FzSyq21Ff-%WjD8K!9>)z zN6C8K*72WoZr`oi?=jt!i?oIYyEGa!<9_7$g%|LFBM03DWrnfJQQ_e(?n&Rhs%v=rhl)}dI%W2R+Z3-WpVxm#=)m@dylFMcbvEZMAx<@{*vMb4*UouCAV(=Zd6+hp@|<%9ICpq zl*8gU$~fpBwpscWHt18t$289L{1bZr^_y4d`~p2Zx2`V_@$IzymUrva%R-t{dyU=& zirFtagBfNogwqjZeFjfQ{v7YkG?8j=F1?2R0_XlSH?ja{UM!sfq}P+7epLAWj&=O8VG#x*F3 z|G+`j=NAjD6>n(BDfu*-p}Q1F8yc;DPuM~xcI{e1W^wfxo=Zy$v|y}I|At9KT!C`H zM6N+GOCwj&FtJhphWSj~!Um10+uD43=xD`_XujqKSt5ao1_`b{)a@xisbT3<%m+0n zti|;kB&0)NP^g%UC>vUh98bdl>o`~gm{gzvGjReML;#m_btB9Ilfwjm6M=OG zCcv0W?PB1ers^$x6Nioxs8A<|QbDUg1!YXv7&uXaGIK&HSt!Z+uN(@M1r4=01Oy0cbqtAr;Z)T?hMQZ(`iK^4%PxmWt5u&5^hxR%z=tF`O&|;{ z>+@NV>wxDL_aCa>>R5q)5%v)P?S5 z+W5Nn;>BY5+xhzB-Rd$Nh2?K=4#V>O>g#1VUf?I!_x_*1SNP$>=hb5Q8oF0!mlvKE z)W-V zx=R$?Z#Gd9bbGecc7et6-POm--v8zF?61Z0)%yI?>by^$j+Z|ze_p;m%Dv{fz^3qx2e3u2SI0pxI(L7A@NR7YIvEL_rCsQB3UoSua$9`rMmHNC<6U?x&xglo z0z8|-&k~-2>euQM;E}1>Wb95kcZrl}XNi6~owUTe`=8lf_dnuja7dgYqaUOgU{z$K zDjY@VvHdU8Ky~A1^wCbbC45`?Tf`B3T>5)3;q9UFrz8Ummf23~Hxd(_^jpUvx1Cl$g zk!7Fxi#ekN&w){|8+MaI$VfneHR1n9(-)Fn+a;lY?UMv#pUkc8lM>o3SN&>n5+!#R zO#JM;j@rNyL?%;u)lgks9Soa#48>gYYEV$%>1>5{&$!(w8 zXOV;?3FJy_rG}!|J&QjSaYVq)-Lp!XQ0;9B-yTUU`k`o?H0r{rSw~7|#lFlo&OPq0 zE9X0Z#6TV^WAj+ujew$4z#cdrR}}pI^fm5g;oj((4amTI2aSF2xCP{Hgxth$e|5TP zl)WC1P;wepUUKP9N{V0+L!p5}ukd3P288lcs?M!yCxbk8BMM@m_|qloVShOloYrK- zZDrb@FK8cVFY$Vm@ z=M9;Xq-7wHG7dQPxje>^3=|ep=4X-XhrGSz40kKf6{U4&WNfhF{o21j78y0tPGCr# z!e5;d7(>$iI*7;4&8m@&<^htcP9dKUr{M4hm(L zbKC?Gm&siQ7L($uD}T*cZI9bT5dPj@5%o(Qdfv0wcI;GlNP%*d5G{yH;ZR$ly=0Rt z_zl~+7k)kSvUcn=m!kzFzQpU<-PxIEo}C%T8?8s)=+zT%@A=jI$>~MlkEUWek(1GU zHS(uZA!RU{M6r-TG@36*@0?8EmA{Wa%)jk6o?ghLw<}0sF@H?Z>et(}F)nN=#;)(= zI&X|h$8IDer%bmsj}}E^>@Z-Om z<-Gn2e!yWkH=_fe`@Ol)xk4ds`mpE0z;D-|h-ol^XCS6w()0V*a_k1u(YZ>SF%0gH z0g;3vm_#iSmwzhLY>TpPGm_Tkg)SQ&*V~K-#fkx##3Gc_Apm8iS8CA|RRS(%9Y#eP3_QadYHtIwiw|l)to)20p76v`Oo2mT5_Z_qIkT(tvzzfG zbX2<4Umn9U4n@);`4g`0AhM3#52<7=HM{A6C9A5i!LkS=^A>7%Dx7F7%Zf@L!n)bC%ON62&Eqh_ZJ7EA zsEB}?tfAO~Q9%+oXE1v21!4LqmXRl0G=Hv4W6`X1jn|cez2*zEB6k~pS>VgIy3u9I%3;=KJ1M_><&-{Y%{_lh#9OG#fj+GEHYUF(84g77c^3 zGoXv+QWFLW#4Nm=zx@8~{1YSP+a#jyOzSI*c?9-XM7u})ma#~@E}y*FX4kqJ`r2yZ zT);si!WKNPD1bS;?|Tv4fOzlb@4^dsH677ax_>6E0QM#ufU^T8@;-~cO_5qeU%H>@?+PYf^k$aNtS!CqZlnY=Bpy-0T6qmCAVx~oU|BW9^CnU-)- z`+q6Srp={llvS#m&rM->873YA6^BP*odzNV*V`V$fO5={7nVa)4)7|R6027ZB2K>+p? zxtl_lqbht4$P-$_(^y_@TEtnx1Lag@P*S5m5O*0nQ2cQ@HcYsLBTZ{`!=i27X|f7J z=R3{k<@gJ%BKy|vA!lE!3L2Jb3EIL)QjVtlEXHo)`Ob_~+~Z`>Yi)2T`->xJKY#4@ zMOi|F2k=gu-&nHEm%74sYFV=YV_9o$>E(_5QH31C!H=(=jNZ|rNXSEd4jvsq%Ur6p z=8=>Gp7Is*d5(H0#O0cyVZPpLdq|w5YP(4 z(7803NtM-##K-5WN-Yj{ZHUAM4}Wf&^2O$-!&rIdHPzzzwQ&Mw^ z-kqR3o`c57DC|WA-xe&UZXS~T7#xR&rk&)@md>mXnZ#TSqXA{ZA!T6}T7--!2~)Ex zmZ3M??c&=NPgC~!PGG|KUH0uHGP5L8wQD_{J0qdb5&*3b*fo%yD{z2w4SOdj;w2oBGdIi z2j5W~0f&$ne&Ae;laT0!VtuEwGSw#sJNbfHM}YHXRtJ(uU>Ei?0)I!~NbPi_c6qL{ zHix`~9b&1PzMgJRt1fi93xCjU4V-5erupUBi@}w9phU<}Oue`(Hnvq)%U!J^J22Cs zEeD{ewz+}=abHtYW;@XZ^&e0ex=wuO4pj<22uYdmi7-lrx$Y^N&+E>2g{A$gVEE~O zH4x5xq!gzX{##+UUH*;jLA(9wo|$_-&%ATLJ@MRahq4m}o$w(IMSok}sC@2#p$^%8 zWFv&0XBFAc32wd*mAoLLzaR#YoR6VTz^fC-ZK1OD4<%HGaKPOSjx3*LJaMkKja_CH zD1Ky=6zpF;bh+^F1-j4le-EzdT>^aNe1l3GOmvhrty0zzTOBd!sQ=H*&B>z%ll z`Z-_*7&g}y>ih2wEUC@*7p-J@$b8JpRElNw;OdH{O~SNPcO3A^3SdR;ziq87H~P0e@KZ`7>qC!@0K2Z6r&Nw6r=tk*(PqeP*N5;21shVJ^ zLJ!=Fk?$>sAWF&utvD?bW92t@>E=gyPOZQccOkOjDU0lcU=Qq_y`@>wWE8yAhiIUs zvqDtm{`5*8+c9cVETMaqeF+zKkrt^Q@ItT(;;m<_VAb}d$D69mjr(_ODkEx9HHm1d z5!k4)pv+j($ZK_f9{9>wIj09!(XBa^lx3Cj0xYVHYBig@jjqcB>m1Y>U_o-=>xwF_ z;ALyjreZ%DRwSI6T}{o4?X)!@l2%44%mkORYQYmLEN97%7TtVPRyhQtZf91tDfv8Q z_S}}%HlCmRFsIg)N5}&Wrs8n|SSnG*ZzfIIvxILrON~c=OQE32sw1zb(ra+ z-u>~{3QYm-wzTOgb{1@6$7>tf0wS&Ks5Jr%lbc8sZRjj?gk4K4%+o3RAlF!NpjpF4 z{IHRR;Mp+Z5_U;0YXcOABvN_23{VsjhoAr(gXUQ88{|7su?s}k+a_Uno_%l> zpdcR6&*6%Hc)DOH!g+C?8Uq3a*=tF?$=9qJx=Z_kCCbHRHF8GlLmj%gj}Zn{2A+J! zHWYARz+?ot1~$hXPjk1VyjEpRS1tzNcA&q4IO8$CFJBjZJE3$dzi z%7u!&y;<6p@pH6|BLpG04*dci8zh6|1;bp@u+-vzC#8Z~W)j+!aR7Z&G_>f*g>lxf z%aE#>*5?_v8dH1y$dk3-MDiuA0lWMSfJJW|8t*z(^7DxbP%Q2W{?o z$Pe()(0)0(v7Fb@xSboxqhgozbx^lXfWzuwaLA~IApl;Ne*nF(SF?=eQ2B;H)s9PT zWiSDMe9xz@`^_%?{O;-DOZ(;Y=*z=*?~(D+m7SR*(NZW?|Q@1I%rH)pRF}@ z%+v$>)+Q$f{pX3|))H&os%b>nKsP8Q8(u(m~~ zwoYqlvds6i&jhMvv6ZQ7g8qE_+ICD+cMdU7@2MRgbt&KVD`08M@PCUpkV$9-9TFgF zt}}UF(S)< zwqr&`o=((RFNAWuw3}peWqXS5E!BeIz}Zn>w>AB?A0gnuCph``(^vj*XUudR>{QZL zIr#kH0(N@Azy?{44JfWmsqdoLc2-0&Q8pToaNtM+L z^(g#XedAQUVir>(&!JuQqi4e`g+YUVL&9-{pVxZCOXPL0vZ>Z%z)CiiY};xmtTfP2 z;R&H1!r>FP_MX-fQjK`kcHvS+)z#)MqAhV{<&o85(wC?31f4{;6@*|v${!^5VmD8GK|t140t)4SP& z#Omtm>gsxT2aAVAu=wRoaPj@i?VEQuCR}X%O%f%G+xtbB1^#+t7RfsGBeP!ImW$8c z=eAjz$lF)nn%w=dT$#*xR{*bnzbwDp{yMRHcN1l&yu!qflMPKVm*05pI$C?(y?pbb zYn*^Z*Z0;vEmxt}wPn>lfbn9rN&U=h7OODyH*1w<=wykl9&Gz;i=peWj2Xq=N+#W7 z2js3UWxTcW9Uc{Md81I*DzI}_sc8#rADnMY+u1%JE;%*%&{x>(tB+NG?{32Ni4s=H z+K-9DD$M*%oCv3nd0p&kW))?LoMahg-ZTFAL=H2lm$&5$k~Jx=UeejD65T@qAYzdK zK#|O;x(iHfm4`btjU`zN8M53Y^D2%u-tA-M=FUvml}R$%qpmybzh@Ck!ETpP>Kz3B zXJ55NMRGlnbd34fH6jOpX_#dn1plGU>w_CP-v#TSY6ULxez2~}$6+VSdvXX?7Le{K zFBGI@Ga|DVOh!JHJB8qI_~ts%9W~k0ZiSMGMs!IpUlxtNW#xt=mVv(N0w}dc($VT6P?Q&N==6%z(Fe+$8x9~=o!U^4fq+)_aOrlgV!R}r# zxeO_=-Ac?{Jv_4A$ll*8Z+I#%oXc#ypQ`+!Bf+X~`A~JOP|2>rq!pYkiX;)hy_r;z zRcUhtIU=yG>e&l*@r9*HyYVZ&XpSL5G-?VX!Vp4z zEB1D6H8`0QGoQ>IIT+8@w$aq!WOihg*R`&vh5fAe(SaR*?6wZPztXBWh-8yE2vr+E zVSjvc`OG^vPn{nb9>}MbRgjrHFeZ1?O`=!QHg87mG9&Wr=3F!uVNBH_J}19fQk~^k zPuAX_i1pP;uoEsnNu*-}N|2GBc7uQoHrY8HZKW#cVx^c@2&QIyfZga!)?_@|SWIF= z#l(`ByjgjFX!C~UrOZNS80&!Kr=j8iGoc0;(G+88EM>gS-3WG?!YE%1lr*Y>3k@=s zEfBPiIk9ZPdp5laeY8O_>l-ZCah^fV+`z2)Ak(CgT{B^=AXTZrxq};Qqi83%&T;Ll zNpdtVjDs&TKV3!>wQ@rz@o*4$4^cSf)$ixzc9#8r&3M1F-f+m_B*8`hyVtK`0BXe$1Z+D^4DJ@!L6R*Ti8R z8c7auntF+RxJd!fbTBob(hhl5JO8sbk-;Kd^ynl3eEQ`Laf{Nk29q#fWbG6i7@%Lk zw`x~^rF|M6m3`%k9i{h9XE_tlSK=4TielI2#bKJBZzf*4avVTGXdp@XtPh~NEgh$2 zXQk0|l?(79a0z!3xHMt|8>*8ZA9MH)aCVrJCa?Yfs;%s$wq7I`4cNbT(tS>jpN-C* zp`w1RBy}``;G(!=lqQj0CZJR%=-Smc+jla5I_*}cIvQUc!&rT7XTVR^p*te1xYq*N65gKQ*Wey z3GxtdtS1B$&~U@6)Ju>HrdL6v^3#C@`ESEKgF zDZFmXP$}|lQSVACgIszt)^Yxz$cx*76YSXQf}uwG*c<^RNgz9~ZJe@Vk9EhT@M88= zca&yz?991^p4tHs)IbB;;W`;k-FTFLj$P5zzD3%;@v14m7X)?3wzNOluo2}my)@av z__DK_ww-H~SqL)QcqP!TjjW8uTW!nD9s%gPr@M%GaWSP(e|xdwv`-?*a2jcqLhn18 zpD3Y_{F*mUHHuS|%8d(HNmm3MFQ-y_laOmJeO~EqFg4fV$`a{M0dsZ(H#l~Glmz-> z@v1*?MklL`X{8j^Yokmp0G-<^T#0pTPc}o51#Vv<1p%5IqvdxBHuZGA3N-{b(Evjvq!>oPCZ)3`P(pxD7@VSfgcF?x3>~SYF5Hbq1KOy4}+s2 zP7{r035}qdV{`)xPMS^IitzRO&T&GGlnp=GGT~vE48&8l7P7 z$VfRCdw|spBm1ldkRe6_!9Vc53r);htW$p-y~t18gDFS85Yjdy>Zm9kTRb}f5>xPX z`s|T&l|_C`H*WF;A2)Dp@{iphw6@xQhp@W0UE++tH$TK@=QI9*sg= z7gKI|v}S^2a>tP(!I_?aIJSn7lZk8PTB(a@79Q-^VdQlrn(?oTl!z7ge4_XNk29Ho zzM(z>f3oGSu;mxgEuKms^Yj7>PiXu9*HZY;SEit zWyJL;ijFlZ(l-wP7iAWA!m-T0qk)}y+nOs1(!8KuG7+w56kK$xAqJ(O?)$Dm$|oi# zEgt4VJ|}1%^{JROk&RsX4>I-2qluA66XDVdLfSV%h_CCW$lYhRoG^(g922t6#?QvE z472l_A2=EITglq0DuZ1BXb>lX@ zpam)*4@pq0#Fa#pq6A*-x=sJRGdsH!smQ7mKbYLx?3pt&XQ*(tnT50OUxfDg?c&At z9gk-@%M+2z7V8<8lBG#DOX8FX8P68Q?7esM+UJ~m_rAzH-KdTB11VGQcJBu;vu}!j zvR0jL=Qy2ss3P0y;VbxUh0&8+rSzpGmsJd}lgHs!d)6X%oj=kQeI-I~EDT4!iujN$Z2QUoGmd6-U- zzuRgP=-AII47T&Z8ZF0qZDq zqogt8uS*ldkc?(@k3fpxR*|wu3c8gG#s%=rAzDRw?@JDa101dNa}s5sJ7Y4-$Rb#A z7EBHlEw{-~v=~@Eck^PH7xU)^^Hx=>zJdsm*FW`DwIq7~41U(-veFH$pfkWAmScn0 zZt%^3LWY(=AuM*d<-w$aE2Z;)>=vq1c3pSvbBUFfSg<6!FX0mkP)3prt=HYalkP&x z-g`3|^9dF4(XQ=#cLd(nWu@nCrB*wAT(TB$a@bM0qpjK<{wV7XR>sJ|Z9YPbNu01O z&22$Dj53oJBd39ZH#IbIorkG06Oy6V&8CNn=stvT*up=XL+wE$;#IkSel(?F5(#31 zQK3<{CzZ-#FwLiOv$VeVtuELluL1$5cm}5oAKl8_CPK?Tv&xwYVwh4meO{1AZwQ9J*@A9dzh4 zW8eezU876_wa|ceH1$w_Ru#Q1HANzahLOo>Bw7$&axb<*Y5g8$Ct7x3zam~$_c#vSRtUsUij16lyQZbx-ZV+PZ>wsW+* zZG#GvY19u52lnLFzO$MY4mph7@&r!BV zE1QXQBVn;%BIi?o)=+l3$AeA5f0n8(jRbz`nsTS$-(R+Pn*D=Sl$)#hr!{m&qasgk zaV-HpV2+a?|0N^=k6DtXCs3vZdT?!K@W~0+{PRvUX+tormQO4!m0Lhl;=EDYtKd05$_%m z>p}kyu8f6QJjI3iySfBL)7hb||$$dQ*Aw z^*su*vgOc!LKKxX#J@Wxz-6aF77{crStK|aA5|(QB9tKp220U2sH^$qRvg18dUB2s zn?gtg@ep~1DH`I!HlI9UQJzfQeUhNhGTxD4(_UB}`on>mUjSQGz3GvubH8dvrjCt% zh~x%-EYwyzdZQgnZISJ5WKpxA<8Y`6?-$b;Vx!qT_xjv@SUMw_dwFckW;WndR@`&t z6?xazvm*_rvT4zld3zf3Nx#lBmO~N*kh+=c2t3OsN8T)6{0CfTM3=#<2@{uqdIlE) zHZqrC?E)!(y%@`K+cxl?uh2|;pkfQ=QzRQF9m%enb|!76^-K>*4loHxbVxwu!?LG8 z-(4&~k&0|5J@leoF0k(h0J)R(#GU;7!aab?>lbHNfj3E6I``+3>(#_dTo$FlWFE!L z52DF+KKbCRy0WZzTNn4!S&#&dtkpE|on1qp=ij}5G4xE81KOwSUwchwSAH^76DKT* zQ&`dpe(&0C*G^}VA31M%*W_wC^PGCMLK%~pH+PvIrhs@Xjf_!w(X?D2#@GTm*`YR)8>Y^?uEB536FM7?A9sVz z%`vqv&H6yQ>;u?{7Nn1}Ix3!72*nd7ATnZU=#j_|l2^hMg)(`@50GZ62G-wvda_zV zpSfONEx&1j!$_+^{p7smUu&-wOa>c!m9_AH{G%I$jcKhSxT}j+ z3@f`{VJ23TmK^bMA-C#jn~lOllljdz7#+`s&(vp37JQ>IlT zur3In?9}HR^nuk&zHsn{|6dL+$-%|*4$hx)@a)QqM$Iv2i60sNV&c5uW!9A()ra7J zo1QFT5<1O8RSB3x8PSam;CVpnRwFIz*@uXdhTjq@RU3*PY`_2$0^Nftk))w>4GM>2 zz&KQkrYl?6=%=ZZaY+bNm>)ZWJ{mst6X$MMS*^7W? z?|wt#blWW;i6iP6*v#b`B4l{&3`d-Q1~OvpffuIk8Y3nvuw>B6AP69Im5@!LBu>gO zq!{RxQIsLcgDC4|0yvJf9MOupDx{UWs#W#Fs3sq&O+RF*8~1gtO?^w1^V%Ll_y6Q? zKf>v-I9&qp%&;e#F3-~i(mx%mvypsLXMb%37D6yjoRMH6X_o-%^3!{$Cvy6K8plO1 z_i=$QQx+|j!VdE-@KD4R9KeJv2x2u3<0f&hn3k!C31_mcQ@s8E`i9V@wU zNpMptEA0)2rx{ns#PgXKfSUdsgzuQTgF~SKfsx} za1PUAN)qmAvk-@ZXrRz?sJ<{VNkFo_f*`IZCL*TF=mVy7g_VK036!EQPJo7+5xm@h=J1RYORI9YCiO4oe~` zD+)(y05ioVT(gSxVCRZ|ASck)cqxmVTWl#jg|b%F4^udzjv9T1D|j9Raq5VBUZI^Z z^?;DtmXDJ`V`%SU}VVIuiGP-HGH3UA0P#{8pzI z_nO&QW1Tfb8Vk!XQ}l_hMcVtm zWxr?t^@QB=x}sB%v9rtfm+#pY!wl5;m}~uW@J6g(d#~X49JfRI5kGm23DvJfnFIa` zzzMGR^a7ZQlM|YMU@i-zWPCuCisxCK4=qCe_e^F)`{)JX_n>Ovf9CQ7U;}$q>k>mCm zj)6fCGyU+h&LgJ{06mSx3i|Q(Fg%9vj|d9J;JUsN z;2yUBSA?x)XL6u}*V^YsFOVH>L6 zPo#qf)9BXd1t|;h$O{6N#DTeqBjHV}RHS8%4Ci4K`JMT}hd0s9~&fgK=iBTN5fwR~+gi;g+krVhKa;fhuQs;KExZ~Ay z=DKuJPu*~`-@!*TVS1?8=;u1g)=htw)VvUSAY&p`%e)D%E0!6#$EjSKWkzA4C3Z=f zWEso&luW)WC1^VHa0Is5bh-HR{2e77m~IfF(D%U!1<skOvw`)RLWp3fG??bbfVnb%P)t$OJ}11q1g1Y>HA#uh{l2c>}+5 zDNZn!@277G*3%Fz@csrKr*eP39p|u!pPnk!1S8Ln`uejXN!MlCMI_hXAhFhCp9uAS z1yh|bCXBpDa;D#YziY%7HOUQ`Bev2)u%3^B9{)w_xGMBhhJ$+1Tdv&@`ni^Ns-r|H zkLLt_q$5XMZ&%#IihH@@{wFIsfPk}#)d%P0N=twmEZGKv<$g$y8MuGeqR5Q>oU#`Y z+DT}MODDCmTkT&Ml@{e@V=%wg&!VZVjG0H9Cd+aa#&yZRp{;1l zR`NPyL*X$&$-}d>gTZ>gGkTOM1x{)(aab0Q1~I7ouv(zaw=i>Hk zo^x>lzsWcFmE%>-cJR95qPkexq(oyocDpH=p=4gOe8);nN>ehs@>UeK*8DI;!2lPv zod^2u>zbEPICUjj4)%Gz;n0mF;!z+;Zpy(N-d06XDb_wqcMpF^+Db78wY#QnlSk`? z1xuye#ZW2-Mfg@G5=}c`r2K{a_Fz(efWRhi;ewY*-P)EFx95vo03>OHvr&t=EjVh2 ztR~neJ1XA*!_t7TduOKAHriEkGtGE+UxAaFKPW7KZrKa4K`WGuhbX30qiZ8+EQ)}A z(AWv0zTSSo>b`%}bNx!|{)By8!4ENsT-YvTvYA(h+{jr9#|;82CV%f_>xJU8KN}Qt zz<4%Rs{pkTm86+hI8>b5>MR;nFjmZb7p5E?XjLbVF1l*R)_lXEZy#$E^_?wtDbJax z)X_^>->6Jt<-i3)`>^*_OxD|a+}dhyFv_D+Woi0glvsZNg~*JYU~5)YNxARyUhP!O zgID_jC>3=hhCB3)Qr9wVo}i~x9kcqPWBQf+c>T)u-;}Sx$Le5x#O6biH5S%g(PU|V zTh!r9H6+EBF6U)oN>uWY@Jybz4rHTaNil1q*GI9}D~dFtx+ub{JAuk*s^XDUAcTUJ z_8j?PGl+jALl>)RMH18(+mG5Z%mgd9dtFpw7hymM=~%MoMmqZW%D*sX|Ajfn!r8T5uY>;SDmQok^H^2g%j>xsOSV23y~h7b&6 z$Mt;_#=f)8Pi~jkNnsLl1JO8g9+hY=ORcZC%A9{s{xMg_F059U7lW(uQDBl$j9o2r zEo(|Vfrj*dgcAZ68hH@%f!d%Yg=oQE_(KZmyIsrPndy!mhH!x?ihMiuc*jZ;^M7ew z9_@M(pa6SgyWTSy12)p#bz2NCi>&?*_e$}~*7ri_{+fu|3Wl-ual}U#5}U@OsrLVWoNN)pH6BH-*N5EJ1@WYC8^_x| z`8-BKF$&$Nhw<8mNx%3(2GaOBnEgt1K%}oFUWoi495CKC{lcDp&H9#aRYisBhh99n zuX{+&J7Y;2>cia{&_qHu5= ztQym^iU-S*`roF10t4cb%-+BdmIz+nyY=N{3LU#X%7jl44coT!Q6Bcky;V2ZXFWFO znwr58HtYDPvl0?FAWxz?@0T5Lb?9dQ??56HlLORH4@JcINn+ev10Cf*(9<=ImvP(# z6PFE$1{MN1F_V!%DSw?XoPLXJv zoh*8iyd?Ye0|-dAT+K=P#R6aP00bU_UQAY##pI_Ki=*!kH!sfLOMjAzGzy~0&2r+0 ziHPEG5{0n{WH`AgCZD`dt7;|#Z}(;P<>u#p@%j58IanE_LVqPbjJPEqs#e!kR?g;O z5PEjkZg(xq)okv2<-==g7FnB-bZ1JQeydT@WFCt!l9Rdbi!@{?t7^9`Z9A`ZhT9a` zZK=;_l@VG%-y&RiykoP?ay_!S`)+Fk7z?LqF;tQ*0X)rW*F1e~R%_jKHq-@br%B4o zv0B zvpJ^Ld24Ldq5h-MOJ{yb<}cmZ@Ig5UV&&$l6c%H9t$&LeoHrH+)#`iB&|;{|M0yvW z7gCkF$~OA))Nv+CM2YH-^J8tgAgXn8xfFO>)a*_rXXzSEF1Z676U#;$Q*uU6E2`|CTNVL=T= zobLxhE`P$KkgIgPGMabkB4HoSjb>FB&JLe8B-}Lc8RDZ`_sb2m{AV1k&rU+$vX_X} zoL+1@uRr3UL9>ZlTOtu8!O$(J_4zF0vNuxRS6K`~^^#iq z@#O}Yzl&$c-c8nCemQMrvJifdc6OrI<-0>W!GFf4y9-J*`T@^^ZG`(L@cp5U2N}UW z**1Dt*tvu0dtZS9Z2iD{E{~X7jySa0M}$9``K{eJP=n07z~s8@*sf&?LZ={#tcySP z(su;cHmf(s>~T7z(uD~9@Y%k0eDTo*+_7t0yS1fVJ9m((U-(g9o4nJ@3~05#hKxJD-R1y3n{taX zQHJuIu}hNOooe}pZLqP`xmg-roVJ634DEosj_t_qAF(48L7I%)fx8^KcGvI`7*2@F zwq03-Sed{n5G&z_!G9=X+Q&)eU;Pa5i2O;;Cf&mnql?I)NAVH# zQY2E1qVLO|XM%UN;#0tr>VfB>x-|bFA8R0qlfZ#R)d6sOgri58Z?~A5#|#Ns=?V)j zp=w8O8+~W%zh3uj!VDoJV4&#eQqs9@nykzArLDU{)3lUQtG-wrXRAHEUUn=#LVweR z+32c)>W_-F^Y=i72R>AhNP@6u54YqBUJl53GK#Unb>HhbZZrcbbE#}VvaX)SK}1$N zXiy@{7@)0n%^O_P$29bHR}Ko*EJ%=0;1?(aezC5_0lb{>i>_`^qjQH_9zk@_+9!%6ZZMJ! z?*MhS-Hajw)rBq~FjTUjvsMtJ!4W9wBd8t|Gzk+-U6H@=VMilV!Ch0D&|{PS9vjFG z8;m~*ThL*XURIX0&;ka=yN@NB)1e7iW9xzzM>?&AY4G`3@Sasc-E0cBDvS88RyNY|A`Q5Y54Rg7OG64dkWD57y+H_m-H!B-U61)Tmpc`INjtqDv zh!&6=JwfwBe@-G38nfwZOfAP^d)unMJPYMhoc87NSZQCI7ZVHU)mrT$?Q z!;C()sEH|!^IBYUhpTyW%5gki9G-61X39102znPi5`t#BZOru5fZPA?4;R=5+&y5o z!tiUBim$aH;Z^YFiY565opDXf{z_uw&DGm}@M@f92fH?=pME{qm4EhuZLuMHl?tYf z?23Nuah}cL*>HyI@cZ&C4tixY2TU>*c9{cGj%3VzER0jQ zqW1-UxsXuwiiiWMtFgV#cz_pb*5_h5-p4ZaI#NPGXE|zHQ>zaJ3IobeO`#Q`BJ4)La8JM?o0UqTR=*cmU0(Z`s|#BBg0n4GM|!wb8Xq1 z>dcR=dn?A6+aAwW@J{UVj~)v|rr&01m&#S(f{u@WUg)*(i|TKEn^!k_*)=I1|5oRC zG#}~6>xyXGrg8AVEKXuN1b~|WG+@F*ahGQ`UX3WyFM7Ri#D9qDyNhoUxwS^|Y9k*m zvb;5@%Vl*~f3optGSQfb=rx>HXgky+K^QwL?Ugp5Yddko{c4RN?sm02*)*f@!Mmox z??c>wWzbbfP2B6IG}dIzP94q{N=i#Hkn3nPLU{2QG6{9HewlX4 z=={sbHtqZH_uDRo1?ki_$JFc70(hgQS2I0|3cXL9*l}L{ca87+e}eB!Co0 z7iG5MF3*Wb!dWbL1%!dJ736#pAT#u?30#nIJmJo0*SU(IN=2x_>=9E4A%`MiFj*Rn>zhh7m0!luiW3L@(3 z4v6~Ov^1)vtX@<*BuKrVXpAtc&B0QnZQqDe(gX64z;7=|+$Wn0`FyvIplR(9it9cX zBRgjNg1o=!4%UvT$(s?g&@06Gx9X;U?HW~H%65Pv6Z^C;I*Y)JLP|VeqC^%m8oEb9 zWk!e4IFTz@3RN1b^?E5aV-MOZH?(~}l+-(3z7;^Ytih+Fa;o@R)n%R*2E1DTg>2QV z3}fO4$<(tUfla>UQj~03GId*(xd+kY@LBIWq)k~@`R+aheN89o{y13J6BJl~*b^4O z;t`3G*nqXoP0hX*-K{WQL2W-OCl0kK-i8jx84om00panK*Fb=W#lO>aOS`1o*s@C= z$V8XkrC;*0FNab$jjIZniulG zvLc)Ay_63DiQ>TEv9WF0q=RgKvr|xRo7{jgZrxqIY%MC9Sc?P2f_aRO8MuM%X_0?J z^=VNobS@i&rwBd~&T$z?>f__yF;DbsVkDM zr^6T0x5WtuEDKKxOI|_EPP1u>o}X`D7z50BahsdmxHjWLujci`sOuEAw0zG`EY;bvCNG&)H@;^?H)+7Yp}n5sL2 zXpbp#W=Ui$-3grJf8ztw6vgkFH~D8)$Oz{!3-}3%;~2-+#Gf2_y?yjQUHS)?kp==2mv)r~ z7y&VtVeJAbf5li|kJ~m9f6u2da1WIaF(Z-^B^5=HOPXy{;BrN>4+WAOPHWM-TG)~+ z$(y8~{$@BMiSqK?6n(fCdpIIFzZuRyGG5)T;?<8&;?dXuU& zs_cF!|NidZiiU@By^+z-)t|-LZVMzE-fkmPQ6vi>*YY9`Q;0NAMjY3Y#CCRHelCZ) zZD{;Z?h(hy_B7tL9TB$N-tiQ>9op_PN){GEss0Q~^?yS7=3lQa!pvh)WB44 zD;>Rl`|5}Hj5J=!Rflkqc>CiMm@d^q3*daDe|2S3?!WYPPa`xb{vSL0P`9Uk|Aoet z{SfPIr0L*540i(ZbPkWvQ zK(@H&9SsK(r%+tB6t{LR#1aFzA6ZZ<=~)o;Ns>7F$7!G`+qT2B1iKFCku?zH32F*8 z-eX5cDKROGP+^u6m4PT4g2`wWserOkf5IjNwLdxtZBx1P*9In|{H}As0py;uC4L&} zgXI`m6g7xpsA(LAXnUR%uZ$=%o#P~^HXw8(MN+WrLm=D$n>1?W&H7YhokQt2PD@LJ zPhC9>cEr`vR$47HYpRToYHu;Yi0xqrNKR8%ka;@#90=nx%EmKtra&`pl*vR4f5~WJ zP5-#EoR2HZ{Nn=lA}(Rf8p6EMzlc3)2w#F4%0i1IlOYxFgDS>CCW)^i?EKcNh$yxL z7iIkDNTh6{g^?*MYy4WJvx4=^yoeN**0(Ex(-=Qly z7*Qs6IRpRQ0+kj~KY-e0H-0`1$J2lj ztK#TtjUnHiEf@paS&+AU93x5^5$obBg2YgeBo~4N&Jh&xW)T>W-9Sky_x0~+4+rj% z-)abp^n%+;&L0^l(zptgjhO^kh`Z~Dkz02akuLRSliej%w%;x99uqLn(48GEL27@W27?P94ZX2QVtC8!W|MqN0DraDdphu4+)|b&#I7{J{Y?a$zX< z!8Zzgr>YA4_wUHZ8(UYldm6&_qWhq;q`Ae<8?ANprrzJ${%IhDGcEEtB!33Ib(%;2 zZ1?DLaGoz}Bx#-RPDVnuf127xm8T&$$%M?OHjbfWDkB101z8k@YqLTkpfghfdKTikG+ zf@6jDQ$WUrud3H4$*Ily^DP?I?S_b^#dH5egV|$YaHPW2p%YrJf4w!YYMXIV*>e>~ z%a=^IM=roUEsQzSuOQ*T37e&3Id@Lt6dDBw1s4(ncXz!S(q8+B^>u?`hh1eXV~KgR)A8(G9~ z@@J(LjYiIeEvM_4e}USTy(NlkP{LIQh?}vq>FGv*JW6djpjf**0g|)xQY*@iJV}@v zUvGzGN^l~S0jO7%*|<4S%U&HA&JakzsUj3<;N;L2EhfL{9_BisFoTkpt8Fx&2*L)M zl+(;vo@+um=grlZWs?(}qvl!(zM+?C#AH(+RfA^~upRtVejf}n~r)sIL-sx8ok<9n1%`in^l&5b3(wYZpwc2tF}2$rQ{#E zUfdjN?h2K!$>i!rJJ0jT*RAWQCrO7e`E#%#sq=f@#={5qz6vID=%K1 zLbt8bc{Eyut>bQdcg&<(;T_`WNyMcTrzaoL^ZUW_spWsXmZ$`ZWVU?B_!~Wa{9t!A z3P;+R6A2PCzdZFJl^(nYl!uu9pQrRsMaOZ>AFky$o9Ef1wRvc*5X;1C0$VbS=khTg z`Nx3Fe>n9qBANduv^-$U*@mOhiX2+?C?7M&m!(D5}FWhtDQiq8j zICzHp3w2@VP$^EF{Gjz_R4$WoApBI-JwuWrQpVMapAqSG-=Eb5!^W4vOK8C?I2!oF=LVCWveDUgohy%`9 zkt9)Yxe2%sEKBnsO)?gVB)BYts|Al3&(D{fN6F%*ROeqWfA=!n^VNqa4+>VKe^F}X zL@_JEELe(=iCCZ*dvuu?8Lvw@$X*Q>_bfx^~ zM%G?l-++kP?i%ZYC}agsgC%-~>aS|u(ErWKi>c+E`wBAdU?W~!*_VD$JKD8Ne`uwf z-Wh6^{`;m=3M~i^l;2dPzMYs`e_Qb?av`{>9=tRwbks?ebxB)hwQB9jwjchrQAB?y zSrB}Zg{6-}#tK1cFyZ#07P&AUoQr6&eh%! zy?bxX{TXAb?Rtaj2K*QxSj#MDX%^2z*6-9>Z?q!ct=0C#MD1Qz6>XAUf7K7VmV-Mp z_PW`UiP!2_UYfS6B?&2YKXiI!m5hUKsaWK0*R^+6s`|1}OfR~5D*aAoWMv>hY`?cihxwhLGS_4kA&MaBeAj#*;M4p*jy zf7B!rHwz8)BN&AJc*2^De>0x?xO~=)uE&~Eub;>Yot>+;O^>SO-4m)478SWyE$^PA z8qdyDBcyeE) zw^hxWLebv39fN*qo+X;KfVCIL5Rg#MA|6a(MCSp946a`T?)n%^pmN06E_s-QGmJ0; z;fN|f+3-)AOq|TVe|`sJ5pn++VC#wtCM6`sPD-%*@?UmkM zlWo_=XNbzSll7zd^5*k9%B5jTB0R|Ds%`s6@cB@&M&_e~#Iu#K%WD_C3_$IbX;I z|9^8HGfbg-s2x=9=9FYI47H?Jfe){`GjT`L^CVBQBcg!;F>&VBF|8k zKmV9QS|=(+6VMSdHOOuoIyN@{%;+rw3&!vTnV#TGWG>$+72;kuWxFRi?!`2b z{^qKG%3qBhf3(LlKGV^_Mz^YMs{7-Vay`}nZ@19hjI89UIi~x8_ zQxBQb4{#RF!Z>r?`%kCc)aX<{Rzp80t5ZgWmxmHNe>g1|kBS56vi}APUJ@;Apgl3@ z6%yM?#X-;x6rQcSTjm5omM+fNvCRHPz_?SLA=0(S7+XhxKzm1U=VdM%S>5-#e>u4> zftl9sV~W;oGf01xu@sqrNv2=C1f?9Kz2=7&mB;SmJI*SrO5M=S4g_^ZxS1 z|2gIs1QVAsqy`oPH8wPtL8$>Lmz*gF3xC=`%oRmZM_U9)({7QVEegbMNgimK zvIXc^Nb+trzdkb@QrB*^N1GR$=WxEcN8akV@>YMm@@D_P?p|G93BF2L68hn4m#w&r zSs2MxC?n?3Tu#J?Z5Yjk89TaFnlGSmn4DtZSU#=msBLZMhe)7#`bl z&Jt<4y*=gWi70e6{imu(&xPvj!$po%GTr5+ZZ8#(Q%Bu0ScKQt?=4$Z-I-UsDf#wV zr+Jp^gQJxh9+8$KgvX_-RSmQbS+Jsat^9fG-?TdOA5NhN#0on7$%IWqL@X( zX)2^Qdd8lQPNDJL;E$1bT@`nW=pv7C={s;A&TaRgWq<;ae`61WL00vkYt%C#3V&ko zkYJ{@N)>l)ZVdMMpwe@Rh>)M0uq|Q=C5r}3@1{tQ79b&7ddk1GdE7^wghsGMjP;J!m681rAJ$9+NLK&SRrNE6pPCAYz`MIN-mHnI@yGt2RG?E)Tu_ zOne8P=i*TN+L>*4vL5G4#yOwHkaQ$n3mCwfi1ad?g>KtLkbcZ!F8&vgdw*TH#D^fQ z<07C(_$C%6d^d^`H2kFJ<_Y0LhAiN+$59Vi&kmYW6@9WE=Q+n{bK|0QNz_}}`(xE-s`R9)K61NERZs}g1Ltkm$CvJ;D9dj{u-%Z) zB8DT3M$uoebS<++C~qkFWq*`Bo|ms(q^#<;Zn|av@>x7Y{Cg_H`}ec6%iR0BAty5% z^oibFl=E0GN{C_yu*7GyvOje&t z^>xt*63+ZSx%{QR=HFGiT+8O6zF}A%U``umAs?uw`Ps@jn|m z=LLn$4gHQ*|xEVB=J z2b%H753Ln$D4CFOOg2)b>0}$rgfurIDcuas~~;bJN6abbSk%WT$e~& z*p-lt!4|bA_GR14&2()WEjSV;w%o)KG;UyxB8stj%&^75RkBDJps#^(-I>s2u!k|q zn5;#N!(9aG<$e8p+HYh6vH0+SwFtpNuRm#xK19!Ga^2DxB~G0d^rU=!wShX`F{Qt% z^19HkCmNy26k;A2;nq)B9LGeQCvtM?_uZ@SrPx(`mvP(#6PNC*1{MM~IG54S1}T4) z8OxH}HuCPTP}Ls7skLb0K@v=PeMoAZ&0hA9$;tMCmMDo8iJT&-8IM!>_w)N;bz$viaprBI8f@Z{B@W>86N_OlO<>!$uhs=b77NX&!5nZtnM+Pf^|6 zZH>vJ;Z*TtSN1Y%4?LDUobI%Xp7(!Ud#vP}uXkHOwaxzS%l&`feKcxzn2Qxm6F%mL zzUu0#|A8rIY?(Y(Y3d30PKV=c}H|OfUY&U37uXuA4SCSqiZA!{#jy)LW%L@PsQVWNYzi#mZTf7pqcn{k|rc2m@8i4 zO9SnnVJ2T`7AVochYGHe!oBWK!q=tzR$uT&*UK9Jo?jr=hNYq{T?Rlt0$>(3Vnoy+ z8W3Dp9xEnT-$tWw6XaKCQ5{z(W~=Caa>75EP|YBX_h8IB9?bldp0R)b67m?dcY3vq zulB!&^dkkYL0VwJLi$!vZPO3kbvH0r^7`jQCzG2X-L+%If+cz`l|)acdWX0ZldvXD zwygGYnk2;;Z?p9llWQ7G)msdL|?B1qvX*OcxHU?r!`i?QS1y^(!uEo zw*5MkbyJB0y!PbLZ~;$P@y=Gq4lv?$;dGgMx59*%SD>di zrs>c$@jWs50JXV4kuNQ*2nHsjX%r@UDB-PuycSt?=Vzw6_SNBf<~ikKFt&|+MaEWg z?3eAy>A`rUoTM=1{{e;+fqW=T58x*F6`A?D@k^et)tOnm6k=FeGw@e<^r>u)$T7+q zSLg4D81jE0MBiS!z7nznG;B)ZphUr$lgvIyh!Zu|s+z zLO|@o@&GfptHcMs#7z_yvc^f^gC%co$xDGxXD-%hh+#Wm5^gL^z7Y1HT(Wt(u&rRG zX`C;0*V`RDJH!f#hOmKT`Y_?t26R^7L)TtrBsQWrH>wJ+LJ5-j5(Gdj-Ddkj2L4#L z*Pef&QGjx4DfC``E6O@eBg~jap#7j%Sd*v&;~m}jzB)r>S@BP9s%vxqggfs=m5Mwz z=|Y_M>nRLO$0&2Se*eC9ETlQgu4XKlThqxHZ$P^MCPU083WfkUJ|RJuMk9F&m{5?x zNq=d%ZbLB<$w|Jhn+MGGa4IpE_LqQ)1V?|!7{x$0TTr#k=|wmT+JX<8%DboFN(TyM zI2}&qz>CCVw1aGj;R9(OkQ}NfZ(f9#RP==y;HsTHwO-*G1*b@NiSn3XjE?j%1QPh+6w zNLqDZ&|K=^{<2588f3b%>FZE^+XOP+t}6eK#s(M3ILiBIX`+w;NFxQYq5XjALaL(Q z`>;dFL(mXDW$Kb9$0%xvV0}}s_AGxmb1L{L#oi{ z8mr_rOfi-qzUnBXO>(;(6rjdbJ0Gq%{@pYq9we}q9VevZ6{d+PC)1M7V&gzN7byl; zhh#E14|$TmmI<(k1E(SP5tuCrRW_ffEHN3rX60PF7&)^7Y3oj=)BK{~z8Zg_Aw#0X zs&L{v5IfNf1f>IkQ}#97aX>g6g{=bI1Y2bYPaXupuDU=p;>e(&w6Tlo=-0zaTqxp9 z7n6MY<(wI^7Hoq~3lOcLUKw~YW>8#-U=nA+SqAC0OoyxIO1|d26ejI;0M8Gln{h?- zo@uan`XF>90+{T&$9TFdaWH?gkY~BMle+g+Fq89qzO^xs*kfpGkV}l^Qs1_VrV_RA z47Ym#;mc?aD)P;N%XkC29=sM(u=zmNUk|51c`1ii)z7{}H4vF|x!6B`!!5C@eGi+< zRT!kKgHfL@<+m>jvQH%vJRWcGta4K{64DY9e0gp4{N$UG%WHs|g}i@az+8QA|@}lvZJ4F<>7GagDSQLo1l#wc=(x(gu@!yTX;qb*#tqd=v*>p2T;{U=A5q zHn!g@pV*otwr0^P7v@uiW&m5_1rV4vKndZwTxHUvvC}UwS>ju+Fo~U~{6>nz2s4o` zt1Cu&{q{BOI!jv(?&W``|1{^!Yt$`sg^KgS-7tq0AN$=x`zJ=+>J#GvRdqn?Q!~WP zn(DY%H&RQUWxKgTKnBg+-1zpDhbUq@@;$%pOGe^MoMqY6XJVU!gRBj$JwUOSu+lN2 z{)2T^d>E{w+ropeDI*^*U7<8^TE0H{GHw&YKR95{9I&~aUu9 zL!z2(fBnvbv?Rw(x7dp{Lk@>C!*3q;XWN-S`}oSY-|PEVZ*F8f%SE09$?SeJlVK>* zB%391DuOVc-LGd4UNa9e@7p{`Jw2CRJ6}jo0a2FoMHoe1S!}c^e=3b!=&kyeMLXs< zP0M0htx(U7xlS7~Uqor*-ORJdb3#u=y{7A)m~VQs@69k*L~mI1lj1SAQHKsf@zruvyAWCtG0r>g4lci z`Tb|Xtn|`TZkSemE$WeUy*eefTav9P_34_w5KSg%PplQ*K}(VM_7<7*Pv`H>?^vkQ zZGo?dbl;D;t|@z?cX(ha)VAf-FEWhC5b293&a5>FTcf^he@C|iC)R``yR>hM+9=ah zbzhN=#YBm9=`{?<-W%`Se=YFFA~ddSJTBKAsWZM4h81n3<%AHA z6ze<}e_%h1Cin3tQPJg9uA)i@z9?FF!kB4h85+Q#B=Nh`EmyNYIO>9N@yI1{5GE9RAIZvy+z&ouYGe{T!7wVM<59u=c>sc9kWvhQ1N+6P{^r}x77v|G+Rj$# z@_+O*J5D$y`p}4I7@%qqpfI4bCsG)L=2N@6<(#>X!I}X&5au@d$?-7GAi>@WMqfr{guOg@w@g*Dc6#I$IfpGfsvmc(!Ru_=9z3wc_jH z@_^uj-V@m2QVS0jWx1!iuCK`4DEBF|VX-UlPZ`s}qBETNz)&}ARoxW_bMRQz5DM3r zd*wNXFo&|uTHzEsh!oGP+9f+3J*HTzlhfSbCV|akK<_>iUv8o+4LxwZ2TZV+c??8(H7w#+MMT`3&+O@ z;}h5SapctO#0`8Wr~Ksd{QYo$l>L4A7AKn&r7@1>aX3V&x81mbhk`KBr`1eub|X0K zOHuWic*x;wr{rz*kg=|Npyi%6x1@Ry@_k3HSuY;CnwRZYy{#cN)vF)WVGWUjH9gfY zFXVST7l4nK(%_9Bp*R7Hw9ne2iyhq?aae(%K87~Ac}p9qARoT$7^ilBU64$$&Isy< zK`tXzCU=sm(%QX+V(`rDgjG>iR7&gZp>rW0I299jCyqoAlXQOEFRdt9W3Vl(QO^ZH zD31aa&tK_xAHB$XM;qD^VUBJ&?Q`aWtQ5Vb(vN!(^OZ7pMcakYp&3n+J9Q93r&WG; z_0({^elQB*W7*{Nvyu3J?o`LeLt$h!#dA$XZIu;FrAAG}fVGjS3BR$Shou2a+8->; zZ!^$uM9q4EJ-j8KM+x$IR3_7El3}NptMPp#ATrD4`O|3p2&vLnLpbuXWtCY=aSESG z6~yLfP`>Cb()D%%gMT_c$Wv~xtrdjOvmmno$u@nm?f+PaT_vzCP}w7ev50iyd>)0_uwb_@XxacJ{l8pX923 z)H=rQ&lh_Am-+o*eu*Uz!33#P+~%1;Np@vJ835bP94CSq6iL=GZuIe%W@VuWlbX{T z28B?WFiE;Y$MA4}G3f9UgNjy_OY%JmNrIp#4I*!(G|6bKX;J8kQ59L}b*4z96O@;v zu}RFjylf!BjU%@9#Fa_&0Y!~SjN&a`AY$?SV2h*!o?59eg{y{6^$-nY=qR%ez?49) zWbyxj)g+rL^kc9}S_(&8WzD;?^rf8#<3w-in=yk7$t5UE z!g*+l&er;Wu$SCIcix)^F1lXjuIM}Z^gL9<(=o?k>I+~_TaU$HQpZ$c3b&@Darvj7 zbkr6z%n}Boj^&16m5)KPXi+;V?&uVjNiiy3D+XrU-7g2efdbTwhqRWJL^PfEKXnFE z?n+0H1hS*U0y}naJO-Q(o^8`1Wq$4GZOM9G8N(regH)$;E!$a!+MD4^Gmo33DA_J@ z;%tN_C&DBkjDw^9Pfvu4o$^eMyUJ7hC5J}P6@6i|Bt(AXFVkcXSykGa8UjB|<)BCnoLqCeCVIVA(-kGTBFHdx-l*q- z^pzNYI|Zl)$fDV8#Y0grcqDHHN@(V`Yo%t^md54nNU3*MrR_rerfm75n2NHqtR3s0 zziG!@)kQn{si#eLKSgw{Kb=4O2ho`VyQPfo82z92++7dlQa*EMB8d5QP9Osfc^L#UM)F}TnE{ghLp*f zGlz#W!`hSO#Gd?lV(EcO@;=l)Pqe zja=(D7=5!{FQ@1C)BE|~f5y_;r4#kcL^g_o0DcksFSDFZXTIZGEt7vYb=Im+DQSru z)wJnyaD=UffAGX=Y$5o^_G0VfLM|&=%Uw@q05$fa$qZu@`wHqZThp>{u8|W`Q{$N% zZCkxeL%N<{Tz#3Nou~%n%pCB9xkn#7D{dOvmZYGHA3hwX2HQ3Ye|*EuRo7OW-60iV zR0Rw6kPKIE?0dhe+KiPd#s#b8;9Xmj?>tI4t#`kUMPv`GN%Ujjy%_nv|u|_^;70cQ3 z8BV}2xsF2QMea~re_f+ieY1T#r3)g|DhU0)()^|9` zMKN_9OJ77d?1Hohq=sW;$NtE136YrN5+=t%YLhRbEJ*W1LFB{307KsZ{3P~rC`{dT z(y~ISyIm25Jz+K6ZAo*P4i7F4QRIfhb6c^ETvV`>Dr+06e-i?$vt?FtYb4JV6H8js znzS@M;>3(*A$rG33T{&>*L)Z!Blb{eo4fT7+fc2ks?N$Jt0YFvK>@+j(yUvQnnVrMwqROmwy+@?#?!t8%GpDQBBk zJvm#EfAz4&+Cu?8s9$>x+&1Gk6-~0mlm2WR2z?j3!rZJf9Ry$7cc#J>6(lFygs>3T zzD#;LHytC?jI2JY6!hiMD}{0_PrLtlROSJ+;U*P+ESr{+bf{`^qfWK1J>=q@>^Ed@ z!?xR+_xqWw;hqe+?sYg@VQjKo<=}_CG;@V&e^qvs-qWt0)%LzaQE`-_D4!&z$aBy8 zvXm;l7tlUmQ{C}@sHqG~Bj9&>k#|WeM0sRWd{-CE*YPKhi(DtxC-wQ$wF-@U>_BRQ zVJ)t#+~N1Dj96rBAWaByTye_ZeyBJ9Bh;I>g?c*<6{8?{HPqYVp-O-Ip^hRAJ$N{8 zf8Q6aZcmn}mOc{6A?Zw>8$R0HJ2KMC#jC1`*_nmJYwG=u&R`r21%;&K`XR!x#|frrS) zrfIo^<=grxymLssM+W5Ww$2{9R=>IQP#ewnQ7|kNYz>{Ot|bMNcKQGO99eQzI3GIk zRPK0%U-J_`D1?aAEK1` z(U)=D1QVD0#s(GwGB}gL(J6nW8QYHBw)Nd#F-3tw1MJWeFVZMT3$N2`>H;nbdy$to z4_t|}LtB@rF0cG?GS+6X#__9+HRWe&K|h$4$8T;YGOkzP^9)@=ipXl%+9` zH}{84r1mUM)FxI5<3erj^UY7ex0Noh5Qx{rLv23Kzvch=y7 z+vr$Vi3TZ!)}>$n9u^pS4`JcAl%%-&ehXI&NBZ}g9#~ekm)Tk1OJwD{RKu6FJ9h=* zXmZ-lJR-D@P!D`U=lW+bo)1;Hna#~Rv7&IV|0@fj15>oT#uHIx+3|!3CDk&^mf5W{ zvPpr#%hxZZF^~U}e=zw%OK8ET@Dg=q2o0a=VZhSzMdg0oi@*K^zs{z_-VMv3OpAJ+ zwjHw(A-Jew@|8N|r-zhbTtNNYjb(~n$=6z(wn)wZniSu(u zLsBXA{BFyJ0aeduO+Ha7nX9CMe6BAmMO+Mg z9y5~PRtS>kKHKqF;5WZTnkosW2Dur%e~^W8#AMMQvWOc@uLq4e-*kUyh!pfRi*LJY zxim=IuU^V|?{EJRqq&m83fH`I-*8jk9Nonye`Nk3&dy)my1B|gzmXpU)~2gb=;ihA zb&KrIuD4V0aqhnmnQNwG4Z$2egh6L$Xv#V9KJ-QQ-Q^hbU)xdsEH|GiGvAH!&;mRUee|Hdv$+ys2(% zpWG{9FW2%N`8`MZ_(Q&^aS(RZ^QPnEy)+3uaUNbmrQw0F;wSsjE9e+|gARr&)uQ_y zGQR^X+>?^=&=z~+QH>^>1oQ&_7w?;WaD2VvzMP2ku^0z1MTF@LYJH@x^l9;f%{_Oy;NagjnMgVm+8U(a28QE8(-|6PL6pYX3h zqyF0ao=YZQyMmW;n*y|*zF?zoR`jM^w88;0j4cO2b0;<+yN6pUVoTYDX)VRp{!4!2OaIxa<{WGhbbILv$b zt&rFKg2ms_JF}kM@%Ou=xy#=Q4|lUuon16&UsADpcxHV3`D;Z%@kOU7>~&Wz$04FF z63*$icl&#lz3&1G2;Warg-45-zEw%V=?Fch=W&ttA8FUdpV8ELtKW5&m_|rb+LGEx z@uDo(UZcKF%+-s#X?04*ji`qN?TAjK-#66&AvgE)=hlq8)A)Z^Z9TrB>v6R`M|y<5 zIMrxm=HC~)3ks4p#UI2uoCvXQ$xpWm@&mHTbS|A~sFU3|{Jrr=+Jl4Q+uw;=Lh8N; zvfjI$`O^voVOZ_Ynl}iDcHH_K_SE^sR>C$P=IJ-^C)>Dgy~JbjBr;)ZQRooFqh~1; z*&c1p{AQKs5?23rVf(VbUR`#B7vu5e`zDfeGqh(m1qnUmlm|Lr(?80%j3s&K|2+D+ zZQUauQdOJ8Fvi@_>O4~%DJZdHX%?gDU%}5*dS4wkpshSCZGLNiGbj*P^4Dw#HOrBKVi0%{s90Wa*?MLL?a zm)b=OjqSn+qyxe&exy`BF~L3#;tIaaVjA;ewz56xsq^RTt&DB&l3DX788(XpG87R# zvdNDY$V%C-k}B~&BeoWFuhp9BHUpn%7NTystta=td92h=Y!}0r@F9a#8n4Vx@ek?L zvsV{1ay~Va&$SQJ(g`I`&3x8Yi9&f-P`o^f$fyZTeie)?CMJvj7zaV zjjyBJJuTAW5&Y_$whdysEN*hbB!|hhh6OH$0L81436_jC7LlI45y*&hShg%CB}x-&&Unn8edQTEP$@q)Ki zPBb*gc0dLHtU1H#!-s@JhNNPt!HaB_?^@Ca=kE+!8%=52UrN20iJ$y7_ zC5g>bbkLK*1w33g8&+S5w4_E$nZ;e1hqr7+`ylVJJ zv0xq#S{|78hczYgxNLKL?N@&NN>zdC_UxQ=z}R10UG<;QS)}v4pxEX4sPJxr(yc!? z^gL`B1gr9)dhG?5t895dWqlh zqb>pG^CSY;ZiCxS%$Ls8tm*z3d%z$4^9r>n{vv@R=|!b?i+v+)Uz~%6R0W8N$TN>? zo%m(qa^s2x^J0wbT|C;()_O^>xWs8uTqut%DSv}udYkChry3C`St3hc#5ayu+J)S^ zYj+1lAW91fX2M-Z==u*Y}YLr zA`AVFZcO9EDu;)zpOuTYXEJk+HIHcw$#r<(d|&y&_^nc#+hoUu@#F?IP~$_48ZhZ){CsU5a{& zP`s0MXEslD@{#;M%Rj$_$|BCXJ8$&pwOfB4<9Iml)6*=3yJbx*U*^ymG$nZ z#IyAAp!Md9Pwy`2GWt}!@!np&L2~u_XCnT;WSX4OK6w^0B>#%aZj(&xbR}hJr~A%l zV-F92-5@hoEx@zT(eLMcqxBih`Esef^X1G0o-@Ho=QR3WuCbZ1UbeYRmUvWVWK^B_ z#by6^Jm+0)f^TK*+8blDSYtE1AAzB-m!uU487}$Y)&6C4sH4)P4>77K5mK5{+5GjT zdFgkTq6`+R5a~QhP?16+8CD&-=*)mv-|b6Hv~&0j+=(?Qc0 zTq$xa6hoMmP!o7q#^{V^)2tqxn8REn^N1(<13oGpjOFscZOn zaV&xEy(=1PTvZkmTW!q2^6=NxRhh(0^TRp}pM)e!YyHsY&zaKU&)$t?ax$yky1G!S zmXLampB-C&mM1hj;<1Jc;Ty^_&G06+?_-i$mwmom-0w(pN^{<)_MpsmckCYQrQ=?<`3)qdTQ9Er4V*uh=yDeKQI7E`1#2JR!lo8nO)a4GOcFmL!ia4JH0SW? z<;t0ey?X1-X7;7j{L4WI5h#eUcjiTQ9X_}xT2K&Ql`V?x6BU&rZT~Gh!4O8})D=VD zD=CYr4K4Cj+p+c4c(Ahe;LXcFCYRLj)(mS)&*=saMo-WCZEv!9i3cYMXaz62&pSn% zT%S0|;==w}3|Wa0C#)3DbXQRh$06s#4z4_+^MwrQF?Et!0G=%dYz(lU2L4 zgKC9LW-Uj-BvwJm=o^K=QtkZ?x!JpqYHpW`)ULIBnC&{J@zY(Ak4`S|!%OMz7aY6K zrodkXlWM&#tq}Q{;CfE4=6=w$$*>+AQ$1FLzbj)|Uvj67gyqrRnK~>{iK5!Ny|Lja=cI1Ew}|iLmhg^4!M`({ zTYf5)f1la>%wL+@7i8VmC2pC`X1+z4@{fexE%V9gO~8xPV`5zDF$wMwNUB8(fel+U znpae(Pg4B8sFbd#gqEtKnAxNW zMNCZWCED+=JQj!$)26ds8%m=-R%&9W4WLp#RN{Q|rHO<#fK^>yiF5x;6N=XCn)KE& zCF7T+@9lJLM0nR@?_UGXuJAPu`;X9cAc_JJ0-LsI5`jKR_}5lM=3GP)T+eB*<||(? zH%~2m7Vun5{eleWY>!b>H2O03Ms1dW9|+8nu&UPA*B{rRZi%)vBHp53{FR3SLuBtS zu!rmiDvJh$vhx#4yxI3xE({DM;U`plBkix;#;)W%uKIZ9m|ZlWb2vsYiWDz5MO*Pd z#thm(i{A7>V*($c-9f%Bng)>Bpj&~F9kjBp_rF#GW?OVL(6Wcj#uA9|iRDg!$pNxV zHbRdk+o4MVjU%L5+oOxYkt391q6YiwJD@8;p%WyWX+;YHEk|@NNGF6;|7Ivf!3pgG za-1QmcpggGazdvA<|hzsBg6=SzQ<^I#|=G(t8qpfqriq6nhq!O1g(PtOde44ovUae zT#GB39|g=jp@y&2P~io4^ag14f}ElrXmJ#v_J)*|*JwdpnkSkU1@wI&zFrJ%ITAr5 zILfDJ0u%`Jg^eN}km;^B8jS*9{UF}gOCku~_@LJSkv}v^uK*FkHTa@AQDEl;*KZ^d z0_U*k1%L^F+^2zc%^#fy+yf!{au`y@o}ni|Y!I3T#}Jv9drz`c8kra}R(NQgUe!||<$qgQ}Y6htF6AUkaY`V0yTy++gGq$ANh zC?FF9LFXv6ISM?Cg&=g&T@(n8gGQ;xplMJbIv%P@mB9#ttZc0o?{{nMa5IvrQ#`b2Q`@nKGG{;H;Blvzc6B>*90MR?v z&`Ips=ypJy1DPK`h0J{K(UU;*BNR>d0lf`0KS9FjC{%J_97N_q^m-=bbNYy$0~L7? zJrRnF_=HBGz)?Pw>gaoo2w9tdHfwM1@QCHhk)lZr!?;8Fz%hq@c5Fr4?{#^Hf8YoleM7 zU5Um4#u_O4bUlg^AS9r&3T+1lYG5~+YV<4!tA&s;A6%`oZDV^C1X@yQ(gMU#M;A@nuCIf^y`MdMKG zX(p;4g~>r<5=vP+f)Thd2BTho3aV-zM~6bK(~!}B2!_k6Npu&en1KQ*Zli@k!W4{6 z$~j1#A%(sfo`z!1%|pAoX3)wgaDD+wj8%fxJhnm;04R-yc! zJhT8#Vi`>gSNV6ysJ?>k0BLKG@+t*pE}hjA%ECGn4wn1B3-fDed=xO> zg81onb8Z7}H_<=fu(vkQt|+j$4MF!UGz$uJ?La0ZlSByD59uf{{S)H71kk74J1_v) ze?#<6Cv*_$PxJuD+=XWHsX`U3zfRUR**?@R{u}L#0xAb+I$ZfKS{nuY|3EI7O$CAa zKKc(x{tLC~AE5bApzIKWF@MmcC{X(ktqQ*Vh4V3T1c&5wfCzx!hcN9d9mC%3gfNin z%FqZPK0^1!HNjsXSUrMMLVyp$pjr?Sj^jm=z}5+7%nsd2fI^Nz6#+sIQflEmpY~M4 zN3uXBA!M1k`JW{f4l0Qt+DCv40Z(EGC7**+_>~YD1>PhO?IA)w0uwZZ;9?a5<-|xM za6}*)NrLo)APltPG-srskzSBW`ac1A2r>eE$q-r`z#uS>Mw26S|5;OWq)0OGp@5WV zG6ed|niBSTPmTOe4>I7B`wsKO^I}XUTVnFDhr2Sa0WR9hcpNcu9gac zdl(TN1Q)0gCKRBdhfHT^kzZi?1Ufb98XeLDXc!^-_y?RXp=uHY7}6twrTX4DO|q$sZ*V0JB`kJc!_h(qP6F0>|9Q6L6CcqR^wlV2%s94UTz`NI<|3 zrS>(!b<)F&JpE7VgjqNXunRy@r}YE*vC8H9_im#2zRUkna_N@_w&iB3m+v=R4t6GF$j7W$;ROzG!yC8;8fi+n zGRQJ0l!uUF9(3HEEYbzWE<^or;}HbM4lq}e$wU8qQ-E^n!=Z*B^2jRiSAsn@E8woe zr~tzz_6n4Gpn$wUfp5x?QPdLVzp)KT zdx#G#@pPfM7FlSZm^NH^kM$r0j_x|7=tD|j8&uSy3lqe$0YqT{3xNVXm^q@2pu}1w zxI~)uk#i_O{s4CTV}QIt0lA0Jq--O^3i{0if*cP{?0#noL5+uqBnsr2!M@?ca2gs+ zkO_b@N9X{%0Zd?%rpN&3wt!Tl_b?jQ&5%woV+qmIjJ0bH^>$i8Gv+Md66SdXyPf7e zRx2bQ(Aq!)W~`7KC@^OOyJ++yLh+6WIdFM|q=6Y*$b6a*;R+%FhBinL*tLUguxf&Z zpDj`kitJ&p(+tdJhm3=I2Z;V}&5-~Fd&CvwIKsBm^|fsev&JVUh{C*fq9+H`K8BXT zB5n(eIm33io{mAK3v4?r1pYmSNuc!!B&<6l-5}o;>h5tt_CcH*gkWwK0wk`;2T(bF>C*glW|ux z0)OKz3V8?XgIL)Aw1B)D4Rh&8972bii$(%qumWg=u{e~R9(xj|&)-1OdK?0vJy%et za|;h90Pc>_@1V{%Z;%8OSW1MQ^b6qfbWT7xQ9vLWDxT)V2;lnPA+#vql>+h8nua^+ zWGkslh3Zd7YLk32()u(gDm(=dMS*)62*ZDhy`xm54v1tT^f>rS%rO0(mVvezaKb3E z;XvT21FmV+_fT_GJDjZ7S%@#t`T!MG9l>4Yb~f@Cyv%`mPV>-<_iz-#4#6ejJYRC?^*%GVSYUTS%t9o>AB1!ALdQr zB4`90GfdPL#nAlnLc|RP6iXneRD^KDDyJ0k!;1r9kXekp`%m`NDnaZ~z_t8@vJWTr zUnz18o^>lA{x}^D!XJnH1ddhEu8?xX00j!FA-Gq8c%p!IEo3?^@$#z>SmMDVlLpsV zjYOZG=xK0|Y7r0AX(s$nJhIipc+_ry)|1=7@YMbSZLn&D`xJ2lq5+;XK}Q+w!_bRu zfT7{g427SLExQqpjlBg@MI2x<62-?L@EiiOHccyJfg4x~7;S|@qYhxoc+i5Bf}}Qx z^0gv`;6*!x*verA@^}#*DA?N&KM>LZ+kT`$=2KBiZAdzJ-U-`aLB9?Zx}c)O4rB$O zx*-I&^Z)kk?nGR`N;l-Y*M$^-r5*^?Q(!KFTivj%`qT^2!*00LpZ7tlSj*u_=v1X} zFH|Yp52^5;rUjfIfK+&ulMcQOK=GRWFm((LLh5P6oc3@YfGI6{2vSdX%~K;U48kqm zZ5X!6o@`pDe9S|LC-549ea|l;7s2EZbf@?zWO+pl*H`H<(gZGz!JKw|6xjy2amaUC zZk+bTjv>zfxh#NpLGZLSj=TUzlhCw|akzuun1;;tyKtNRF@cbyz>66uwR?<27^gUO zlCtLD?@0e=;)AD8CVp)W_B`EtTW3!8l)v*3KMit@IT+-8Um-g@=N|&mMHq_|3kbY2 zp<9BDuxR}6EI55dzJUED*e`+vxd47_A{gMkh$H~vWyl8y*bDZSA(e#^ntSORECx$f zpix(r;dH03LY8hKxFep9O>qTr2fg26n{3kmCjQ?Fk_{5qpa7^{2rR538z5yJvV8rH zRDj}*|9A}^pcavvfM*>hpn^?EJv~&-tiuYqU<>x`m4SVCHjtW=QuFk@EPP^V9a!Cl z=qxD=>dh_WE8vCi^#toIkePZL)*iopBD6Sou>$MR&0mmF?kB7t{{4o~X*qH67es0I zpnQ0!5CX!#Va2Gr4<+P%f>}d;7gn|g2apQuU}12750=^g%cB3llfRG;o>qPW|3fHm zZU>r>_6HshUi^cE(~_#_FN}eNBS_V>hT`iEk#Iyw2)hWF^Z(6Ew5Hj;A$s8WUz7^svGKM0CRy*1(J*M1fs4$as3}B4WjKoMuvxA_G@C1si7N z2=&guJmbrOAps9KF*ZPz8$*wSyWC}XnZN@2o@O2NMql>8zLi0h)51)wDX<;jV`Xo|iF32UDaVVr_E25#1;mz-0a7(uK< z)R$XLqGy8new}#}==H3*hxC!*>c|Xc4YSKp&PTam^PB&}%J6Rg4!>6r%af>3G+R!7(!+`ogR1Y6tH^?(UUH`dFN`@s&kT`G^@--pjxHGNN>VEV-D*J4|p6}i`I zoh#*`J7iLt^&-OA`ZqZCH1js)G*wEyDvN>0epM`9Np=`!8Q>G$Lxjw?gWq z>C`2-CXPQhy|t#Py|$$tYqz~JE}YvWo|R^Oq&zRp%k%6DZKK$=?S?ffk9`7#Vt14? z_zVLHbrC#!25%j+FJj=K;-w6nUU(=gK!IXe7=?A0Fx@cI$UzWJ!bz=j8G`UE!H)f6?x+(9BbZYM)}oJk5}Kly(g;SSYg@7 zO-=V_*SdnlOV)co9vRq{X4X~?T;5Zu=?N--)BHAy{xGu6D~AGW_aIl~WpVa}GD4&B zYU#+2J?8+8$if1vK$RJN3P$8RwY}$pk>n$aIA3hO+QknnE=Fe8mhMbNCmIFu$c^N< zx1UdM8q3mqNeber^#e3MncfsqBR@bceAoJP14+QO=t@~QYwNLwmhPC`eCdYzO(t8a z@${SA^Jp(^`gdWm+rRrNgc(yMMiiZ2vo7=m2-dPzw!|$@lIXOMZ{3kGAXDIN6_kIS zxI|Zkl}Z2E_4N6_>(#F=JSv|}ib8TGjbiqGrsJ}=CS&U#=}gkuX*%BY~NwY}_8?^W?KmzRL~;Yg?X*(a8P>UW9x%(R5{ z@o1d5S}>L7dQxY195_8P6R1V{^&%7sFHk?Gw<=sHwRiK$U8oUda!M{YC>N*})o)T8 z4?n+y4gTr4olw0<&7n3&-CL>tk%@vbQ^~lqQX!z>K?t+FRcEI-zK7L_`-dNoW`^y{-4M z-Fk##yuc*yiJf;J3h7JY_Y$-FLm$Ccz5nnNqOs=?o8d7DJ^Yh`RJHZWXgyKYWhbcMq0+e@c0vSXJ%lT@ma1^k8GS z`EP;WgIX#6xmxh)FEOieb<1EZpSHkvoPb7MTnw&*H~-rO;v_-%=dky6?R%F;%KXT! zM_x7O_m9A_2z_#KpY&`tJA7NZ^-#xSgu+o0JLcwRn!PVeSDd}%Y*UXf=xzIXZ&z_v z{(wokzTk+t*^OmAX5(ebkqehT{U5%`EfK%Dnz_kIpQp}0|AbYSG1>pJxN>+Yzs{y~ zhQZHheP_r_(U>EluD?qyY|3v*(+_X3uFi++*Z#`)-@m&_W*=4ut741(O*SWg?vWoq za>f4M=-wM8&uqirpYF6SsI7T=P@P71-ps-M@$}=)o}-7}#qmSI)g^XG2`aOS|F({* zocuO7+-8q_KBZZ&dD;~&4tvZRo8`q6J$zAatkm(mBv0hww2$|}G_}C3A1{d&P1r}b zu0>K)_7ACPO8D5u(KkHIW#vin zwd!6A$^KS&-NW8+O_L(LZ}Tc_o%{aH2|wS;Nv9P4rC+AO&VKVbw;lL1@FB{(XO7*K zPz$?sF}qtUFtkNJ)^V50^=mNdCL!4oMUcdGnx5+_(smkX>y7jCxy(Uo+$`(_B{wY| zSnDqDS|X}iF%L55q$tH~d(V~5V>sO}>E!YBD&sHyh$pfVAkCqFbN3Me#*liLg-O7K z_gCDN=H_cPcRt9n>fc?7cy6l_DVAlO<>Jeam3woeFp$Rm4Bqx5_YVgOC}oGAQkTko zI>_^<46ZkjH!wsUa8|#*L~$f@B*pgSVadf!-YWcigdFT16w6bkM3)|zDHHY95L|VR z$hjksT@vw}Q1~clxV-5;CoVEx>1?rWkPcHmE(jmD{>az*@=gw`k3evM^yj?WG?NqZ zpYyN~zdf8xM;h?<&4k;yNkyy7XfaFNT1JumIFYbw`pYE2RQ3EJ%{OIsjsRECb94LX zI-A#+x%EYX_UWt}*o0u{qe~s zIW5Vy5yO1TS88FQzg+di%k9$cT)R>-PG5%ABKV7-JTfwKN@G~0qQaQoNHq%d6h2GX z7msV?*CECbu~MEr%f?jy!zh;X%;nL6N_>l!LehbCWo*0mJ(VJVsYce-E50J!wmF3Z z<9$=htyg}Mk(010uqAkx7GE!r(!o}K{v#))qbM~Jl0)_LqoMDO6~E_0{i}D~DJWX< zu?+H$ZG- zbUAbEof>vMjWT8U*U|EkT;AJpk*YdZ$AZ?Q zjVBi`?Ncv`&2H7GRpN5Ab_wYpM8+OamtVm?u3IRhmPxiK56tSi`!{?Izou$al0b?< zUh}q3+OQ0BZF~MloPd4VupLr3PQH@seyf*l&3)cb%?Tj0OpB?s%m+y#~ z=(6qYz0bU8s^;c>^Y_xDv{8q>J^e6v>s`UNH(}T2cIlS?otr`79TuO%wwA`!1q$h| zVSRYsF`5kHQ@?fzl{X{*`*TN6BpY zKAXu=>1uEbHYF{4XGOOaobhELI2{GU=VO0}U)jcMK|&@EQ+DB!6W4%oMk$euP+ovLIdA#G9)aG(!Li6uMm5Nt!uiGqTlg=|3a5+$j&gFVcMiAso^ZLXn=n%fu1 z@$DtfQ>7g|<{GiD6VbcLd~Ncce$RUwVnOUG`-nxcmnj|5ND8&qa^Yq-VyCI3mvwVA!fZ)m{jR1Ckm z;%zW8R*Jv!KwGJ*WUym#yykbyQ{{x3e?12I9_*BHR@EyTf-3kn_BYnQPh9d%8y!Q& zZcRVRxQo^Gd%B_1Nb2-gV51bd>oG~%G;PbSMK>?FCe_L0_~}c?0P*asV)Z9U_i;jlNDp=LzRVQl)s$E>H!cC2pU#jjEY7XBX+~2D> zSJD4K4p4o2V|GMTx@edH zD>8gew3tQoR{XV#4@%mJbA&oA8)w1I_zL@%Bz+$-B&mc>Piu=?-``+d>)IL8^7k)_ z#7#eS_~kCepCz6=;rNxcZ1RIW&P-DR`}3H;ZzJ#13wSB=)B1cVN3Z*{pnlfILLI#} z{^WF0n@56V1B@-EH(s(*TQcmHct3Z%D~OHW;Jp~?g}gv+H@r1#qbxc6v#)9;@RENi z5~rz7L2FGXvgFdBRt0<(vdlJ%4m*b1>Q?Li+9mBBZX9V0;-<0dG?iOVx`gMh5VtJJ zg$urEnK$X0R7s~0V+pscs&`#{+W94feRJp!eX&#wN0|w)5yC5R-bu_-V!Kh>B?a3* zp+DiBS44R82N*3&Ni5mmW8Yo5#_S?*uSiW#pW$-9#Zz8)X=d9fPN`U2zsWpFXrW!5 zetW2UjJHHu&VfU-vApN=dsCxjjsem9C$u*1_*wG$gZJL%uG%n;%7n`^j}|A%Umw3+ zw!6+}=*`yRR%;SLIc?Ff%4I+*yuOIk+m&rvjCe3p zP5gfF@VeP{LbK;aX!UQRih0A@6RtmjBJON4Lho8&PGL;aF3iFx_!> zTI)YwR2pZlW+`vC-hVz~(jn5wdCI~2!>u_h!>18)X3i4_qUnz^>&$EmXZHVFIugCR=m05QWAX=aIKTFv>8PbreFKm%4``i!Q#|}j&~*OR_(eYyI@%l zx>(Ikg`RwOrVjVkysVzC`mR-vQ4E{(G>Xvt`@P85`~$OM^ey)9xpuBy!^#F|T>Hhh z7Q3QZEFs_B^CL5H9=`t}_=#)B*`9fFYw6cYOg)d!J-e7Ve+}P8x-pb+;Bhbw!o5TW z^wt$V()Y1TimG0)F^QU@y%l}(7!B8Rcx61hvO}PPmXq6v(VXt*RXAAWqs6}VVW*3v z{E(GZoA(7${FIBpn~|eu^n_P zYzUcgkj)D&_-Qq8%!`~eR&+Y^OO44xWolMrhetVbM-cK-!a!n+7?z?q}zd zYDs73V@^JhlMi&*m<_x5+%=kX_xZ6f!B+c8a_D9eT~^Ntb95Y^}8Xwcj9Y zCa-0KUiKpXQTS$5qD{ddlM{K!RCDdX1D~zb;pmd`QZ|Q323Bn3voc+(3$+#Yth_rZ>3t)o(3grx*9Ipq5r1ncx~4z*3^=7 zC$xo?1E$>SIF_}N1{R-;N>(To4PSX)@#)Ex>sEB0ZN_T$FT7p_P*=P(_`VE3WR53P z@>rg2Ok^bnY;;)KE?Y+?vIs|DqAcFK?X8z=CMS(d7Y|l?yBm1)YOHOJ+e}}U%d1+> zC&umwxo;Y9L?$oi6KWOo8k2BGwLf5(#*b^O(lIeyBA ztY7X@Xa{Dn@F`x2h)LmSZP$$Yy2#h&yhF2yl|DdR5)TPwH2ebOs~HGy$Y@;Im$~g1 zkjSfeMFTRwhs-bb8akeBe)w{iZH6=;YuXLHb#s=tnt}cq%ALC+1QAB} z=RU;J6nLJ0|6p4yST`~4dFFHi1zqhy`^$&jkNNQ!?+WovEE*;-wsIhK*FFabVke$x zWnOr9scIPSdYVz3YeD+vCf8kulViepn>Cg9;g5=MAT5nQD3U8oA3x+RLl(}aw(9%S zr(T~Blwv`Gq9cOSRwanvjvVS(io`GPDKw4D^5gdUZEtndH;KmK#k#z|SMq2yue#K$ z>%LuSGt-J4UpBR|_4}KGdp@&C3RrP#2N&(%Iz;dXOS-q;$hzTb)T?IJaL+zmIj+GM zVWYL+v^etK9s98B`PlZX%QNpA<+>%>-k%8Nrn8N4O1G3Xkd zq_VGVLUlXo5!*7<%~ zS*rCrRzmNFV8@gnhpOr?#DZ$F8W}~tu5;+Mc!l_i)Y!e#-@HzNYMWN)!t;`@<$?C-G&20Mb<2b_SbqjZo$`w!%HGFmz zq)Rf(V$MOgsp0M-9PBzuX==m%)yyBFyGr_hRKI&a@xrdMCdz#E)^nfs>`%NqmMQrI zXDUJdz@0PjqmALM>>{752IH}tO_Ig00|XKLr+-QQpn zy6PM?lc9yzfgX_<@EyS@-Ti|Xn)VB&b#;}1`^5ms1VdyA{+(EbdNJ&IU;jX@KmI;7 zf;L2{{J#DfVfUu)gu>_T?`Ps`1ePNt9l4aT`t1D03U{o0Q@j2soeCVZ;r?XaDSqR?y`%R z7+_i&IunRBNDy?!+N(8N@(*M#O*VRyn>?OqSiSQ#t)lS#4U)gCRbi7?rW{?CjR!_F z)C`Yh$zS*1@D4S6M;_C^bggk*!vBMePR#2t=S$62sO4W$I?47+X&l#=?@gQh@S0k) z=%h7xA^2Ca&V9+fD68qu3Qy*UmvG5I83CP(d}^UHu2rM7E)SdgO0WKrbb?*W$iIn# zDW5gl$g_`@rSc0yO%^IEW7K|X(hS1Uq)L6S={X-v$*AaDJtjJ zCS9;FD{}{6l9S>N&NF^W)i`pYI^E}eKR=lvNv&m2V?EAjhG3TXTR=mh+nk}6*_`3Q zp}7uYnY+a;GQ%pr=pgEwrehOgvSRkD;_hIP%O%CjS5SL^y?-+O^`e%S#1@ z42Z?!G=rW#H&7exMhASaZ^o`W&6%x=JQ^~%I`pAbK)rKR_f7m(fv7N&Dk^*)J~!dh znkgzwk|ip9LsB%)XU5gUAbsQ`#7Li=TwExyaoHZdSg~)HV00*X z`@&HIF9TNljgD2P?U2EBy?~g5rz@?!hpW2p)MXU<#PXfyu>;learKUVulKj#dvslX z@3A;oBq187*)@Zoy^}Y7^i0Y8k;;wW z6b)5J?6phHPXLyMKScYxM$y-Zw0Y_M%a@n+`lg?)E^U8VIlMOS^JYcL5&s<9XPYB# zgWJmRPk?(u@2KGj24*87HV?aWCKE$qT9S4&dNvH-$9Cb}{r-X4Bl3J$bp*c)o7N`& zCDGk4AI;UIEYaiS_`IYl+XYKPQE*Y*ZzRPpl z=^pv$TRg+K@lHi&(kyDYjM2jFQNpF`FS0p5T^)=XPP3U|WK9UpjW?WO6vkD5HgM$p zv_xN|ag)~qTNAC`7EE%BL6`09o$f;8b;92aVNzfD@G0lvxAtGwTrJn*G1`=qI(YLn z@%YVZ!IPo7-CyjT*r!Jf7PE8RiS|-axTf5B_WCC>L?h3So!EK(R(0np5))%*$TmXL z)9nn-F;xmI@vl{$Qx8mBwaoy%Ig6&@SMtfs$z0qR&9T+10`c!v*9GFM(}*PO5T?9|9Rq0MnRFea@#|LAtLMRD%_~# zyRny=XrD9JtTL887GiCrkH|9DJ9@`wRgT>LyX;gcM*K4>_$pO}_{mV7Xwog_v0%gA z>^Tm+j?18UnEFCiH1|?tedA{`0{pt0wk@WF!Itlw8Lx=ilhXvVzMxv1eVStvP=aB7 zq6!~%_#o$9yCg66iBaXVuDn5yQn(6{l#|F>S;T6uydk}rW}eUeJfb3&R`#2L)`}i| z+1+3!r@W(*Y8-~Ob7HsS&esiTZ0q=L{YNs%He$;UU)IkqJ!l$I7)p86$))gOj$7-s zUsfyDK`v-@a*avvg$sV})yqU9{VS>}ZzI1t@h{}b-Fy{ckhtTb)K`%GEXQ4Z!7t=$ z>*TxRv&7R1iiNA%<6<4obOgPEE7m@*W6pTxIo5ecP^lPc%JMZZo&TfQaj1l`tIzt+RF;+Y*up&2PU26G)^c+%Z)SV^<%@~i!F0l zs^dGp4#|Q|>C&D!=IHqM#sl|u^$YN=qKa>6=@oeB%k}U+Su63I5X>88pWSm$ydA&M zyKGbU({f_#Ftd14bah=yUABrGOIdm_G1zYtRS z=dU=hilx}IexCa57QXetghjY4^_#no-uUgc!}-|C6wi;PzEyRXom(oadpTr$r}@#g z?HwN;Z7O(YjK2Lq`u!7igBL+cd!6++THd0&zIAvNsTDDwX_o#-N&d6(TjFT& z-o<(BMznmH3t# z+hfO*9eQi)CX#FL$;+H@of}mf2fK&Amzukm);4Va7Hrhv7$#qKuB~mnNGSu8pEg?I zPE}Al?bmcw@qxo}am~yF?6GatOnyOf#`84>hS6E4OOxHrnv>mo;1a{6=H#&>wnjyJ z@61QPoh99rfAa)sw$(8o^f;?tWh)Fl49OmzQoA?s+Lg;8=t2Q5#i`~2F6@Y1c%A*_ z>7NIH$W>BuT=pV{T?Y7RlG5QOSV=R)z?dB=JuXI*^g0VziiF^n9qBF?uquEIe(oL*&!ZG9wlGB(&uTdH2grCz4m~cau3Yh3%xYpVa z`AjQ=@QDI3e9jnPk!OV_ry%gFlVE`9=)s&L@$*0vOXe45!FCEUmp1EyJDN*<^QYNH zMq+0AAC{|u%o0Dy!b)eY#EM-}lF*yyiX>+gvj}R~iAKh_DXrMXuk@Q^C6qJy`riRl z<-O0|QC}I)kTnmT_DXrKzYB=oR_*@{dnqF1$p221T?(K=iVld%yTZd&3MxxwXeVg| z;NYSHbO0vIu3v(=l$jelp;;{0Ef~K5&_*vJpm=7UxdlhW&+z4vIIs*9$KH*JH&!{2BE>ty57sV^&_oGHOa%kTyVTRl|~y*zzj`Do^@S8Q39JYCn}51xlR#H{0AoLvnNtKe1O2` zAGn|JIIvKkIS=G=NmDZeha?n7Zo;%ETw`gY{8a&e2BUHVO;pD&1u&HMT*`=mcn?<2 z4|~_XAg8^b9}anD0PjW#RTrg|MVTxpo2;i%+H=@)ZPyqAax7+8-A>DM=SRvNFzBM8 zr=b+Rw+HLfTZ7TxqByC<=5sSDEoNAt z7o(yY7Zt$S>!UlxDtg7z88uDuuog6hS>RlLv-VIjJlwN~O~${ao$-leG%CQzx@bR# zuAsbT!LH=Bocv_;QJwt6;zjYzsNh)3zZ|%v^Mpr!)!iz; z=>eDB{Hq9ksv4@793CXhrfwA*?qto**dA+M%ikV-_vhwf(OrAuJBRX=1707x-}yZ3 zfwpdNt&fMY1H$=V(Pab_A&Fd}bv}Nj% z^ToAJFy1ab`%L-O)=aJeA3paPA5T^2=$uRyC|Msh*tKmI(Va`poUMGi#3-=#c_w|J z2Jx$&e)Rm|)g%MM`vx^!s&tkdz}3Nk6iTM#BF7Rn;#XUxy+ zBr!6CgIWf=LJEF5Nv*DC3hM2+pzEeeWD3cv1(_zQ2_fzYCY*&7+@e2$BT?cqjjnKb zl2Ndd93lmp#Lx#xSdjsE+VrMHvtd`((vWn}QQ+WUR&5|7<)C)Y^8y7D^8l<`5pv%(bf2fK-v9FX94;V{>BkPxygJ`Bi7+Csu7K;H!#cVK%7^0bDd3<;SR{)=W~5YhTcrX-rc^`bd?V*kac@^zByFtU^wb)%Hp;ckQNs#; ziHqq#5h2ttg+_}W^V(vBTqJmsTycS*!Ix^8V@U-P@yt+wG*&1*HAj39qmUjHC03}o zUNsder}^lYgHm38tYHf3>BvB;QVw;iqX|jm?3?1%NqHXzh zIzN0Y=}q5f)7c|i=)^eNo~ z7A_Xj$1HAl%i9t&p4XDF)$DDfAb4&ZTDIeldqw8#|D39Swsv!0C~;C8zF+r4J>8c7 zthVlx2iG<;yt0z>;E+_YOb*gs8#}=CoDw5G-H@&G;B)(w=_ma*L24e8VZK}BtG2$nwQG1KW)K1;3_nNf!HM+~rnYGBvG9TsjLgQJF z7Y>X}w)8iDuzKEk%diY2gt3X7e@UzY;7ACG*4ZeeZE$-U(N$|%2an${2epa+m%8bGXxzo z9@kmh95WC$Sg}3bB??$~PJ<-~O*S@T(uBGUFCgR3qg8gqeHFuu79{=m);9(^N%Tg~H*_GOp_iSPJ8N}Rtk;cKgu<;@Yl`)U(ugj4zz z&;CO@WH;wDs2`9Q2W#KsFx&+82A%N~j9QN{#E6||TF|Q@s1Y#H#tUqDQ;p(TvAC6E z5VSa*E?eXsaZJr*cnEH29)0^^VXoRYzK4t%4tn+YF)ibVADdKFdHZN% zW`-?23q!7A&QE_u53gx|(XZ9|xDTjg^o15uw-8>f7t}Lusk(t$W?1FtFKo`psDa&z zMci1L!vE>u1H5)wS_43+RlqQwfRrmhH?1-BwDkn6{-=?V0q7}J+7?*1k&yHCHkjP0 zdWHePZchZXwkgwo43On>8@cFL2)eUd7(2hwRm?jtaVw@^1U39BApZNgVbcIE#&OQ@ z^oW1cpVMK&7`A{ueZTWg*cBLpkYPGvuN^<)i$dU&2l8ieFVm0|*sh@S&*FT$9~?G4 z%%nIC&a(@K1zEY72?)%zy2d<^&zAVrzJ`bz^axOV+8a6Mp<%mpRT+m~`58C@y~dE_ zB5n#tnCxmo{%aI+*hqXoaI!vcH!z0Zbi3F00lbo67Vy^p_^X+%r3wZtMohA!M!h- zPaR9TbM1w7S?$-<;G(p%`FQq#Mc_FI7?DsQ3XGBkKA+#5)tK!(Jc+%TFYb9m@JzzOvcpqQcrF$s~EIQG>_S>HOPTGSPPniU+mV{ z)C};S@IiW$0W{zm^KZc-hG2P+-Z0q!8H?`+2qpoLklsrNu3V+g(0fE>hS?6z2!nhJ zX8au$S5rpx3mUk^N(}eOps8RWpcN!%F2)3=UhSwi8E^Zi5C1d64=dYUeiL0Sa(}`4 zjD`nPNFas>)a<_>xWISI)(R8VtC(@M7nkQ}5+Etv14FS(pz~AIf9pv^^z5t0L#hXG zdAHkl*;Pa%;0I&A#~;%qE64QGS`8~(%QnzLOOsKCfIa-vQnOh)L`Y1l4e2k65MBb{ zeOp0Es}0d2|BJi-Xc_oF^sNTb_vQc4cN>VM={~v_-q(K3-rO1A7ahFbyfU=S2qprx zSv5rU>VkMyl_pJbeCt+~8Z**+KY$IJG5~BWPg$!$FRKzi;s!WcMb{ zl#$l5J`twU<&A3n@*QA4rrP%N5*oLy$uayF;Lb{nKe-{rWp*P|QeL^~?|MAnqk6m> zgD4{VpxQK1cF#4TDU*$QysnfLpYp0dx1z1$YhQ}sCq`nXeB{C}QdLliWPc%x#}O~K z5Kx#bgc8u3P>T_B<+m<=ae~TDfgJBN46N;(tDirWtq~%YaI|AVk5;|P7{SMMjARYx z1)5=Z_-D83m7R!~RJ!g=k=t$3xfhK{siAuCY>!5^k~^NK6i?aPSG%5->1AQs(XW$; zU=+(?t;)$Nei9M$iFD_<#cL!|(jpeg?&UZS(e-JtL*u#7iYpo^scP~x6tlscx0oZW za@E;yOVC+bFu8xf2!nXSY~v4`osnKK3e1nuZ=Ks`sOBGa6E>DH=frTX+t3%!40P^` z?JWfjH#uYba)6VY2}k>6K*Os_6ZxdRQL8OYN0_VR-PG$2tq-!@V0&8ri`VD7jmsUD zCZPZ87RFgc!Rvj*#WUiPFWwoflRy6pnMN_n$6u6J7kXhNP2o+lQ;Z)*A1%EWeLr|E z`oamG4YPbOP^zq;Gzr1tiL8}K1@cC2@~d-WSUOg6fjvO+2!)2kjNMzxF|1V%bG@Se z<<#DX-ifz#68U!@W zrXKeCS3rj`T4wHd9+uuWR;_Z1zGz1^@O|M;qi%V98W%MzdT^%4aY-%q$?_lJ*uD4wgyILBuY3>ux@*fF*O!Xa4q(trdd7q zM^no$>nrGO*1DXdZJ*vR(r+nF3QR|tjtq$D^L^36H#M?ExWFgzW_K1 z;XC&`CJW4pfk^ImN2s7%to~vhRnuqbs0*S^m{OrFt^!@GgdzQBPSZL6tuhQHnhEnU)2Q@FG2+_ZoAR4F6H4W8cnA=X zY^ebD4LwNys>xz2?W<>#oBqo;Sl%|_!VkVd(dOM#QZu~wbcy}s1lm9ZC2V1zTes$vemAHPIz1Q6y%a9^?ma#5w1U1BVraBp z$8zaqKa+`r&%BIua^kcSNx&mniygd}0koIS&EN_CbQoh|^uZ3*$VmUMZa)yg@UG|1 zhZDoZheM5!G5GR>Ntz;>=n(*k)?`yToX*q^$_m(Ja)7tm6U7`a-0KRdP6SuJ%4G*Qu(V5bC%^J%$TXvJa z@$2`y_^kYn<%4|KV4((FEQUtp-zq?psE;ttKls{un?9d5ZC3HO_$C{^DmNxr2m3AT z2e7QTU$FV`Cl>y*!a)n9P@>XhRKcKOK#tFSJcnTbh8Y?q&SV={ zr1~X^Cy7LHK)JUIa%mR&=!KA%MHys9{&Xg=b>OrjfP*7azi~9fnsMGjqwF-{!5A=M zd(EJ{rZUhOb&V(T#-^MUf%`_dkvgAhn1wT$gQhHyuE(KFGlzGo?Vm51ZO76xyjZEq z{F}8EIF9%#76Zxsng6F0i-~;J=9&ZYhA^vYSnpZqOIGd~v06j@IHB_#=Omor@6=zD zYLwQ=4eaoCi83PtjSn$z?$BnOG~N&9dC3j2t7#0H!-nA+NF{#iH_xAbq9@lh7u;*1 zyt6{ryq4+POSK`h5>&LimQjw4U{?E8eZ=*J6BrhX?@`R1&>mHZ!Wb?KUoyaz)58En ze!8*A{VZ?LheK>lR>q)({Ua!fjp6tEBIQm2&>*k7%scZgrAVq6C&s36R%KVpWRoF7 zc(h^uv}|~P%bdQ?z~sOmdoz?VEmoo|P0lf?YD&WvEwmVz-+qyb zcz@;Gky2|K$97Owdtc7xN7N+nD@AP*WA=UP474tbpP_21ng8QSjauI~Umu|5Ba_~D6zl%01`#_Hs-3$8*!51~(_ltkgnF$Nu(2~>n6Zwuj zw3ezWRSCtQq1*1}Y<{sieV`4wZ2g_r+&R@V50yv|ol^ei2O8ASCYM_{thGEBF5d>5 zXC$%EJHw%{kxW*=iHhg)C4A;tU>@-83-lr1Czc^OK2fIIA*00al73@^?I(3u=uDx* zD7(S9)DyHg7_9cns5hCB)9tuGbK?D|Nz44$ z$TMOC37NkZYzKuRKD|CA?nB5+LD~S&&r6K|NdhP!F zdwu8U`(qka=@!gfnM@v`Uj}#-ny4j_DAHo=_1&0WYB}FCqQo4qr(uzW)W0z_RZhEJ zGEZO~m}*ln_Et)kx;yeETeh+EAAqc~(J>jH$V^}G)g+&~G8jN@~wyq>M{ZMf*l+aDkj3-1Nc+1x9HTNZQBF|0Owr9!#PdwVz1O{%=x;@6 z?3~MyKH=N|f!B#PC4|1(a5C~WoVuv0oaW(-(Qa}~x&f+5Ox!>;zYnj#z&!RT8g;cJy@;K6^9f!F<~$9u`B_q#>Yah1oGp*O$l{vqwM68 zi9Y)Yi*!NJ_6oT7>o;t6V2b5mY>ScuEr@Bpz*Rx=ge-rTedH7(0B%qcCi(=`%?S&R zMwymeI_+$=y!B7WY>tvy)nf@tIZs@!Chcs!xbsJi@Z1pr-(2=a@4);*%yN<2=5NB= zBFzoEWtn`NC++=PwT)@M_s_dW3s-s)ML*x}q4@}qHe;Gzr^5M3uV`1?;9ehod!{pU zd`$nDwjD{br_QUv2IOBJ7wTuDzIiZkBrO!zH|WlJjxg4=6Tw{ueE5{;>H^F*!|M)`~PhQOK<54skl@%0n$nG z-m@k>I(0Htg}SV;eLb3bdydl{lvQfsNzXRHcm%KaPzd>L4@?%7h$6k)`eBm<@7Kd% zKpuZ!6fjjMhf|=G!jRUxa6wNni2s}jdB{oxQ~Af*D%k-FXBnmZ@A5pRppIgV;Ew7G zMHpwD5;*5|p2Vt|t8pj=zOx=^c^F5PVGqf+y;})r&I7nmkd_cC%^02wm2mEWECbSV z6m{l%Bb&^@oRvNqd`=O}OHWNToYSmg`WgC-c_2kSYM=*XtO%|O2ErlyFH}}o6=tPq zd(D#h62=-t*6bPx9dr!|mSm#I?}v1HSt1u^)DT>CszRF8z8S+0@f<9`E4Kz&9Nr}{ zf0x6tGMmMAnr_XzuIHqy^dUQKE>t#EUxzFE$dHJ>=VL%V&gu6d28*y*v`?ZOK$TKw z2Q58_Bd-e?ryw_i`v(V%IGh95DXH=d6xTT>N1|-z0C8&*@^N0j55&-H;MQ+lssw=N~vt!lbBC^xGD4q(SX}7;38B)&jfC61@gS8?o^s-%u7iY`&SM0i(!+#u~Q7=2i(F-_9|T*m6L zr9+|!Mxe-Wu*j@IY?am0rI;gDVN^k}iKjh>JB}|e&b4w%up3P*6+}NJgVH}l7pHY7 z{4JlIrrjPhK?%>Jv^`Q<&wCo4x+od-iOu-(s_ugST2^C*Nvp_GzB*QXI8oasfjvJO zgsqLwgF2x{yoEEcKZZ&jsW~GFgwr_9m&Gc>2}dfZgKlkSxVO z&{+JAOD}Pd3wO}s&oZC9IZxj~!(OJy3Zup7gPWv z7oz|c0i`+0IjVfQb+J%zGd9POBdi(H&fE2T!Hx>)o-{%C+Iv0?Bh5%tAC-g|=X8ru zP&1)WQiU7vu}Cg6^p{-GFP{26>sPYgY%D7&TsW?*>Gus}A0-zAs88ey)vLEjKC)4r zQDB-CzkE|!-`!;EuFQ!Lo5xwaOZ=*8bBM*H3VSCX?m)9zM!!}~_`X=_aZ3OGte!#N z>XQ+wry!LU>Q#|lGZh<4QdNPlW~$1|ud7l&m2W;4z}oKOf}U@GG3e#yXLP!Z?F8mD z8xjYkCD1IAH+}0{7F!M7<%RlHS%zc7wdJSRnrvy;){%9od61UemGXMt@3ma_A$CM` znR&^Gd4Ip@9PN5f^}hNL%ZQ=ijc3rhaxAe9-iUz>v!2Y3GnJJovY^@Pte&1;V!plL zHWB!l`z}RPSG7D~e#X%5X(~}aO8KSqTiQuNN|EABdVlxY&TZED~(C3smgbs|ID+L5^v~K?lW&NIi;(nzkSaQ{zn#L zvW8uxIYm@b?F0^`dyXvGXjz8xK$|wHgxlljZ{=ov9LpwiYKH#9@p$tJOrE8QPNq%N zVeWT|t#{P#IrC(?{Aj`^QT9e(x4P_6qSHWMD5k!ekHVfH5^%O9>P=@g0k4_OvE)^Lk zUd5Im3LcI}N_OGDxysnKw1tSIq!w(IVQS@}M`g2CVRL5}P0KQKI%UsBI(({PmQsIs zJ!8QqNUdXpKon>=X6fn_ydYRwSuJ>wzPM#Do47?sWA z!E=iTJ_qDrM_EdX8;>VBmGh`TxfonkfG5eaO3Zmn&~Vbh2Z&s`O_8jWF%moRlQu$a zNj+sszDC^6L+sENq)1T#@bn8O;?DPXD*|5x+$bkG~+hI?&OtwS5; zKND^)+3F3Y=1JTG-oTW2PfSjo3dVzHleVZZ5!fUEAT?+I2U-0Ds=euZYR6Mc^A&2Yj^bj&c;q(SE1Crfg$+{DZF zw@zUBZWWtK`|a6XuQutSpxjuU-myf=R50@m5k98`5{|3{85fp|gExa27i|NY7ZQve z?E@1RI|nxpF9#P3J2xFWI~@ZGn~Jlg^hb9q3VLZlPIe9fcK-h&$c-aA01J{p?tkQjz>I0&iyU<=uN#B7?UaN3b({x zYj|I-If$33h|0~wbBY)0P&$%Wm7rcZXzdcIb&(k=Pm`4*2yzf=-Q}{6PGcTQ)U<1_eMCv)n?#;&_%(K8ycXWgRzebi)cV zgCJ`S=0N4cNk#e8R)f+(bMBZBb`PasB(DU#4`unnh~u5vI>D(#e0EC|9_7oD*~Qmd zeRc-u`XS}~MtI3fN-P)(wpoxJ8VMvRhBS`(VmrofJ~y^rNI)?mKX2dFUZBv$rBjEu zk{;!$l-3_=VffX+L#Se?CfE=A{&R6d@Tj1@Vyr-VEo-a+zm4?a* zURvU^+g8N*@>8(1GKSY!H@5i+OI$bAr9M1=Y84oJ@jLm)Yw>9W1KWzf4AtQe?>p92 zntIgcx78w@K2Q5E!y7QIj{&<4ndIsEU~QP>hopYU8IzyG(_CZmerSlc(<@oXX7m@| zkm%AAcu1yXeR7DLG!>E@wlYH>Co3*>>NK!j{VuW(Dfl_nG?sk-KYq{dGt`4zhOnYzX zgtP#$M~lM9UKNkRI4;oMp2d5}vC2xVx~`SECK^EkwR>kpZm$(T$5t{yb&)F*%?Z`z zblW)nV_u!+7wnE>9Kw&htG%*uRDN%h`0~2^Nmfzorrn=e2iATbpsfCynnbA?u*G&l z=!k)r$DAU6X2TC`|DoHb>qkIJ_kDx2-rsJC1^c9J7F>21@)8eZv_f9&VK7z4gsy*r zzvJGyN@m&uuTPVDrR`5`By9QW#Da@koc*Kd3Vc zSb6ONkyyZ2DQ(#Jw{}%WUvlLfac#;?_;LH&5?7u}_7zQ(clVn}^*H2d;>=5q1 zrNk4BXy3yoJ|~fqkPeof3sO)O=>U`g-`s$}h1`rEUPcwfF!O&)@H%qj*zE6yqKeTd z5+0Zf zDH@w-rUDH|%HCrJH7U5&oZ42EjFoQC4U%U9XKV1fYG|K`8L@OIs>QV>now=hfbHqY zpjoqLtl&_5NyboZysyDZ@Q~1hlU~tum!y~vg~gIGGOkR~8~s(o&OS8y!cIx8b8kWy zAl$@v({YST#ZadLcm6A^z z#&6(*KdwL_%wcbvKG!?7_Lpy+&!6uDyk1s5MvN|91$aG4Wf1IflTV~yo0+>(9sZX) z=gTN1LZb%qo1n01+4@*faB*?)qp;~w7;#W=QE))8n$FJdf3Z9i>=c?PYzj`+&VPUM z{^zGOg^{ozFws-9((k#u z$QY|zN+AJ)}dQ3gM&2l1k*6k(9X@{+J9RElYb>Bg07olr#-$4M(;VZeT$lPx;otd>Au zw`qO%k>y>GZbUh2EL_PfZIKL;TnL9v%G}~h6M$qLua*5H>AM78d=)7^Xl3b~5CW=P zINFjap7Y<7gA4#h;JRdMNa!7aXdS23bnpa#u+GU{;6iI7ZZ9JwzKUdBR42Ki#!vi1 z!YHmi!G$$5q8+&|!bMa+upQBnZCA1p$x&>bY;d@6yjApbz!$zX@^GJP8`&p*9coHO&rl42Xw*LkK-kCWZET~jpD_w zi*aGmkC0`>bfz@My#y93)1p3-SX1LVIkJ0=&eIQ|!!TSuyDhE$IJa?Lb8 ztepKzhg+AHcsdQK6|wn4A=h+75AO4~VF|_ajW@qc+5N2^Zw_gsjDKBIGFn8gDFesm z58FRf$q~!a@_)%SanLUh>A@t!ra(qHqyOOWiTWUy2}JjD!#P4z4(FGwDk)n=RArI; z7kGrN`L}vV+B4H<@*m0}IFBzF{~=Y6s9O4$l=F`H-;QYuHI4*=%-P0|(L)8HhfR~K z?sjfR8Niyb>u8cD{1C((@Q!ZDHoEsii5m4{AY{;MHeJT(yu+sEpM`|e)T4@5l{mk3 zRe#U|(PRNRi_j#SGPpZ!Gc8#mm)xj*NabJvC)4twb4){|QFdUnk6aZEVMa1v|TkY!PS?#Jt9VJnC zp#YZb0?0&0AA!my8EOAKs zheh&g$rJ+*Mw4Iat?zA+2R{P%vtz3!D-n2XIIUwF31Y=lIF`ba0C?FKUD$(j05nML zMFRZ(EH_gG@>&0v)?pi>E@S|eAudsZ+L1C$%G_6>3Ldalzc%-eG*Nkd%<`g-_3muD z?(DEYN7QUJeq8QCk0MeE2e_8mOq+!#}mL$S$55?e++Iprai-jvax>T z(K}n|lF}G0dp1*hp&L0Pu*^oe`ow9_dNw$mZ~kACy8xf$g-%Kk6Qx-BAbu3m4;tAL z?fu8-z7wA~hSv1kwKmxpA+H1iRk`!~=1oHF2L!o=Q@16*k~xUi$ZaTp#deU)iE58h zM>m@Nt!@}`Txv8o=IsMp#A+i%B%pS7d-_9_o3`XuP!e65&yYBqRVCyrVjXE+iaPSK z#C{ZWofCui)7-C&rBKD5g!cC;QdA2h())+mrV2LUsFy9~4P7zjmq%ADz^$M|sNroO zlq3Ad_l2uZ{;i&a^o=;tdIGJyf1RQ&AOqKjBB~tPo`x9_-{wc{iB)JwKY-h#JggB% zyQqq5DpaapojVK2u*=R)5?2v>i!*4a9x}q5?L<`zPd#h-Ivh$Jgzqa%EM+9kPZTRr zBW)zlPY^4Wf=r0D9CQVFkhQr%rVEU+;oKD-Q~+=iB@~@>hW=I6#n6esU(c95;>`P3 zRdI4%x6FfCh0t}lbxG=(VdYW9LF#mwAtNUAb#LWjqL?ZcJR$0YeNZa6VXvivy+|tC znX;A8ElJAKodTsEokXBAi09Fkq^X8iNePA+RY$R{CNNW(KBTqCR&$np+?Z+hD|RUz z&O|2DoHmGPPY1x0J>o6=*E5#8>iR4Pd)WA?C?!z;^^8|G1-f>BJ>$ac2$mCq(QJ9i zIDrX(iHI-wmVVF?AVj1_ZEIkMxEeLGAnyaW%Q_oZjx6rEi4HEj;!duo+(h-6O*J#{ z29mEPls{w82}AjDF9lB&5|T(S!~y7w@O{^`nZT!NlH9-lB#{Llioa9glEVM^KCm`_w9o& zai`(XZTwh%V`A|J8`xy-?EJHWS)e`phq?_2b6*4mVD9=gLd7Ak?Esa&VlhkZGmPy> zeSN16f~O?|LKZRcCPKs6E=XwoVbh-&hnG~w2=rhW>pL$TRzy5s=$1zzqzy<_f9Bp9 zgHj7&In@$72cKb&>X151GHIK}eCo?;=Cs zev9LzhM=$`p&_Idq5#pLr3N6LV=)ji4IwvB{Wk0fv53vXo`l)Y)vS)p3Z}yqc7%3k z>AcX&aFD6T4zCOk@d=q2)`1XDz#VJ=;LU_R4E6?y>O&C(%K$iRV3hhbXVDD%IcHJQ zLwIK~bOSLOaVk-328d>nAtnqES%qp3R4JSeBVf@0^3epdHsU+LZVX^F;(}nd0DP;k z;9#;wOb{YsAg0fNL?9*%3kVVwofeIT>1PNOIouzLenbhLel%!nBzCF5_rXo#)~g`N zsr$2F+0Mi_Tz?|OQz%yCj0RdX@{Dd;G~ECFfeaK6fMv#tbuGFhUNUpQ{uu(`qIiN{ z!yF3`CkjDlZ7tJ4v3A;P|Nkonzz^Q$ba&O2E1d8NY)~uc8Yz z4ad|@;Ml-{u~Qo ztX&4#o{~gCU@>dhbK@UgMKx}M(mBM;#=I<^MQ3jmacxuO2Zd{4PCImUVL<@@`4vx1 zA%K6gExL_cQTNQEYpoiu2NY13IA=LTmC)x{qpco3Cy3^@iK+Btxjj*>mX6oawdhDx zSBelI(qxMWz;Ci81e`b8vaKFUHuN=wc)=1t=lB>O%S*3|_QUp?O`UeOniBdPt-b*~ z8c!14=z40}fGl8pnw`#By0>Iqgktf@2QrFp{;o&GmnT?tCn+zYoWLzPyC?mOOAC(P zhKbH-Tn`dkkj3$%-GF=`{G+_%j?GcH>WZs{1ZgG|Z4g7C`@M>r(+~`4#V1z3B{|(E zYeBvX9-hRTzD@u~=DzXEgIzzd|GKZ;=lWfATP3JY5cQV-WNyw|G*K&gnMx_|QFcz5 z?WF>-tbMM1L(0fKAJ9x>9%IhuNY@VZ)EZSvN;SVKxBbW~)L~$KzQBvkaJGOj13i1b z8Be&RX!q2rwJ+4+9LH_f_Un%&gU~IOA;YiDvs0relPt~E&J;nsX1^o*h>&DvnY>Bk z283%PbKkO1YQ>P;r)ecSiW-_njd|cgy%PgSJ`d%F>YSl3i8M4%C&0<>dd1U4|gyPpbwRe ze;K}xmM`Q0P=(6&5JjX<;ABPmHBDxFnU~PiBb{h{fN^$r(BzY%#Z*Tc{MPak>!zD@ zcHe$w2JwI);p?Uy74`6xyijhKhqFFbY^-s*I9?TrjP9YuZ)e#)m(G1FP& zJYH$8fnApo_%(QB%*q0qI25Tu7O=!zsTrhOs(mB^K`EGa6i_z)@lKbZ2PZ_z~?MmnGmz65$j9ZJu05Ihv zU6+gL*5#+C;sTL~*Av!fV`ul>jPLWS!N&!dhh4vS3R1$&9tmNL=fCxb z?ObTX8Zq>}e$YKxNgv=WM2F&<>Yns4X0k}2dul^%M95SUO)h)nPG2*M+0E`fQxfF16$i|w@i-u1pVmBB6F%&5p-@wxnAbaeZ<}w8H$9w4=Vgq&5o-@R z4>c57Y3^G7A)>c@RU6NQ7{c#_SP&klsFx1hDA!G2_S3DX2@f(bk57V^yT>xp~`kPcLWrM&jqGkR|~QuD?{xYugwvV4nBUtkJ-_=0~9ko-3IU| zLoW;{vrow+Yb_N<&sXVp*W0-y6=CymC%fh4hh0=Z0P15F!8IOabzObKfS{Pza~b2)r@=cXhb6@WlN@ zgtjWDSaq`JFk7J}fRdP60foApK%BNEnRb%NIo?J@kbBbBPI@~k37oIVuYQwxpMKFs zauO)fW2>dUShWb_Uv8U|djTA_KFfb|lo z5f_-QG@7TiE7t4DlqK=$mQ^~THJNFxDa*F`a&$c5)L|)un^_vhMi83n@K2y+8=W~> zm*&YdFU)z=5BPQDx#dv@!cH(JDQCf3%~A(!;Nui;P#Ejyvj;(GV(L}q7~5tg<6rgO zcKs@A2Y{@YO*7UwbSN{uZ#aN@hnyJU=Vrqz9Z&pzQ3I}(lu=m=^nMou<7AT~Dz*SM z^fPGLiFLH=_4o7bP>kI6vOYkggXCd(1LF)GP0!A;IT^cIs%mf+x8y2ajDPcI)r6R5 z=dyTHRt>E3-NKl3kwPhqIre#(r_1?eNxam~OBcX zstOSt!|UK{jJf^GTX}%F1|hxJwQh5l?llrh-CPf?=nkLf$n$E#r^J2LHcYsM4AwCk z2|=Dk=11?B}_`@p)NT=0u{SV&_*mB4?M!IKM%Hswh^&=Pn{zXkJTA>zEP&+f^!v zu|1M6nB0Chs|=!4ZQ4lAJ}}=Uux)|vW?>*nGDJG;(w7WS9Y^CRY%)aqEn$}wqr)i2 zV2c0y$}IXNbrGmZSo5lnzDn0d!tB<>F(DUyo`$zA-`2T0WHtYft2bZJRrsz_#S=(6 zvuH<{b6yqHn}XM;*d&r|h`9<}5Do2u|RSLNb%_Y|f*|fD!y39!~H7R{DME`N}5Aab?{XUMj_@s(HLRJ9askeIUiB=~mT7Jel8z$QQlbz)S#C?Qbk3 zmDLmE_7v)dH@a}wos+Oy>Cw=h4rbN(Xbt}>#|b>I^qZ^4_+_58p3Bj=cEnvnSnXHV z=h6$uy<#)-x+X7^yz`W&T)XP~RbjR6m1q6h`glKnRz6S^U6yE3qglbDV^zt|8}(hs zW6LZAf(+)X`xn4;63%V;Dv~>dH4RQ=(1xnXNB%vQBDa?FT2zh z-tr%+^&IrNV#>gpQH7?mnmy`|VvLZAhf-Xu9UUhDo#>fzA=>4I5;q6ijapEV_bYNq5d)!hB8tOwm zjZc)SzQMgG^(2@hg{8-!Q~t3oo5HVLb5R=hp$a*nJ@nl`^f7>7_$O&*lprT*XF|2T z+3Wm;y{rP{dY%=se&I$)i~r#O*>(dh1iz!r8-5M zOp&x9OUsIcIAjXIRPT~0%u@}Oh zD%rkYvkpmHi45sK;yc1n_bKA2??jByD*-6ysG71D{f?kW$ zTWXFH?EoD}2&D3pfnts1%`d_m55-KReEy*D#?o)OdJ5QlbZM3?N}8b?57P_#A@0}- zzwajxdZqiU{mZ8}>N^a&?u%Lbkw{OlL%d#nC|c1)L@u#KA4(9B{h`(IWunKPU@;=r zmWn#7ermQc7ACjv_aqKi3&P`)2WfXjX*|SqwXEFjBh9Tzw?xb75VPzE@=QGF-1*J3 zRJbWl2gO@27HZ?Iu8=cinunXh^rU|QqWPyo1|T9}R2PI2J1p6jU!*t&X+#NZ*shf{ zrhS^FEo?d{Z$da)^?VXQB=`VwCz=iLrA-rbbzL;I;*0cZfhhfvsGD^HDXI_LD)?(> zc$q-)Pra)CpE|X6`dX5ECw`rU+t)zjp53~w(&u5I?0fl73ordwQxZcu8wk~MBWTrw zXCr|9=Lqf({;X^^I+^)!=e~$ijpyG_YY;pk1Rzk0RGT-g2`(vs&8ndOAgV9?apr7x z9=WW9Myh^s2`LYn&}ms$qUYuSNMy51%E*_ zG>dr<=cO>`hM+4a!^|mwkth82S37h?9l@h%+mSQqq7k5|s$IXA3U-A!ITckVoi0Mh z7+@^4F_DjLG^EjvXl}YUA zim`2D)Ve3|?T5sp!iEvYb3~vp@a7!@D<8I5jIh~KCm)(X(1Of z_?4J_GPQnD4V=Kj>wz+n%G{#k=xQ_E7-}~#aMVasHXJh=LUA+xy>i&!ZWN;WR*d8SzJ&TWx?KSxsEMRmK5D6hQvJh2lPdLZCRbb}GoL#HJbPKH@xvdI=L+Z?twK&sGUpTX=WZOM ztDvkA4Om(`y4c}%<4f-u%{+54z4xRFj<13rn8DVT>EjJjlu{`v=1Z$Sbl!xYAK>aK zhOVs{R^3Q{MgHaq+L>AEh&+X~#8pIxw|%sa4AJsX#~dcM?hMOpefrI`TWIlP zDK=|`4adX^+ufuIc?mQN&*S9sy_W5^AgVxs;!T2CtPWIeM>`fElGRKKg(r;dh&wo@ z(_3^maR-OidqLoK*%YE&7MGP!XX71` z*7J&$X3p;f3KU0uN_Zwidn$gTBv!W!7x~omvh&5Lx*3Qyo}GeHh<9G+I5!jfs+l(y=mYep0WTZwp`F<<(F(I%W&UFL#zc&GDBU0N?#aXMI4geKrRvHE{gnj`1gbYx_;m z>eA>)x~^H!lIgL3+~4Nrt^P-V|KUGG8%e*U;E~ELEqEj%nZtz>A4^#|B1;COgca1Q zusa^jzLX;(?Sa$Ma{M!pU{((tpWh)(1?VK$p{a=AIrI%-e5aH_q31RS@ReGm!>JIn zn&2gWf4c6?Ko5W`#h;~rpM;cD1uvaoiW~q6aH~X%JL%`Z)TNH}ooSVwaANAJj9!}f zIDFeWYdn78Z&~=WvA#2%<+aSrAD$)@nBKyH{mvCr~)COyBnZ5LfT_zu&o* zH$B%SSIkd$s#6U#iVTQ}jSlpW_-`68Ce}YXmK+@zq>8n&utcFYdW;-!CsqFqyjwO! delta 137103 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 z z&YK*9J;d%?jafz{*maEAqJC>hx{$3(zZ+U6aI!(1Yc~VVCcqv7oxcQhfAa{~%yMF# z1E8m;k-k)l8IS@A8YiAe_a&l{E<+MViLZBNkiLir5KnFN1%LxE_4#qA`!i+`!^89; z-wDvQ2sY@vg;Z)YP8IHdif_8+P!C|Jy)!2Qw(bb(Cbu0xUFh0ETR@Ilq$e=zucyQ8 zK+mO+T`I&3+JquWq&y+(OGHB!LYr7BKgi-?1>#V`LkHP%*V%f*JYfCVvY3+)dPaQz zqaoOWyR6J&IzcqXT;6;FVE%Fm&@i0Z!(^`3y?->)0N(UTz#(IQpXAOjLU5^=Gw1;l zGV(;ACk+k2kP;!OUvvPcG#G_~2UE;ttZn=DIQ;yXVjvqrc0J-c8_4RT+9EJ(o6dT^ z!{FA1vycRbatsJMCc_pU_5SJ#ST3pJvAScAY0+&0609Yj!0%Fl4spXF=nzUS%{0fr zk&u!las>$q36lYTa_qWfjV;z3!_jgf4%WtpgfpiP4Y!w(22-96!JCL)+33Y=`{g4^ zf)7@}EF`f@!Lo;Y3}L$CtLe{}!I>-}d>b*IiKB|HFS71=-y7%9)R;5q8wp!}{Z)Ge z89|K9#vb9dd;bhd34xW1iBcfi)CcWrr`_?LzgV0ZkU@xltb8+DZz3A0A|y%d8Sg6+ z#nFo6C4}=pZCZalet4aMD-#euOt?-^s)gw8)2)SK)wq*N=o&Wo_+DSt=!h$?4Nw6f z5D<*+D|&qkh5t0>>sKS!Ur&L0qJtmk$(BX?i=~)>2^KOL_dBX0_y9<82ElS-bfXa=FV+ ztfxO~tZ9X8!g?CG_Dxk}uHvLc+Uba>d#*x#`u+OKmEM6U9DB)lzC;kj3=VT1 zNOh|u z=5gnLB5g(nPASUp^$)H|9Jp$ty$+>rsesQbjvs$#V9y{%m9QQo_6KD{{m`s&FV5EZ z;;g8_UD!m;NxMHGMc?aZQo2ZEJPKZPMw2ps&RmV$-ASGRp}*ExD^^pjU+qzr@}B`8 z1RV)P;C=}9C87}wO2wRcAsCD(xPn=duvpl9yh!itS#uP<{;V0K;woWd){}!TFYe4q zWw3^oxX#tTHwRPjRbjcy%^OC12tvj5tx;3W-0%QqpQ-H=A~lH;1Ef1&rHOd2!feHoFHDxt6p-)ArWB=@`TXmic5B-TYyP+y!?3v_UK!v zQe*ZLK>vGJmba-%3)a8o#ck=5hmAeeTYc|tMTI1p(CLGaMkAMbe+(SU|vk9wwm*7{x$TvX(5>8$Fb_qn~9hxG2uONxcdKGVB^<+flQ`$ zTi^{vmXB{Nnb2lE73vPt@DX_TPdHD2*k5bG6FBw9N?Q=K4+9Ca{KVbGVmW3I2qdD= z^Ont+Zi~|QfDn5VjLqh*O{?R0_7?&pVhNg#gqYy@<9bt6 zdEFHa{s426Hw%TZ+BW%m*MFJ6_>Br1s0bPN(U-`TT7RlMi$=puV5*iGhL=$ z6f6@vo-;`%nX!8pXF*G}nqveSK=wGlKKE7?h(bV{F0H_7>Avc#?>?#=28SRF{&^SL z=l|{~Ji~wnlyjn@IEbVog3Dlcy!+$NFxbN%e-1*zlQ{UQzj_RKEJ-3cycfa8yZ?@a zBq#))oGl7}2?GY-rB=j`4|i|gQyC=ia~1^;PXUcMiK8fpL<~YU}Mi)wn$23&MG4uvTYPoo8mZX<(;?1}uvR zi-ik+*epL}<+0k{!AsP-*2>V_wdjt5z3^&gmlE^^DF=40>Ey|9iT&3li6Lu%f>^nv(WNzx zY0aRq2PWUxKYccwRc3`{m8@*?7J>QAq)%{s#6?Y;A5%c=0$6VlC!`+9g&QYolYec0 z++pi7=X$dyP{#qfv3d=Ubs$wDd>lysZ1PTeX{#~cGBr0EB7|Z zm_PnK<R=C7AQam|%- z^j=l)1thr67X3l0OL|~3d;u_Zb&4;OH>?D*HsgY5-T({rAQ(LzW9bH+r zuQp-=ZXklI#1pf=LJ+e8NjXZqFe@RHK(sQ1CX`YO+6tHat6uk}-JNS&BF$lc_y%?` zk&sv_*N*qwxXUtbi)mr0u6jpJdjBwQJ2#sf*ou&ZT)G5n90Xr2Z$Edt!lvuCPIuXS zfqw(PNIHces6ABz6=pgjKR0vG-nT?Z4vz48j z*bbn=Z`$l>!)Vj-F8>sA0o|#8{`4^~7l_LzF8%U&$mlo&Wl$>ph{s!qMhb2t=~wed z6skaoaijqHb_R3IiuSS_XX(aYI};J~V9M40c1#%hfj0H!`h^Q^D=^&#D5Att8(?#6 z+BD~5FT2T)r@{)hC3Vx<=^xaX;BxGy+s}Ck`Fyv8T>nT}YH|X#mYUXoS}}oIj_HAV zLf==2hCY?BxYVysOb93fdti}bZlT|em700n`>SS=gi@Kis;>rSU$#Dj!~hA6-0Z<4e&eD!Q;UIclSi^ zT+i@%c{V^ieRwwy};}(M1vWB(i&yl_xMgK3Cb97 z;ds4qb~uk?f9*^_tszI+Uz%fD9!E~DPHkQ}lYqiZ9A!wL0(Vh7m^1ZbOJ&EFv!M0& z(Sp`-aV==qS}_3?!3m?>W8}D6!qjpzA_37H+IH2KmUdViZ@!s@&CD2w;s_F6aY~FC z(){s~h37~KmmffX&EsgPgk9jN*GH;fuh)y{`2j?P%N^Hukc_VojT~fKk$4`FKn<-_ z04qp@unoZJIX^Y|JiXjNFE-;5ZoZ-3aidh)Hu_Xl?d90t5(Qw)cHrs3eh7Bk2B_4> z!$C}VG~ya`Y*36A=dO~x;CGlpadN5WHi-Rwofd=Cs`0FU^l^`);sq4nAbJ7^l{9gq z_gf!?H@%bl{5eCb7mCRB_DsMTWGK%8g0BqR5e%tZ_+?$h3=3ES5svLFxXuoB*336h z{%V=Pu{U+qUe%4mn$g;Hbz{aMUp<+`u#RrX^ayt*oE^;D7Y?ia-L;w5@pfhAU2DWO z;821Pp1r?+w-60m$hw?UU)(|y88F33tayjpV^JOEVe7A!;SnXE%TYU`@vE*Mr%zBa z;^meR^BPWtUYJMJMQoj&ihP%L^;>*r4c(6y`XqJ1}~JHmIv8ax=_gO82!6oSmBvb}TD4Z-4*&!@G|YCr=CI#0_E~ zu!?XauxQ^ryw}pq%j8o`Y)v%GDLbO=EKym}e1>V2KYRYl^ zCGf7*JApe8A$?mzZy^X?3i(NR9;7KsLTGJR7(#zwPiX3@-RDoUpuK@w1T9sOt9Arh zu%f$CYrC9JA?N2yF>Efv=~{yH)EiCS4)0`t8D#2nfn9dFuJT>y3o;>bGCKUXd28%l zsE-G~>v-V?!Gyd+5@@Q%YdE-RZ#`aU5LerAjcF9YJo`gm8G2hlj-#H3Y&ao^Vb2P%K9Sy^w} zMi74YuOQHeNI^C#t983G&+Vj0%E`0m^d_m#qd(@+xlQ4+9K!!wc=_SR*?Vax};g4@UpYS;H9`w?} z?_ZNSSgv2NWo}7Q36ykdf)>13tr<;q*Ybw1MwzI&yS$R}4A_L(CA|g=` zdN)h&cGk42{dT_Koa5HwsRKFQdnFfa8E{rM#5WA3O1GIfTe$GmDMtB8}d{^ z!6;!bqM#6h$dQ%*FlW;%iy=|Ds-J?8G$xX<^HCfi9Xu%jdN%1^M2Kdt9}}Mjlb9RE zB#b~51JlaL*tE@#({%cgED(gE8~aE8IcL!<1?CmTkH08nC!IB!((Kbj{oJW#t=g_m zH5(U!Z|ufpeJ*NDRZl z!J`bUy0*GZ>Z)lL2q(=APm$0pCOqOpKCikm!3E6|CIe4RdM9+q+Jd2#wABfwo{i`LKSBm&j-3`^O*od}Tz$5PL)A7SG6c=SfN*XNvp2yqgZ@RYLJSo@AzIyfgd+87{n0hJ@Iilp`h!(HsEd3&E%L zY#;*t7r?Sej{SdEsCIKaRZKq~Vu{3YFg)H=!Fpc?H4052SIR~e%wzx^bEbr0H-@<+ z2nmC*9psYJrR)Wi63_?7JRB;;`bZ&ZeI-Ghi{b#oKm-$FB2A|X(KLmcPWiN}1vt_V zz&<=CK^Pd5J|G1Y{ury~X?vWj(=o6>Lc`667@lzT0il05P>}&;DqgAb$0lobL^jPE zC4M|2Xj07=u(hg}SqTNx)x5eY4v=5W-n@JF_tpDdn|6ZQ(`_K|(&3N%U;!K_^SS39 z;358b$`$vSLj2L?TU&$+TOydIrj;&2L4D@7k`h(93HU4RRuh zp*L7?fChglK+8iRdjCR#$lFpCsTuyDyLE6oPQAW!lQ5y`wg1|#*Au#%uh-O05<=|q z+p25r##NT5Rua-vVAQc8J8yHfF4Pyh0bbmxw`GM#B-K{cZfcp;ke2TTYJ&l3Pu<1t zkdOqiJ*2MzG=m_>yi@zG!4HKFY)FBhv5$V#;(mXu*Ikj8t!_sUq-jZjm^1zQDNVA~ zBladbC@Da}#Qo%kkEO$ChCapTz4b%aq)T#AO^Csm;x$b|>u23+9f=xgjrXQMs+bSf zqOnu#Jya2kpiY}Ecktl;r=I{?yB%crS~2_!9_Z%Muq|O`W!0di zHI=HLM^C3XMl@$wkl=Sa55x>Z`;-}AD0Y8#xUw3TPLE`N3DmEcDp1c%l}UOUpOrkM z*x~dtMC20z`W!<9hB1Td>zn3~pG*Ly#wm%;aJ%d3_jbc3Yi#0xu5J8f@F8=aak--C zl8yCiUOl9>Rvh$Y1APOyyG!jC4z*j0y1|1MLh_k=>Wi?RKwu>pR#;qxiyA$VnwNHyTC}`hvSL9b51f@>;a7&a zjAse{K-qeANJF=2*!$pFRZ*yNY0Wb11EDsGk%4<(7FcY)EDXLW=vmBuDrq|ilgGh2 z)2W%y!N@}M$9tT%3zz9iLH04J6DNP%#|Zmo`gp)}0NdW&JcuBSbz=vyj2RK4SvZ|C^M~0LDL`k;4-t2kU4HPcJJ`e0vB~nYou&?t*M6X4*|Cu z`+=r0l?g`Q*p7A8yIbjz(Wc6FFpQH2-4TdqAqgYC>x-D{i{{W1ZP(B4h_-+0Ub8dk zbTAa!18ojc8r*ic>)CZN)i$klQven;T@dpjHI8`NZ^8Plmu-TY76R}qea}wpxbOM! z`cy)L%cU>mE}>~}=v?xo#E%l0HcRSKmK`Y2R;~MGXn6D>`Xc;4lfcL&QDnc-(jXda zfg8YoBIrVr3T19&b98cLVQrI)n+TWl=>-=8H87Xq(grJ+Ft!B>e>M;^qNr!PSRl=I zixg;cShqmo?18apTc|C$lI-rb=zs4FXY`Ds?WPxNB$C5#zIkwHXSSO;vmahLgXedv zS8wi=oW&vz++eoa&ZHlSAoOQ}9|~9bvsE%%&(~F6lxdzlEFM-rHjCffxlw1O7YIjr zFk+Xy-!7KE>(8sbe<2~!l_se)RY{UAcreQ={&FacP8Lf!f5H#m{HXDz_}wJ9_iX8U zLdsyalv2b#!#kNG&69LPDn2^CW5iCtI*{fm10lUAr5QahDFeEKG2A6Vj90K;zGm<+ zP}zN*{*GYPkpZ(QeGJGKY6Q;2fU_RkOBa;N2$cIGf+w+^f1iO36{-34M_}||ZYFsT zJfU1!^M02eOpSq_e->$FY7WZtp(ek|r=hXl0knm3=NJ4j1^RIknJzN6CSsAr@}fcTJj|EPwq-OS7_KCsSPIC~rxo zfz7_2dg`qIe+u_y`e@jHC{BAX3WV2Y&4+W^nM{|9K!jm~kJk3qxV>9Z&bg>38W&$dKQ zdk@@1YkC>~AF4{Xn7p`_OmC(D) z`cO9`%898NK|wFv2znp*vBX3BqNF!38(VM=RZy_Qjw;MVo(HpN3Sxp+AGG|(6@)m6dCMC3Jy;TXgYAeSYqF6?d^FmRKjf|XxPk+5acNlvz>t`iUf4)QibtgoOaEje}@P4 zwHRs5$oNeBW$a3j^P$PxDR54QN3I$t8y83LOegK^>uv$4nd5WFU zD6k;@E-Nae{C@rZ_U839(gNIq1l8yU$3fDMH|u6d!We#saTH-t-aPz6I)tDoR?rwY zgIHKM#9Vh!T=)`~*4%7Udxy31e>%gB8rrU5_x6%BtZ(bhxwx2i6L`hd7t=Y&y1Ffh z0j9EtVI%_9RiU%8T?MbGH!D-D76oBW(r9aQjYS^o^x)LGi_iB}dv!W`P0nP#LrpviC+cc|hVtjPg(FPksTkOSyzf$yOlNU*v?fU;Az!6nnTFDZMFipnV ze>v*wsjVRPN{=_dBGvVOFh89SRSFeu%*S0PZ8UH4$#nL8JSbGwQ(!NaWbrZI zV}_$(ww?vEzdi+c{o~=&=bJE_al%>1W)G_w%>t6}XvUJ1gi$hk$Y+o9V!P4m*G66} zk}#PsYo+0q&TAzIT!Wa;Z-346%Dj(Zw0YQ@0i)fMsO81ahrc0!1*L>1xJmO%F4aoO z{L&4RVrkZSaf?=^d62k+x!*S04SdF38lhEV7I2TMbl0lRW$kWtb!cPJG-{25^^0Fj z3oR|WB$(f>#&1Q@8`q0WHSX`+60}k!L~~%vA)#6ovN7W?-6Hy>w|}C2VS;gEU)5$a zgzds_QDdl5L9q3qy@z5HLND!h#Efl?@%bjAJy)0|7~xUsmbkk8emxpKFj15c9;Wcd zj=pQj0{-oFt#+~%`K3p=SI*J*=G}F#g=N8$Y#z#7ErB%ya6!SVJRR9diy93g@EUkM z7ZkxuQKDc^K^SadUwRC#@QRfxCAAhMVQajVu%Evy@z*)*z??}xS%8=HnGidE>j z+PU0bu!O|aOTv2bpd@APUA(DY-K`7MyWG|gxAb!_9fZw?zJKG58K=34p`?K<&cYbW zW3>ewQ5Maw(0S&5=Po88-Q80V#T$A3bBaG2W+djUB~Wcfc7JsR^O(yWu$}jEz(|s2 zLo=`HUJgxz1*>vurb7TnD;Y+i|iIl)KTp$!9S#ImyMHJ3u zM}Pa*IgE!Sj(^T(e4|#<=Ctx;8XF?(;1N1vUDr?^lvWiU4iKrCz4Phu5h-jqBkwim zAg6@JA3|;`Xp7t?w1v(&3Bxh+4N}cTNjD zVyly9-a(MdYFiq!>VxH@eR~Gtgp!2C=L%|evUq&e2!Av`+vbRi;CDJ7ICLR=|1-zbUDK6z_0fB2?{nwW8H8IL4)6z3VVF2& z^MGVg{1KbuN#L9f9~qP#YmzLS9M4yCU!g2ERs52b{Vsj4Leo|2hcG4hJxM{K4hQMG zl2ZcDxZ|F})BrBpo&&jfmXJgK{C2$9cC#u~xgH+t9ml^x$Q~cXdvXh=266QJxrLsH z2o00}fd`rlK%~$%LFPsg$x>`IXc`RW{Q2b_=oi{e_r}Z`Lnf-D$ZSNmC35!*%0~;?^QU;0TQ0|2iAKJ9dZ&t5Av}g(QH=vclB&tzbD_Bn? z`hw^pIZdni^;ADDwes@MS068CC&M5XvdhjzLUC`vL*D#M!lBq=Xn9*4hs%9`hLm1n zf;iUW{5njkvf!CA8bb4sZ;EZPai@y$foIrW-AOw{ z%Rm(<3#f@8G9m50P1|(2QZ}J~Kyd=c(4={QG4|l7d!bF9kv8is0O)&RMFT$)G@$@f zXacte-LP4L@jh=RULi?bz(i5p)J2=u1-pDI4hNcjp|eRyo2J;65W!5oV5#G{0}Q7{ zndM)DqtcoXD*l*Xrh!CM{h zqcYVp2}~Gupd^5B#j+7 zVCldiZNbj!yvd=$;edQ8y^I!JIp74j>jXwv19%0_#5pAXf)lNGw~UR)3^BQ%{A+rA zIOIT-T5HA-Cfn+8s4(h(r=r}U%0Orjr}k>psLwR4GC?AuaV0;bHMi02``h;ciPDSa zVuD`$rzi)z%h^`S3dpnEa35xp#w(dzzw4$7&$xa&vgUx&a(7_Ubk_9xk4s)ShE00d z49Ebdj*EytZgK8s($|6TO(4D_j~hRgz7)d8jZ*Q3>eLQ7HcQ)o0hRTOP6|@A+|_j6 zz0}4<2_LyI3}=|9x8}|Rsyq9495~)FSS|anZYKYBA4%DNQNKH^K9zGlG*=n7T)R;2 z#!9bsRpW?<2C4@@O%8XAZ60%GdalDJ)*xm~y# z;Z2C<4WVg5-JyvqRY~Sd*n(SeCfG@?G3BbF9$LSbpj|tE$pO%-5o#39P^09acAFV$ zoa1p3L*2;}sG(dq0_fVwaoE2zM3d#aI(G$Zdh!Q`g+6=0RwHU9-Jsu5JQlk>p3S>} zkTJgMUlt4c`208}GrB+B)!fo1!Jg)R)?p9|GrkCHi6}Do#hS?w_%T!oexPUWP+wX! zmlo~(p}ZP@vgvF@(M8kv;%YLAX=MYYk_j4O zeSR&1#_^LRwc{Po?NcoLM}C>~R%aSUWk*G@fFa=`vyoEI5DS{A!17@wy=PZ*8Cd+5 z>m))t#iuMFLePLJMNdiCJupz!#R6K41y(qeSUeXBSZUEYV$UHT3XB3gRMSHP|M-yl z-Cs0+j^M$7EI2SQUHBNO4|nFn zjri^@RPKB&{E(kg{fo$|KqYdv(EG;@xO0DnA$cD%!&lqnf9a^v<0z0Z{Jm8P{N;|B z8X`F|`EC8;KT-Wjj|ydOWOHf1n_g5I( ziw)GODC!vm0lMwB=wi2rZ5DfQjewEpn1~*RB&W$X{qH-&A@#83Hr-yL;cz(b`AEl| z-OSwCyJxQX{(AZBDjg*V^f93C>*qyOijoOj;>)o^2N((X1VF}4sC4G0|#%N@TZ%&h2 z=FFz5X@9e?+pRPUdB4j=DOy#PV3`q9)YdtXr~6#AGDD4^ZONFE1SSxz(dX{Gtwq@= z)B!>GUNYa*RY6-)q!`_n_)QDC%$bT zZ8Ntf&q-T$V0XfAqndMHdx78HP0j=B25t;wkK91!svW!GfO$Wfzb}pct!ju$MPqG( zazlPrc}~|B zN-&+;#gfhMP+*&Q5D@zm=-x#ZeeKJ>NDq&Qy5HT9>Ki#mU-y z-x_~fXx?#Xul`49Pcz1Cm3}HRZR`M#ms368 zFkhpK4-`LVqx;B7A`irS9IW>3M527(Va&>PK(iGF%mDcd(LM=fK;B_dFi!dN_fXe+ zs?!Jhytq+;1)$4Ky_I-@i=m{0E7AR{qgo&1^AdhEMh(f} z?gTyLJ)Z(B7C62i4_hp78Kn-1Nyx$tEWeiQ%Re>B3`Tj-!l%*5XHJj=C;hN+6lpkG z&^r|PeTU-83?fZ{7dl>aF!#gh^rFZjERu911A%=}H!Uu@TLJmkbU8!$4WkcTl{X}= zf3Npm8Jw4Cg&l;4(J26onG<%ekjqa0MBo8*s2dIf*CBEqIc^7V z1_-)|CuBrQ!_*KA`dA$Eit=ZaCJ(K_e{;)=oH#Z~gJ0wD6O1t!%{1RPEwuj+%steM z$7V+DaXsd%w9fE1qkcl-Z>aMW7jXANWl~xrTSrd4>4uJl_J)hrHwQ9G8 zzFE%SYg zv?{d5T(*+@<;>yuOdPo(ciQ9*{pi!6Z(+D~J431jA2Qh}c_*{0G}HS{yIz|%m{Ofx zH>ohe6sp`+8c@r&4}%EfLKn3$scp5Do(k*6Eo#VA(;DGhD}1}rZv@TU)$oJn{$g#M z^ISma@X)uN6t~U5zP>C=6TTj1f2+EzRMR<--DLwynZzD`xVqD&yF$mVHk7vG!kgh( zrQ51%C-ef(+}P->>bw<$bm&c#Ta!Pl8(fHe)2vTV%<8s~qBL31e~qrQUmteK z3IHR6o~~5c+M+j7no&JRl5AV!xBw_tHf{wIr2h-ooQU~LEk)Mew~rbFfL7(E`f7gi z0(lte`t)?aG36ogY6oU@wVSikjqzo*s*7gS01YrQo9**LXi&NB@(+w=-F4|kwd?ih z_$n*p;4mEQ_J`)B0oC6EI1MF)%?-L%+oL4)gy$^V)1|DVsZ1EGYdQvB#|@oJd{QDJf~zw41-^; z^kUxPx?qw`B{xE#k}r~y6H;{4i^akmC>bsqOxuD=LN4q+$2+3;`#tk6358~VDc37n zlBqpIM^25rabzW`@qKs6k)j2NnPLqFq zOZ2g)5o0ht6aZYkpvbIBM zTUEIIW=_GIf*y7ZCf1e}GbD1UzDYu@y=%0HtzR`y-bj*?T9tSM>k(VM(_*cqafkw$ z0f!j}|Fu|HuS-IMdiors-AFk`;gb+Ibao zPS*h1irbVXD|s^Sg0ro7M-3k<%s-nia(T6wNQ&o-K?c{@YM~)3i-5(uP$Jj-MpBJ++^ zfmRLFJ3-z^61+6V2fa~JKsBI%mw``z8k1}?#Z_8Pgo!?cck5mvyxf)uo4rK+GBTxW zKaXDp-k|sNnGmBaNNgswvdvt~f;51d(H43ITUUq7MvW{>P?W~-MR9rCRTQ@BVtGf| zmX}iZZ`$^?%9NT6YSHs|@7}+-)rODQ%;j-nH@wP4r=T>;mRDT8h^v(uzdyv!zilJ%8fP7C{ z9-=G^jbm!y9{A_Een7VJNryx+^1aR>FW3P@V1$XU+enN+eq#2ZGhkIzb7Dr15c%1G zdnzJYnsW;~7B*|XY*j&RjnKS*t2TxfhLhnJTnlo{8EnwHZ#xEb#7h-m2H*jbgAjjO zu31aPrs~`NU7nEQBNT?dKDnU~tLt^iCV_|A=tma#4-lX)$V!GBEIqC0pB8+{P(@^$ zZzzl{1`H{pl4$|$)7X)s$W6vzNY+ax1gmD-re}`Tc+j*A1}b!dadYT@#w~gsvHCiZMfh zCZXoJZVALw+r^#{2!>22o0pxzHB}J)wkXJ3K zFs)Cvqonoci#UXUB9j(>gfVjc&Y9P$QO?w18JlbJ9yFaarL&-9}Gg685HmZ)XSaxk=aWg3Q8KF$m{a>=}sM% zs7Pf#!&+M*v#{tB{1{XUjyObV8jVLLteGCEuzm24(caYmd4ho^0XZ)~qt2_?YVp z{G@%b4m(G`(JbtL;0;&0{sO5+YTL|dZErF)v@~U6=Apn#jz;NS(JycD>I!L5keQ~` zeR*^C<;(-MT*q^QC_*VDl$Us5bmZjK*?i$T1$>0OfD)8Cdqr9~Fa&t9G^cc~&;B^_ zZ%hD@?Pii!h}_VO3l;u$L@G#&U^IJx)CeVR)(IW5h*6S%1P@Xi26fe8?o=Flkn06w z#h2uW-l_bf$@?k2GarQy!W)J!*#Rch23E;fX{d-t;0_^_0k5A?JCsS`M*oZ6l2;l= zW6gQV_Ob)qGup>4Qi(8_yox2yA@(OSG02b~W;PMK@$tp8>*r73j%uH-!DiOLcG4YJ zqL+QznLE#a%!6hPijMLVmuPO3k^r`AgXH_yXYkbzM+(Ev;)G!n#=u73W1v{TL-e!O zWKZ@{oOC$+Zf40lOK!vT=q32z9@2K#Le_O=7r-6aF8>i2-H!~sht7ooVQ>ddAx!~9 zKZ91EYDQiXjE+E9{|jEvz1s?9Ze(+Ga%Ev{3T1AWj`{@$m+=D#6az6dIg=6cDSw?< zS#R7n5PtWs(4!4x72O9~1jx}CZHl5uT=c;S3|iu@2uq|&O6#P@fA8>INmdj)PrA6n znQy-NX1IZO@B;7hELdD$U7bCD8;4%$msyl~S9>ok0zWNdFH3Vjic{~Z_O8!yFG$Pi z#V#)5^B?hxi%(a-I2+I3M#aQHmVfz48o?L!%Qr*}h*tPwmqzJ%H4bclu?x@l@;N!b zmG^kK$C&B`JtK4w`hL&2+{fD`xnD$)w+lnROpQnIu_N@LS0ZD*9mRmTzwmAr-YzWs zEQU+$mr0>*T?>Mb7jXohusuf|{Db6o*>rBpbBHif)YSPk#+=dxx9mRsyk?a|~(>ECP19T)ufFjeL0h@;9>sk_muC zhN&NeG!WRha*F{ocoRMzjNF3pF^H2E8#sE7<@4ulREo$?GYiV^9Pi2JQ~9i5!(=E^ zY)Jd9v;~QuZZn2kbl9@x>wdTq1uDa z8yT8zPTcpb=HxKqzs*Wo>r95ZpDYsjD+t5&77)Q4 zf$!JE*HQ)di!5o8XA^|z zaYgn7*GAq3j?si>>VLj%^+T`ndQ$m&$gmYL3#rX#4l&JQvm8K_0fqQiG!Gfm5RQB+ z@Y5@?KGw5Z)ac6|@#W?yZe&5-=mp!=8RoGc1WvTxl6Il$_rP$|2f{S*vsyGs zJ9Gdsn&3LE?4%n_eblo~YwfFsdO~_l@+tmoRy5?$U@H}JXMaV<;fsGEMW8&tlFtn> z9#n{0#nabO*k%X2uWV#>)i#a3=ZXwHCr-%Kgs>OO;+Vx|Oa6iRd4){-q8M)l{%ce$ zv3LM!`Ux_6`-U-r%@ne-M~CQ$+NW1mC8^UerI~UAeFILyMOK0&**{=hK992G<%@sy z1py@gZL=&beSb)hW!VQ+js}SP2Fu`73)eJD0+91$(f0M|Te?g5QLY)`iZ9PQn(_S(|S@kin4ICRpPOKF+d z8otAv;#L?Y%LCBjSVGp$mCLZR;r0tkNKGS4zOuOqaqMIkQB=Vy?d~ia0 zw_(%2{A%-p6PiAqZ94@j5T~(mDq9X4isE$h=#+{_fF7rh}VyOcvpCp@;k7XD*wTvrFCtj5WgcEwZ7!F{mAy}9vv!DEQb@o4#fEE^)f%*j$ zmzWO*6_->Z1}lHHSKDsfHV}RHR|x3K8t^ir?n(m#Y1Ro^)Jb3)=!267TH@}i)@4C* z?YPLtcX*Y!Hsm-(pG4h;b7sz*8HT|o2!jtt;lcH{tD{%%V;q#I%%Uu~S_ilYQCh}9 zmgXpm)8MKOt{2yg^!3o~j+b$?7@GbVF5>I)*Q-CArB{FNqhewvPf?Cbc%%NmP`@|- z(BowqrHiI-j+b~L8q(?qoC>`d*7~}GO$68LJ*TzYDp-O`VNnK4j8T~y*S>^l$Y?cm z9qnr>&DQV;qXXwWj7wagEQU*r%A_z5Z#jj1mT?g;-pJ+YEWQGxyeNEp+TDKN-B702 z8`?{cC?kJjU>2&}MXuKHNWsctgww)@Wdbh4AT9hjoZP_a$hSrqIE|1VG($g+35N;F z^4NlU+inKdh^@hO-?S!MY%g8v5}?nv{saOHGY5 zqB4I&MVb2{UNu~hzA`_aQ^8aeDowsma=lYofBhW96p8~|IqSFv_g!B&JgBeVbcElY zex0w8C`3u-YD98rHRTc*l3da^;Jo7U2;hc_dkUhU@fJX-)X$))A59L@R{NIdaxi?j zYlyYP#d6)4ZL48mKtUqRJ{RYgE32N8Tu*-#3`kPPeUj+_AlMLKEq*tJr-EaaJ6fsu zuN&(SsPj|S`2z^DU=Pb`kl!KPQQE;uxQ%bGT0$UBl z>IF_SFI_q(_$7n~spNb%>2ZpZGO;;7htHSywApM`W;7Fpx}o~!;#}TbuI3fRSoMEp zyH~Y0-&^AZMX96U$`n&S2wP`n!3En_V$Up}1})Wtc2f09Qfkd{;(sV_Z1NsLlYQSj6^-ORjN)v3u! z6lU1>*B0q?j2 zxnknc7?MWm|K?YNc&)2RJ+!^U9mIeEX3PX9L2f<#_rR2@9jaUNZPQW55NWN|mTx94 zQBU=>etBeeq+zl;{b_VQG8w{=i_H5+bElkH{7|)noZvUFl$Yerm7agx{J!Bf7SMRZ z%x{NQIQYwv$xSV~`IZMgGj<)~&%quJoOfCSC(|akM7H^VBd2dG6mVR`>EmPcyWLJO z^7m(=)RwJO&_}B1{c88r4IJER@lT2;k;K>R|Iu(HvFogt#Mb(wa3~@Dk#K+*|6kG2 z();n#)!A!LGYx&*kgY%J)3ENI7>9SNHOT!tTis(bkR^IJ)Xa3Y!*elLakHndNvyFq z#^4;L6VSgjL`7I?lQ>Vkf6lIs{sk0ZwVjut90nDZ76ugoIFrHADSxF{S#R4$5PtWs zAm9f}Xtd#yTpod8KvCi#1)8*#(FREJu%cEHYaR*@sqXRLJA08_iI(iuza%9(~@@q}(SJi)lP| zozaGkCvNDD{?1q?SAYM=%QC6%dk?%(UgYfGF&=##f4%#$Rr~VR3Hv%j4-wmizl63K zjkyBikdfBQcUu$eJBWk?78pfD*#;_Wn#XFxlGaqNl3ZTF#9}#R@)#NFwDkY&5-q%M;R3cWiQkGYBQPQ>PsjouHc8Sg*&k#dx?B??~JW2Au$x)iC zE`-Zr{fSOhPJd;%mO~C-&9>XRq<{QOg*yUv0*P1X0J}!LOdODbLy$#KGTTvq6DSzxpsDZtZ*0j)p}P8dE#D?tH>(n|Xx zAiOwulHoRr-0+m)AS)n&Y50N-*bO<2fJ(o^ZqFvjvAw5=035pT#^ZK#S)S*ac0WX}Mg9EJ+VX8|hL26X)&1b(mcJ{keswCGwtpGOg74>@`Z~^eOq-1V?&>6CH>bO` z?V-SlbYy=olUh63r;|&0*{O)FNP=SZ z*wR{eRT(l(^F^D@wv`MJ{+rkH+4Mu#TDo>jt14M@b-5(moUc2f+ak&9%D~f(rj7cy zP=839JO|uZC1qXGn8ig}lqXXFa}e$~3y3;hn(EKxg1f|CNdNXKv}(-qY~&nu5h+dy z`axcB7v#7Ad{3u(eKhEI%mQs(%Ho@IWi)a1>4W3>*iv41rS$J%vXxUMB1O z2;e3b2lNTMO=#Gi`bA3f`)3ld#%k4M8RgTdAQ5%<#df{)9DxA9+_0tQx!9TfrN>&T z$rCUq{BB{qhj6Qt01lDoyQZr^qnXaZ)#U*|ywR;dc{euQLaoWwbhmz!=pl7p!+$I) zmos4OvPcHUMc6aro374st`GA>AU8>)Sb4HEE zXS*UR?1T@li#!sxX@Oa6NlVo;_(74%^X6grA9LX8Fj9&QYk>s#VAK-9qkn-9Q53b` zFmAhX=K$kXT{dyul$Zo zCS86O%@8kCobI=W{Z*lwAb(#zr2~cpbsX3mpv4D>Nh%KL_d`Q9SHPQGi+w#y`+!{g zz&l|MKfT=O5#z3n7CaWcQATnju%ZU2l^EiE@_wD_z$g zZVY-KfY_zc=dR!1bjWYNO^CT}%_bVile(D1;!Fi?kQb-iJN@n5%^X4OG3ieqI1mTJ ziHlLFu!;lU*aC;){{Zs6SgMzy90n=@F_*Ev0x5s3S8Z?GHW2=vUtz!>tN;J`p2fj=suFFCY+QF7M3*h+32ouL3nFw-EQk|5r^b!CU2?vnO1ObQDC{#rcS}ytCpa_w9M$f z)4FS1Zp_AY%?j;XeIc_(W_98EM$hGMx5r@OXHrXVZ}dV2Uc+Td`ljMM@7Zj*k(Nd^ zZY)dyGy6BE5Wrg8%xDW(a(d96nnCaIGbP-;Vmle>%v^p~X5_2URtfgoVON~&GUk6S zY(Ti+6>n&&_24qo3I%I?$4)*i=8|*@Ak##qrPX1rF`eV%zT4X^E#-wi`^}#qHz*?3 z%t~{{32%lXA(NkGpv6B(XoAyiFQ7;#;*I!Vy{!}XE!Mur#zCfH6gbn;t~$uGEmovV zK@H!-W8Gq7xp;4>^vfR0mwm=h&_sWmUl3es<&#pvZ&dN1LN1k@N`6T^lRX_QjGV0W z_pVw)%hn@4+%HbI{ou_a^gGSK4M3$o2h}l3BBl9}?}`G~-*}Z%urQp5&bgF7d0~{3 zi_ZrN``sm+p=dUPPg2)|R7L+HFGJhnR0~g-69p7t#wd=HzOPwnTuY4(6Fz_1@RtQ{ z-IfsJl#11o&XgH{b!l%)m>T~{(uURF)HY~Rk|fZmh)&U`_7nK0lN?{5tF_cApg>vF zPsZ0>;x3uW-dqu3A)|+NN4y@Z)oWS6a+50eyW*}~DOof*MYKhY)6SxWJPTU$W;J;` zL=ACdijq(tX^kBSe(a-Q9+`jG_t16w5m!L#hL)QnO-5v&*RQ1o=0FgK ztO=%Q>bGguW>{}@Fb+Q`iC}BmP}ZqBQeG=r5VxIcWWpH7jNBPY=Er|>MLs&pz^rLK5j7!=LJv7pRwt#__I+(DZcBvF@QcwP!P2I zx_@PwG>l)Fs@JKt_ELW)+|FIE6LQS?RNo$afgtg#ZIQ&Az|<{zu99Ddas-um23jq_v_0_ycLS zEIa(i-!jGpm*FeFu~S#OOgr|IE=M+g=nX&aQDcT;Xr^uyqj{in&Y#VCXKt1!{{k|? zax9mD`UMo1KOzPd0y8+15#lL-rC3{U+cp$_&#y4BhdMyRh`L$YiU3`^3?0xF*aEab z8Vrg}#Y$z-6Dg+|_TzVWcyytyD3?7+l*x1X&UY@fH@lg6vtN(Az30ytN2lijpQS8~ z{djhf&v@doC=F(Dv|xS^%`R56%lWd|90&fqDKlN{%$qpAy7;Y=a(eE6Cj${t%7P?? zU-0a^wUkO6pG1B%zrMH6Qr-!r?K{YBj!*a;Wz)V@p>$R)X{DvnHaiJDwusW%3Fj=0 z$g->~?Ix+$qPDMjoo$4&??pvg%El<j)Tx#vl}t2T(XLAWjHHI9F!zQoj#q^H(Jeh>ubT0>lCyy^urbcM`EUiCn)Ac!kdg zIAjC@k6sgzZYo&=2zF(h5UVkcSn^Fn1w@VjqZ1whR`%GX$_yxf7MZFRTfjX?g87>t z&aS3_N<9`PL5tJx&E~rGK<0#3hZ0Dwg|flbgKVRGLDb@eMe!bL9jyjY_zJXH4OzqC z3pcW-UH3sQZ(Zl)JMmTRbg>cS_g60nOHqMk(*W?0GjHLy2z;&!O(s?&U^G?nw=uFI zl5n0f9))|^LFUkZ(Q9R-@VN=NQ&TdVAvcBnmA+aZFp-LCCGVUXwnvsXc|krt1GN^k zoi=1aCHO+X^&eF6&!gb^%;R0D{yZ?)9430mdRSJBvu&s>hQ7zRw^w0gqqmKI2^Y5D zhd-BFOoszPoY~bSB;%&riJ$(p5Ay>O`fAd1AB;|-XoNO@PVwYv!qQ;j`nHu@ENPM= zzEk^=Z!-qsi)Bfi{Y2Es+Xe&X8_N;utymU$F|B+Y!x_RISk1wV>M12#rHEtTi7bo zugIusbugxk49< zQj_zd&yuj~VIL@~RI}M+_5CS4u@l3)+Lnc;er!d5JD4kXTns#x`g^Ucl-a6fx^0IT z7N;F5T{lbJ)ZGY4S@g%pKi|7y)$7ci6NwGru&6~hXA~r;6iRgmQ7w&FTa5TH3zv{$an|oW`03;HF@JQy8SUUtRddoJM`bMUi z*GEQwngvVx)cuG@hp0DdzcCH$&&s7KPdK#7idiNspvf<|8>0_-KkDyQVG2=u7ZI2Q z{iL($ENTPW4UlV*?e51iD$|m%8=d~%Nm;LoDr@ulQ-dCC8&dgciv{<`r}KeQLUxqU zW}uD3`K|4vT@{@i4*~!VOg*fkV^^&4)+}{BW zT5b7Y%SfLcgBNRx1Hay^*J-}WQ$g}u?-ilvtIy_wdZ?Y)BgPYKA=;PnuYAcy&f zBS@c24$7^BB|hID6v!BfnW!y~3_Z+pf+7?2fb00cRLJNk2CYHFl9;seMdFKf+ z`K{KSmx1~P6qiRR1{Iepp#dv@rC3{U+cp$_@2~KqjKzv5QL@~21=_UTI$%SA1z3u9 z53EHeYOTw3agu+(bE7OdYS3;k7I{b>p4)fMp@YTsB3S(LBslrLx_ol}Hd2d?ze&Pm zak*QlIQ5ftyh!4;A4c)wGFyD~8e1<{QRr2D2j4XEo&%+B?57~ocQ2NIe_#GKa6Eq- zrek)2_S00s5;6L#$veAT#bJy*8{67)xl&$dy8IyYU1JK{^0=#z_hOZXz%W{@l=3&R z$kf`}G{$k;<$HIiGzIds8BayjU4Hk%zw)2kyY9squiV~P+}H?JAsHrZC;At?f47<=eSCQ72GFE(XEb zSZ!XF{)x>eE?kAcM5VwihHp5ya91qBht7jt1JWeg`?4cR9`~XO{aCG?7vIte7VE3M zzpWav^{yg=vX%|mpr)9?#ZN&TWOirzeaBwi1b)2POm%*FH0Wi2W4_pJ$1LyRwE6+9 z)lqAVr^l4j8$aEIPPca@csQHHC`D8GAVwdCKwb{VP-{{$T>G1~o_Fkzd(of=57$Q= z3gJ7k2%K~>gGF<&jk%Ud+cl_hzwLSl4G0klLbJUQNNPU_)2Ut)Y@p+a9pjXYI12v> zUI(liQf0uZfaq+04jq8r3387pdjzYa@SNh6#GAqE94wE>B}EXuLk>E@>wm+lIQ9SF zHS)t?^Bs6~$?I@UIjQe_cCX_=a)qM#YtxxOtuT6t1|YY#Pao4T5FQEh`1l@`s_MTV zlRpX|P=|DLF?M5J)&1U3(E!^r?cf}r7xtNhVrK6o3aSQwKwMX4c2ZR~5W$;ABG^G& zd1MsOD;#@HCw>Z+K@X-{wELq%Na-j&a>7ZyjKR1lI17z56dJ!MG>!jtAGaw6uWrFQzC)a$k1&Ubx=K8B?15eLG*&ZVcQK#i{q1FQFghl}v2!FU#A`lv^tiJC;inENs{0TOYRT2r@hNFdHbm8H$QS%u!Jk4050ZNbhU@ zjwPX^JKn!H4Q5MXfv}7d?+aQEs#TNQ_KbI-njANOV?2Z@l)WQ1+uR-y5q8GZP&>k4 zfWg-#ig}D8}5TbpU=PwRqCEHUm?vlbB3_B$H)1MkENVleO3O zpIiG+4{ZjV>Uhh<88|olt;BKbD08`a0IW(y?qzNm92FJ0;A_$oo?e1{ZZbgxPz%3mClyMzE&9 zylM?aXy$f>v2l#F@4lYW4BmaJ?a+fk(t{L#9qN;~dvj-sdT*bi%k?n>A}h>#QKZSj z4>)g`==?b?C*LR$8-5XtlkboZxc-*eCH0PPN~v~Fm3+fEo^$Rb`ExR!XCxb?oTxzd z)os3aB`NZS+V(|Zn)~AfIRU}XTt&{yU3DB0{h^0eA*46R1rvow_6GB9A=(F%tAI^^ zt~d?R;Nm5JO;;6&nLrX6kf1>3Sx-oUS-3)-a3o2aPGR63Clq~gwAPLdl7$*X2qFlEDsuoUCz$htK=9f27@9m^8&Z@1rv^u zfU}dtfO)<=-Z(3--;3Py?Ypxs4(H6cq%qT*$iAqhs&4wDkoPfsGr<+5-Pu3YqK zR55?V`L+}#(9*aCgF!&9rSRby!ykyIsqhjO9B+iW9iwiP{i0|JOfG?(XAFvcxb|Jl zrrL87d>1BNj7E{4&Oqv7z~9PY2G^;V8_*QZfJr+r4R6UU^k##F9$;(eX`Lp2M^K$K z-wsqU{Yf$IW!S^ec>hj4L_32?G<)ht>6#B-QYgTQ*8&xM8~`S5yLwgi`wU-)5t#HH zx2Cug6aDK~?=Y`2p|Gay>B)(+mf{%(6**?<0x89wXwQQYXn;34Je3X$2e$w-xOgT? zMgg06?bdX|ghMiMv7LMiK(I1@Wx_8#=nS6C^*v}SHN3OA2Mpo$P=GGfJHlk3f>q9h z1SY)U*CSUR;d9bR;NMf0ba?0}s8wk&lsD*sk~CmX9ZCl{~!SZ4{!) z#W@VN-0Q)0T~p;5RU~LmX!9b6UVVShn=UA&$r@)F_A0u5ETfm|f%Q>{0XU2iU*{0kxTusD~2`UMo1@z4nr z12Q)?m*LU|DSwq&S##sY5q{ra!Saj1swISDz{9a!(XJzTvsJ0BSV_fY`(c(Cl88uv z#<5!c`}Eh{0|F9MNnVJao|&GWzIzzW_Oobq_a@TsyZblSw{bSxgqvluoZatcu~~^`6U8r&b;e334r;M}+_uNJp7$~HmM+JCla+F~n{zeQPe{`roy=4_L2i#-)4IAS_k!P7^F^`_#D8vKvU!UkZk6kKmvi|`++sWElv^hpSedFxVAYr^*ua&d@nuL%AQvr#m+mA3 z-W9Ea=8w+St`X#C7L#jnhsSxc;-g(DEBAHPog0B!4zxk8PKd!Na>7baSPoBE1&>90 zC_F(TUW2EKfDXVp#0{=*(|BM=&+hXRWK8?x;D5_}X##ufgmx5IRtQZ%>1h(Me1cp^ zj%_na!z5V|X(mY~VUj4gbET{8@%S`frvd3ES&^7Xi;HAQI&??3Z5tU_d=k48+XuBuWpHekkXve0=xOGiXea32iEtgW z4u5WOjjBFDncdNT3+ytuxqkOnc0ZCQIoWz6pYLS??sqSkgC%RLc#nbY!Fn+rx3pZC|Bhvl3B-hQ6uFjpmt0PM1YmL zEV~w|@9zJhC>q3^CpvUgqINGw76Vcf4u98HtvzA|*&gY`itCc{cXf3VyfQ)BKOPsk zZLN&9POPS`z;F(0!pY0E3YwODl8>;ETnLHR5dit_s#>Oryi(SS_>daoic`FcX{9~q zjSP3Q?ZpZKtk*Ky=3P>QydRf=h{{$aX);mO!uK4IDaSy z-$Nx~iU*H?9j=S@&TcV2$lTa81ovB%npO|-F$p{Eh`I3f9Pv+hmj)jW6g&CG@+r!^ z*jm2MO2Z_87p$y$;{^DHZ&5$V;-)wiM_Vh2O3#r5h}9sr5@KXDjX(I=k$M%xvo#4e zgqd@|qqXgn4M-M>E(w5aREfA8JAc!azrBy!n(5Lw)@&37i&(xFL15G#WH}GKFp%Cf zTJjD(pG>fq0LGdo(we~xl)o_R z)kx8py9iwH*Ed0@-XtAe(hSRh=O0k?(79rzf4PK4bKys5(VFayG?s=E*Ze zW3Ec*+rS=mjogd?xlAa7Uw`Jra@EOT%cTned)LYlc1qk-{-E+ODA00e1%fr;D@*|b z6Zkex5mzVJNJE0*A)7Jd-I>B(RbGwv@pYdB-g8AF3gNv{^0w7YzY&2K&~aN0L8 zBJ#0g#bIW{QBH6Y8VI<0DDp<9zkB!ru6ZS+>P+Qj$zfupA)By5d4EKhphg36BSRKA zRu6*kI(qI96aIu4?_xC*Qg6ER`S>)-?j#wqPJxDn00l7iqgvXX z*e7>dUpUDCf*+YTO@BXOj*XTk#Zmh3f@3RL)-`&lmI3i=g^Jy_9ak!s&_R~=RuG@N zR%$x5ToMro>y8Zz!x&#)D*mfIosaHY9MkuN{(F6!tOvm$p==saX`80udX)+*eKC+F zS@1rRZ~nPq*91(_g*&MhLlYRmQgORrwE_2S%;mTF^ZOf_g?~7mfU%N1RyIG|q82*_ zhDWS-k9mJSx3&HECz|*X$d}=4;jfU8G~@NKu-U9GUP7>X$p(!N7blxlUv2WBBn{^R zes``Nvf0-kLUP8C%0j3jGTw7HnqC0pZfCorKScM9$U|ilI=}j;p(x43Rk&WrK$>eV zg0>b0Bsk|6rhn$h@RB(e?4#J@F_+=xUNqoiLW5gqsc)#$z4rur8D+hU26!42h!axyY&!(}3^+W{y?GJhb4Zw2DCGF&&f8io{2YUFMc zrII)ZM@q^p6+fkt=vt45mbpriL_#=Q`r{G$n#nSds}u@du55}{!zK3@u}nxggSMM5 z%&_k%jfmykN7SPfJu$KcTMSnB7J~uh%Rd1!0p-gVrS0%Q&a419;t@?GUyYoTl^^Fe zW3;5T(0|lwnQ<8c?7>2krY|5_f?&1V?oSqBlN$bB(FhG$^TGd{(qzC3(2ee%r7#|KJZ)EdYEwjd%Y;H zhDjoH?5Q*d@kHiCQ?=Vu8K&zW4@F;4(Htud z_ONVEoqs5#)7EJed?;mF@+dEK?Lpt2 z$^Xp8j2@@4qS(oo8T9215Ot^2IEB6RG%Bb=b9ukqqLn-7qi5s9*Q&n5n<`+Gz({M{ zh8arnjd~eUTmj}d1ZMIGfv@%@kZ&ek2_Yqq1KL(%=$5h@-<5D9+Xh18b%VAJV}FYJ zg}hasbce5*C*RmKV?JxMfs?cksB~Wy2Sew60fR9sltB?g`byKb*>8$1E*-8yh&u~WKNZe+>H8x)c*mHUc693SL$0+NjasUE=d#f91yC@ z-vpXisUA_1m7=~BjTZaVcbC0}(SPP~+D*a#>zwy#dzlS5pn>Or*;|tBRDzdmDUf>- zNZ!x|ei0IRDelp4Jh1y+IwF5^#ckk{4G%|36a!+USza++iSf%z<~I^F(1*#iuE?GR zAt_EC`wXL7#mGE4X-dtQ6^Rmty5RT2e+=rvN36ZjR&8fq+h!DgY;Qv|5dmox~t7Hube?DD;oJH}CGFY_$!xd7Q6yhgFmo!6u4VdA14SBwOvu)kkl4(rZ5rbI<90 zQhYxb(DUVaC>H1_tFOKa8I&vNgJEH_mp^V?RRFD-Z4 zMue4m9eMRS-gvD!$PP`{Rz_R5{Zk)!Hk_*Y zL)D&|?s!`JNsMyPbl7U@>V;cY|I!(&g|C=6*rd|jz9{Ezv6+q5pEbx``>=N@0uN00 zs1Ixxc{sfB8b|t)Ae!HR<$$yzs25m+Sr+uCyVq6ax7xL6t|S2^Yigw4zOz=dI66+k za;RYeLFcose@k7GW!~K<(FlI9Pn-qCHW>jgDH88T!B3ugUp}l8u(Gj#f@|T?e}_q0 z$;?Az0emcaf^0lO5hO)15*-)OAR3|hfiT^R#?fAMT!ds4LSwwcc-7*M}tDn^# zAS>%c4QiK~Z49Rt35YDDPp5v40LGbq2ySk{@KZlZf1=zw>KbsymjGQ-2OEP;sGWsy z;d|+7WzmheGuqfO=cGhd-7p28-)nSG>=2@W-Z-Nl5$gH|#76;uKPkwn3u?b^<79ia zF}A_eEP)W(Po_O_OEF2AJm9u3t*kg{RnlRg4Vn_{lwp5F9$N$l$p@5G`xave@os*+ zSLAZg~0mqBP!G8mAgW%YdO#PPWb0qzLFin{-Lce?%fQig74Dn7w zqBJ>Sn#rnEgop7$!10%~`e!q-QF?Wa>?_xR(ZMxf$>ds*Vd~kvDTWr|lxK?_pVj%& ze~U3<@t8ByQWo9y0&D#&iX8Nlum~ArAX~Z0QoFr1J-VkzfLLhFGmCQOzj~4}l5vD& zMt{V?OrygG6GY4s$h9f}UM%PN*Ls^@EeA;=yqn3GTwIm~(I$csHpUtjkT1&BrM_TT z{9UdBArnVQIgM@zjDs#*Pb&? zL$B!1skz@lC3UQ+ID+2z#LU4ggH_Ap8nCP?IR=llA`|cav+$_A0(x$oaNLXYxwcEf zAkJr~geuWts`(E4&Srg&eS7EqCXV9ovG2)hv}CgJ3Qm?I^8#~ZUg#MveZjn#emRha!6`+;U+Gsji#MEc&`)(ZZwE6C-k}AG}&FxRcs;(NsIG zNwdi7={SQHltGc(XHaajvqPg-8{0$VfOe@`g}TA^u5D~DL&*+`$xtOD%UU(sfnbnP z#gR3tb$u@j(@BODe;RU?R5&@nf9`%=fV*0Jkc6}12{{qg6*{VaPx{J$M%a7gQ_ z+IN*|=b?L1<8jQo#`dXyG3NyrHeH`jbUEGCLiP5mQ~Xb~8}5Wj_zz6*e>QsnMc!xI zJjwWP=i{R>pGkxljX&L|s+wtcxro7i@c9z~0!I>$QjbNEO0Yf?g{3nUhS+ImMY*86H0sz;AW71c%t8f4 zn)PwOKTFAuaK|}~g-RREe}m9T3(qyoXRM^zsNjPRVPSa0^GrHon)NGEe5XQ)K-$sj8}vS<+x_d+)nBnH zPxD!s4vd%y*L)a+W4LN_U@lkp&=>_{mI60~!I-=BMg@8V$07vse_AD5L<6Jc)>*4s zmSAE5<7005j0o6ZHhuL=%X$TP#3(6ThGVf<158mX~M z;SlKj(MDsE0YBHnwhLIOn)-;>k%#?564C}e?66@DOeN z!~~E8{VaqK-7=S7e{HgUxGPx#F3HgaJB%7s>V83~g%e#>M7VN1!6w+ojPvtVv9xzN znuT8{48PgO4~UUJ42d_Pu!W_^)8L@mzkUeA2^?=YHGYNld>j$O%)`y)87UmbU=pVD zj~c*%C#jQJ4O5Gegg#6_hx87!Z@rzutoI|9Xxw-yJi9nTfB&rf*CCF_ySw{)qsn_z zX|$?bp4fPhffEBb#b664Cesy*iu=!6o1iE}a1;j_m^A_q*iA(|#gDB~g%U zvZ6{MXA3IL$1aLM)>ZbwpvX_(EV>8j8Qca7rz2sl%}1K9o|q>OgB&a@<@N`w zA5GIa`~r#lLp(&C=)sQO<6~l?c5ky%ae8JhHq5isFrIw~<_K5lxo- zw0rYEb`=JWmw`hDDFQe)lfls`f0Y>9ZreEayl8Iqi|i~aaL7gDkm#ht#`hlk|hxnC449u~pk!>eHO|Bu^OS2s#7a*-!t zvbfzYWSog4jTcFricrOi+j4QYtn|UG)+$rW4T9IJ``dr^G_G#K?2JGbe~2K34Z8U0 z)E!UVY8{91vNaAs5@JGP#KW3ttYs|1C}T=(r@OXBTEqq|{|Z#Jzs!vcB9Ujx^Ydq8 zJHuQYKRUL6l5IP?TFYh8A}(5XxTqQ4AWY9~?eoXReEZj!^PJx-&9SMwOH8U%WO1*v zKempsqbcmxn({TT3%xIpf0xrkWa zC+B194S!L#U|y=h_5Oy}<%x(BFXaum4Zqyg{Qop~;n;#llDqC&e}7&$8_G;XX*Q0C z-*uw3pthpRgX-t zJw~=5f$~tuiBE3phA%g!wdKj_hxi~ZT;0g{+y$dVWMK@x5D1w$WBmpp;+!T*qdv4T ztfMsMCvc;vApmr>e+n}Ub=<;iKv(Pmu}FlZL0n0Src;f1wDqaof9IWsf(Rj7@rftf z?U-AoCh9(uwOPga(%_FSiTZQ-qEtknBB$QJf;ec6aRvz%a?zkw5cvGz)#7d)$zYiX zDS?Y1UlQklIqC<)kP1e|WRg;>)(J5G{;Q@I%INTfl`?TG*l#x4EQ7AOFTX*U| zjn7aU@UPNge=(r^e(f0CV774kw~ek^OOXza>N)0&{?37 zLItjl$Emfz)wZb*452D*(P2$_{-g{-k*Pj;IRLRhPQSg+1b-nM%hmMBp0rzje9eHa z9A;z&&_NDKJDOBrq7^Q?ok2h?DH)w8`FsUTwloc&!19a-IyN{OTX3YkjNUG9wtgwIASejG{seqXWh^q@i?7S)s zhWeB)JH}vc!ZV-W|Lb>#xK6c>ij$KUZQYRF5e7#<%YP#cvJ9Y3tvmNZv08(m`P5-A znMw*7^?`}56iMhw@R9J`57hWh7D<2SydD}%W{`LeU-yK0p+4q;$dXZge7QIx8<_|L zW{3<2|ITm#Maf)onms&TovSwRa-U4R64`=$>^ zOg{!PU<8Q|Jq$NoUjz{KMeslAxBx>PmrQ+k1sL}KOQ$pP|JMwn(jB8S{z}OhrPGkZ zzW>+!REq$mMgl?hO+!A3w&k|EZ@b@rJML{^yMNE-P+k?s`b0He1c7v8%+Xe}Cu!EK zxEZJrrtz9;{l~dHd*)sf7Us`4r;4O*eJ4{KDPM8`yNh?f-VXyXPuBfPl=`e+**3%@ z4|o04(ho%*A@oDz2T{D>r(Ou1Xu<4-D2kIb5Z7 zgVXQ1R(YyoJs3aWga2ofZsE-DojGW5XuMwi=aaf}Dk^#M*Zr3^KT^-A(@FPITTzEc z9_*uWFLzJ2Fs~=X^r@@Y^r2?T=#$;MG2*M4dtsO>9nG2>I@3Ja)=@8-x^343X^Nbw zUB~?yL`R%}SBAOB`oxljqdV_!U;Q7@;%haRf%*j$m%K^_6$3ahG?(GQ2PuD8OK;mo z5Wf3Y5a=NkP;K_bS1TAu(z@2P+p%-bn zkJ)*A^UaXlyYaa9(-}Ydf4x3?d7*@tuq2eBx88aph*=l~UKm77s=!-syi5PGNN;xO z$E(HF`j`If%L^F~4(gC4F)V-B1HYyp&2GP2ECU(%aF_;dMgLh^Wcy;V6n@&~G_|d2 z1`|Bs{?A*S(te2>s+QK9yF4>nlW$N3Zz%)DL+LGrU`gPVOpC(Fs?%NG&^WE#1^Xu7 zup|9PhA$<^5;4dOn3Rb#yabh%^s{OKzxRt!gT#3^z)<&o_`z9Rc6NV|>Al%mb)VK~ zr%B5!pV!Yx9_zO;b)L2J8gZt%d{QHD3=J+W*3DF$(iRoWDJb9wc&O_frcG6T?+)CW zh5{pErei%0%!BD7bM3C$?Xz1K*P?pJYv!WbMF5~DrPDWU)3>?P^%cUvs?&To=PPc# z6o4Y+rPT(cGzF59M>yF_%fZBh8Ysa=3kxu@OD>WMuh=>+_YQL$vyu>R@m#~&a^X=AT zCNBvoFE2ze1W1rDB@!@*13sOs15c?)kbhOg{j^UA3N|fo_hmMCLOkGR!YEEP=n@=Y zG&eLnAdq&`DbatY3m-!y>##PsS|-D*@yQBN{nao9H#9Wr?uOy$ZrGldM+@?tp7(cC zx*$R708F5|c#coJuRFMcXt4zeAIz4#J}$H}Vj;NO1&<_6LgY3(+} z$NPnh{G4V8sxW9Qv*PF*`a`ln2`CSZihRg>JlQrbEtK}ibZPS<;w;3ph*%Ig-URZB z7RX^x9qWIze2s|sDt3&5G_QbHQOG({%MVz#0 zMdbgih$#`wDpvk#0OTk2(M$z>u-2w6j(ZHNNMkHuPLt#r{Yd(_yRzA1^&@_@`7>L} z6F`5Wjyfg(0RGMgZ!^pL1{GRfn{w>JLw3$<>BNt<>=W2(>Yhm9XIep_SQLv1ZAYBY zD@pD1fgdKj5TW4BGmfbZQORi^0uX#Lt!I$#WbZTZOa(v^g9MbTY% zfJGvIg8caj@5epwL#0*0KKp8lzXTC|GOT~#q0f)=D;i!}{O!`_eLpin$lcO|DL1y~ zdvM^jU@@N#JFd5B)>ic9dhGX3!|Co%LS0MSYG*PUQ#VjG1|6RkH0k`Yw|!YdbG8-h zPTRQ<3(;xE0C;4A_l?lXT0PYWg-ojc?W`KqeJDjFpB#a~c1l!R*Qx`HsH4}?1{{Ah z)t5mJ6sleu+%=_*0$K{d|fs`lh7c_Vhx+P0p zxPIb#n6n=}q*N{q%*`$Sx7fSk^00S{ypSak@GB~saK@$f6icK-YHT6?5bW$BCQE13Ybmvqh{#Z zvUwt?`&J3}%RI}QCaq2JNE7*oy}4fmfOg1D>uoxL6o~6t{RnMAQB*f|dUt<|$%M17 zmktefulw8raJH?BfdRcu1G+$C|M$`yy}x!}zG_F0?-UQdbLK9Oia?oN~Pe%0$q}CqW|;ZCic;@5~X<;Mc>Zmjsn87SWdgC;T$t%=iH)Ax=hq zn0%#!(c$~ZOrpo&gT8nkC-@e)jw8<`NX`(i*rkbx)o2fR`X4tBe3$VN1QVCEPzDzQ zH8Yn{69OrJomXvd+cpsXo?oGl_F6L{sn@{`kZnzY0Ud@l>jJnhP-NNBD$6cKwF#R3 z_uY|4$#NY9*#}z`MIP^-d+vq4xAuJR)rn8fUoKBhFJ$0x&eAXx-sQ^UQnENoyeN#B zkfC?E^cE8?SUR0?F2YH!woQ|7m72c4{Jj(8o~IXoBJom|Mj}FCB4DW>do$@X8AvOu z7cdJ!-lEPnAbL{ZYcQGPc-a&hri-)pcFi0oGPU;4Xb(={LY(9@$rz*Mi*2iEi zk1F-R!3WwQ>6~lO&JFm?SQw>@i*#sUPf=sY=>-q>^$!3O5q5T%Ex%?}-c}jvAR;-@ zH#GQjDw0W&sb*W-iJijZK`RkYmTj(Qn=SYQS7}@jO!6swR?5a7pMcEL?agr;hy#d! zqP(PCjs13QKWUu%ItAec?yjxOtg7s)W@ipA@|m&TAKG0a_DE(aW#?srV3?6$qlj6O z^tp)pE*Z7z%T#isWuTvuk$A#nLU^K-VpZycAUd;D{YKNpXPj&eMDU3NsVDcwvNQm< z9`JS$l*YaG?b|wQQBFggxwnVZCY{}X?r6)#CA!sCZD`}BxZU03-zWQz8$RxUI+ecr z{@pJ6Zh00!Hj2bBhb+#oUd+E_Ra@-Y#u&aF$(t}jUb+OlAfERNlo4YlZRi~Ocd^NC zJ4j({M-Vv5!~~+)bmH~4qLna$6?I-bAQd#Yy)J7)Z~n&>A+f23O(Dbx`Mf4w^K z7Digg1RYLV?XoomiVy?KXagvJM@4e0_WFdS8SAX9noiykzBtetN-X=iH55#w++mgP zc3Nfs+I5F+4Wkazwq6#h(OGRYXvY1>fk1K02aX&x7dA7DRURLV=?=drA>6IK%OC00 z=NzoK3T#Ie%dTZ8SFUPyH5V$9-+7fXMP0Z&=aZ>4L?k#FzzLlTImd zr;1gU!|khs(3smm#VBh>V#jT`E6~aH@`w04R-~LmdjFWxa#Z--K78hn2Qu8=gCyv% zwf8aX0H>x!Zy$+&-hd?z$+xy=+|MUjeK({MY3_P2h;FoiBHW+~#WqRA@#1`Vp}lgq z-AtKAyt_ZI)Rvs(_jNAM>iwK@*L4fU0BgcP-*m;KF8)_Z>u--RvKPq5u!I8g;N7t_ z&R*MqSs8P+eaX#z1Fj|q3~;^qq2_~&V+1KNhI@{Hsf08Qhe|To^78WJKlezA+Lr-X z21)@qlfls`f6ZFUZd^AE-OpEO*={rvNs$r|1jwUq3$#FxWYN~hB9B2}H1HID0BOIz zhw?ZW`(E33?o0q@5lD9^i9Cm-D9Myd3Mo)78geFYRQ%V!v_Pexp($|6-pGN)LM02_ zI8@F-xVdNvQoE>xQh9-f2CW$)B^asy!okevw-jm>fB#}3r^Nn=u(tSw4UOswC|blW zW)d>1E2K4Kv4+_V$}0Ic!z8M{v~kGR6*r8v=3}8juC1?%v5n2YVa1es%ZH^Bs<;xs zp^ATQiWMpbk@(1pZ>+9Ug{cC=Y5^D}J0evbqdp3<+Trq*t@xA;BG!CpgGlu`bHgO9 zriHHqe^?142;g!~4rD8ViK>6&!0cFIPL|xoz=MDTv@oP?GjpGyo|d0cR<0A6gs=HJ^`De<;j80)A429CWBrigQc&p%g7x1CCI% z&w|B^IaCB+DYZid)k?Ges{~CQ0~oDIJBcZ1?)HLR?L3jRpwxT~u&&HyQGuocKmQS~ zE977R2Q8hULP51p2fVbp*HXb^P26yVj3q!&-N`e#c3{o2`B1p7Q8ieB@y2kv3ePuz zf9(}Yo0#j1<*ju4;moF;(F08Ae)%jJ5=-+-{eL8vn zeEl^Xd)`raC@g!2h2EcGv`2YY?0SWl;kf6fuH7Gh{srwQnmXVmA`DJ1zkGRjk1%&( zHjWIiCrS%NL9kDh&N(J{#WZQM6fI4ah6hDyw=|hX@^_$B%aB(1=xkb5cA)j9fA9u+ z62Kz|fQMaybV#r)kAT&718e#0^mL8cj<0r0*XP!`w=rpy$B|GfOap8yC*O|#f18=%JH&gf zBS7m!pj#ROZMy(%BdL;WY*`;kUC?+4Ps{V+X|!u_-RBS62G2Kf!~@zM9?)!86Q^=I z*F;J*Fwsw(JDIq>skL&@O^vXb!Kt($0&sj|fK`xHaP~(%O%H8qnMO($KX*@#!&}0e zIBckw;2V=t*u6>X0C7mke?}NAvl;Zeg}oR=8Me(}O0i_5V<}OK-_B5MsOqybdPmLq z@#JC^e>%Tdoi!b@r;ja7P#$JNzh#-tKu(8c0I?#}YccIta9+rBC}70&9tK{oKb@U? zzFhr>AQ;q!e#!LMa#?O8JlHfIAUWhg51GrDQTRv>qVD-E2q8O>f1ko6R(&{^}5r%d*(FV^I7P%F2Y!589{C?ET;J&( z!Ijbbc3J7aUEQYbf%-`KqB1||h|Pvr;;7#Dhp30|aZ02?4CyZQ zm5$U`suuI@99x__r9CeWF1fedV)8}xEhpJJaox}T{~$(SE0=-#1r(PEUj`KcHItD* zDu2ybOLN;c5Wf3YXwpNdHiG$rNM)SNxJ~StPLp;Tb?T1e15Cj(I((=kXgm4!-3LgK zs;o3g550)M0{F1|?P9TzKUq)w$@!7L^ZkB#bbJ;ClZ9B!!}(-+GYMoW=82rlWg@~z zPL`|5rB~~w&23Y)b~=mF$UAX@m(#1|kAFSb@mZJ-k^F@S<|zyj#_zSUdODM#^j6v` zlQ-O{Zuo0&-RO<6PEsNKWR`*^axx17v5-!otpYUjy)XTh2B5%GAZFpa<*T26Tf$Hv zcGMghJPQFgNC97pMa=MpdN7@-(B>AXUn^^>@)Kr&wL?OEOMjvv z_AGWPZr9juOSV4nrAX6cY=73)cU!uwDc9Ul`I=CH>)T?)&6XGGe-S6P${i!n0{zQO zALJpGLZ(qi!w;zNjC3Wd*PV+U)FZkEuV?Y6wRKyuwgGPlvHEarkX_RG?ZshbF~rhC zKHF5Lv`rtF^lclb_x-IdxwY109)I~4j3f`vrHJCO|87jFa$YfTWp6%WbQCnIaKq&% z)Kczi$_*S;+}a=Sx>8m-P;I{`2$0pnl#Wzv9;9^%*R&#BZ$Tf;_@+zvA3ba_ZX9$v%Z46K}h!5|}b9nf@AH^;);wbTfa=K zhM9QF@Km-JTlQAD<=JLqYYfJ@_a{r*a;59Ws&dt^0DakL?ZV5S1d|$L3>&|mA5AW4 zk`&=SWpV2$3cZb5Yi@<%C{Fo|1w59il6kpiD!Bu3xO&DDnD`L5WPiG9Jj#R*cR2G) z?kSoPVAxU09Rvin;yCu+nZo4Cp<>|U_q9(f%be&?$nU+%Zl{COozJcp`va}1sBAJhRQNmi)K`Sz{Sv);#+g*rU;^#zye>y zG94p6LG^jwi+}CKa_V#YTmI;W4N5OY!qWglic<^rzZ)zM|HbmCTmEE6%^jOh{TD&E z40~bJ3m-$My4#Je&n+=jBV)rCK2qrW4#+(yxcuICR8TP~i%WbR$J)=hH&ia!M!RAuqxbGWHdt{*tVt+;!|~N~d8ad1VV-=`BX3=JQdBFdSewz-wO+NE zzS`%D%d)sPA3Gpy-x0vRZEzHo+|?Hsu+ERZ20B|3v4|q~4+gJ2Q2)mZSTyh7q`j40 z;oW>F3JuQ4x&X2CH0qpV-Jc1>2so~S!(u+_!$8j#x99N5ce(x7hwnPa?V*=TApAJl z^T&_-*Jk`?K_-cG0^ZeclqTa8-jhIN456MnB7;`UMo1_GJbY z0Xdg(ga;~romg3q+%^(^_pfLmz`6%(u_aMwkOXk-*gyc!X5(4p!FB?U#O@}nqa#w& zZvXnN;-Sl$PMnwSB1IPK`0612&0*tjUO({{|3BV6*}bB{CMS6kCY!tcCWteVr12(+ zQxej6b60NOZQrRQ@9Or(qZ!@4YkM^vfA`*}QN`i4pAow+z1Z@gfuc!_&AUJwT)3S%GwlE+qpqJ3bUU`@eqyf}=v@BP@X&$v&2 zfni`i)J%2alcC$Dx)4|cYqu|0RrD28Tp!}kx${PK?dkH&Y&&wM3{|ib(`N|fKC-4X zglH6h?;2K_5pQ^RxEOlhwY8zZOl!wU)%AtyJF82(x2&xOZN-ZokFE5ANGpqD_P{%K zu)B)3?Ml&rM$(EV`RZxxzJ1`Mfkp+CG{l3r?@;4lE80P!eaHXoc~b~(SM^dfhZ+CG zX#*@-XSu>i3FIY}C>*nz7f05N<9*xJ;0!r`jGSm|@{5wYeFr9;KY!)2ubc+VneyBT zWx&)}547h0yS=awwaV{T9dqJ^*b81-i?&KblXqt!?FrqZ=YDbK+q;+l`QIH`JLtAm z+(E>FO&x`Fu5G~6S`TRupp`n>Eqyb%&Ot}HZ-s+-xr%df;LQW8dgoLp>K+vdwsVhv zBkYPA`Rran)FvcPX=ZXnLb8-@C`kg#*$d3NS-htt_mhn`S%xqqixe?SKVFa}B*{}C zB9eyG=AVZf8g6+ZZN~ZLhpTxep(F@Gmu7DJ`kr@ldl^IULfg3Q7Se}(LFZflxqKMEmcWS+GB}Rw*n0fUWzwPR?E;hS=Qb+}q zPRYyCQ*h%oFAPbP1oKomEl96i)*BPK8(Yqca#u`c(5J4T!^vQQ$eF_-AYSW*s8{Z2 zUhx`g-yEsNaHXpa)`LHZwd1~bA3yyb8uH(6=JLhN=ikFGlt)vJYA#e?IS>tD%|A80 z&C|wdT-Tq2Q+1iEXZqdSpj7OCl2%%0^PK@+BCL7zr1+vdZx8A=NOPD=E$>uDdxv%4QyQ@ z{@d&e)TH1XDd=Gbe|B!e^XG5|c!TGG_`$am=2wSL!njR{k9kyosPzQ0+ zmR%&*%c!;j&G2?4Erqzle`Y_lqd{Hsiu3MCMsMlNzZNZ<_J25XLQEF?)v?LQA^@9V)A;Cxu3NvSuZkQ@MMiwleEU+c1HRda&rFH7F{{j}{$LnXFAPmeK%ncT zb`l>~EU9Ay_lZseQJ=SJQG3ntXr7t2S`(FQ4Tb1`Ot-C+;=Y2`%1dYl-M@~v6qQQ0 z_61_L{~%1XHfAX7zk7fPrVkJy4VDiO?|t7lE7QqBxNU|*F%DMJNv12v^ohb9Y}|aHQ0*W+Yf#-Zk~J_ zIZ%5lv(pDl^VP9pRb}_<7HQVG1qs}Uvjp_XNtW4a6=d`N?x(va{|A`e;FOnv`UMo1 zV{8T$0x&X{U|j(#e_M~+x)FZQuP~4oxmYU_she{a3#4iGpe@>>ISce5$pcrSZKAqt zMahoOukTz)S@zMkX%|>%I3$O|nQtz1wA!zt)z5Dt_x=6Tn|F_Ky4r@@Ok}I4-71zc z%(G&ZrFkf1x_YWsUxHFuZNv2@OQYbA?Rt|$A{Y)DiI^stf7R{ytN7C>Yd5F0C<1Nv z2mB7Y;;FL3;c=a&f$GgV4pduezia5t_d6KGTYrc^3A*JyD6bs4-nzqjz5e#}C%Dcg zj>B#0R9-whq?K_JZjS4C1Csvo^)i_^4AV(Fm^Dolo^dK*CG!N3PiRjd$VMH zUN5#quv|e?d2nZJF2E5-(JP=BC{<&$LdS z+XfT0lcrnU@6Sx1P!)};)bFqK&RB1&32!p^h3tlxoD67@7?7gNB~h|qe9POP(e!6t z^oNjnA3`4grTqJ-`rc^7xQK%n)q{P(4}E>+l_l;zsFy)A$de$sTe5Nr64F=#HZiR7 zJjnR7e?uT6(RAo)q$)iqQ`_(h%7O5YR`IIG9Du~gO3M=*g>8imb6iIfA}A*Rs7akuO*kib^~Iun1Ek+umjPk z?}k%H!;F@~g;*|$^gMw^r3GEP`BnEFk7k)M%|Po>Q<#8_z+%sYS%si&)pPBR1jn)9 zwz99M8S-UTI3EJhIxVjh!wK?s2Dn2%S>}zDN@#z_-#rGR8Z@s($BteIW3Pyev%ns@u|}Z8^P~(D8qiWRA5}ya6#y9Tdku>A%mkmcp3*!&sgsk) zJn(DL68J6A`1RPdP6=}>fv>rkIw|vFz*QtwCafN^2=z>>x@Lm>@52VG^lK>mtnFA2 z=$;%==QC7}`L?XbO7lSBt+>keYVVjOf6E7*ObeE=%>5drk!e|=i}b>`1Nqh^f#SNh zQOZgJvK^Jeo7q?4SayA{YdA6n5mIaeb!i7!QHd6SVujVy^3ZE2h08_AWDSubchRmX zzn4IDYyu3Yes*BP#;5ga-?fAuswcYP5FDUg_gOL2LnwJ`jlgLx9tH9-S1;nnaYx3BMYi|}) z&4te^-3?AHbT@7bMFG*2-B20=TV#;9hpt&lPcFtOqk~s`TB+FdYzg37xVLt+VejZ3 ze1=69c_)J@2&oAeEtVN}HQkWle|ZEYf8yR}%2~MB>rV0qtLvSk?ULV5y%`3L@F_Bm z=OVv7C9L2dnrv)8Mm*`+LXL9C0=0`Iz?LD3oW>yOYb2ANT22rrB4%D^1V%Q_6VQt6 zvitQUL@lC#GC6M%aZKh#V!99x@InGBsq4-yUIh`97MC4VoG-PRrTN;7e#4NFL=fU=$S9Sk=_b7_l8%Y2hqkOdy zpi*4Sn{r5Q0+Rcw?2fAE0f5S(CKZ_XfIgACdAb{iX_E)R(v>mg+?;`;76j>|a{v!< z#1!HKzh7gS&=K&&OT}^zf953O5RdV!i=o_DH!n=FD?sYIM;Xs-wJAiH2sBn27F)@u z{B0cg*#vp#alOUD+wiq-UZZG)B9w(Q@{@Pz*GNQ{ng^p8--|i*Jg#Wsk_nG~MTVDp z!5fW(mn4(gNi_~5E)5U*``~3=7A*kmk`w(~fm%!0xTA}`|6f1_fA8-icXs;nSyyAJ zt8Z-MPX@~zNI%j8n3+3M>-S9FKb>FngM(euRoXfJ{;gx4Oohr5?_^LEj32lzL}8K^ z*I>AR%WFRz_e^~3paZZ@*!?G}H3-6)I=`4-^@GdWOCmoUUA3O%ko6hIG`BHJ8kx zd*YgdM<6mAe*B+#>Xjc)B>#BAQd(wk8wU)AygL+g_=4W|e;?Ly9AEcfG6%0-H;WI* z&iu~~x$AKApoTkIp39k2cD`qZ0f1HCd6HueOsx2E>_#;jsbL!rEb71wEc$iH)EogA zwbs>{(%@aSYoPqg9i#W2RzJ`$E%exLeZ0FIGuK8gnfZqzUq)dpvulL^4*ocb(ol%) zjom5oDC!VtfBoC5h&&?B9v1y|vyV&CN2I%%w-z2$VHp0`*vzkDQ>IgfxUvY(EJ1{t zL${M40$&diAanj)Ad;pNM4(%A5CP`QDLAV2X6Z)0V`taRK;x49{{xe&9|Ynofx-oD zm2sH(A5!Du`bU;ePjCJW!=bWE3T19&b98cLVQmU!m%jT62AA1&1{ssV(J6nGSKDsm zHV}Q!R~XnAxmYv2=w@JHV7IN?^bXWOA9fc7ZBw>TTQ+n_ns)o|ogs&^6g!IZVoRhr zJacB|49Q2E5g&bb!|DIS;^ua)!jTYc8bxxn$VNgbmc;2OjuIwSG+L}j9|IwokmHFE zG78rD{aA^>lp9m5%=q)-ht_|b|G%BfbTnntSjK2lhHT1{(L|(7rHQi$R=kBtgjOrz z9^S2CpP&*mKQ8@vC1<$X?Oa41;}dUPau!Yr@7H>_YIeE?<4URE$7Z)%>eX1L!C#Y~ zOupQi(#@bVGO^B#MPSh=6$Z=k1V?L}d%$JJ6mCIvgeiDm<)63;&6Lgfr)S>5tuYEGBC&|!dL zXLL!vHHMsRZ!~ zz0bcJDumiQkCt|=?@$>iF(SCvq{NYZ{=UAQq}{pRE`DRxgm-?}Fo}jwB}(+GJ6-C`k%=tx%IQ-_ z+XUI1G`lmi>FGA|6-g$iqE8x%PW^bK4E%SgtK90}m%7aN8h-w!(kTxQq$ls*%)VsM z4y}ef`-$%dtHbUz`1wDF1S=^{lP6I6<$3fyruE6o*6h3u^?DEt>*X^OOYJAnRDMBW z`=zg97W0450rsWkSjOT9mVGY*mWOITfo>5>U;B4O>s^;*m%+|w+j^fgC$HqI_H1Iw zj6%j`*eCP*e#)%#%9etL^T`vVP{6NHKha4Wl)Oi%ZTK^T!WVJIxoU;)Xt2FL5x;K4 z>qo?TG%w&v8Be1=F3jK65EMCO$qCA@-Az3bM;(76=+UR!gC;QM9*kaXy;!3im)fAb ziRwqA8)Qx8P(c(m84}-|5@6VAkcAkHD;E1BQKwQd5upq*Fj$JVKfRd^H{uA+?<+>9qfH{A^I;y{4CnQ1_EgIh2O1MpXYzUU3O`=PAALhzc|#LZQr(M|5HD-XH081 zoML`;V!yRV+k4Y(o)@;I9P930bX(&Kxf600h3Vjd{phb9ZCktI{2ofiMVl?Zw4K{C z-IeohTYE9r$?#dXA-vaIi{g+e+1@SY?X}0Z&xg}R$+t->a+frY77l}^4bL#+DdawC z)Je=_B(Flf&P%hZt#;5A?!6Vu*@wPYS$jT!6Jo#23$?NWH!!=J;eC^L8kOaZN^#?u8Df0%4A<7%Q$Bwu`!=ha%?9r?tiTtZ z=Np#eh$PVJWqI4lDpl=pi9p(uODCA9Wci_p-N3M1;B;cGDb;ah|PgsHVqLy@)mV^A_K@^(-*~*AU6B` z9u~2w8Vhum)fPOYdC7_gUf<{4j#u5X6|EMN=9n_5rRRCzx>8kbURN#nf57+I2Xr#? zG6Z?zWjbb%w_1+sR8!{FAa~Q$<;z)4K9ifSFA_VN=fOM9oHqE)cDy7m*0rzLjN3KF?%39KUo)S{0%7&~#_q6j2 zF&tMoQ35g`ey*0`h&j!*71%aJP?VjYQ~& zkcr$yKb-G4FTiAWZa0)KG>I*_fvfzfbbYE#9g?!HwgclRV{G7@jMh?E-h;z@YznG{ zM9U!i{6Gf*(J-O*e|^Vm>X^9DS;iaa(j{gFuC=3T-bn`B+_d2PT|NZnd69jqpndXM ziS1%3-S057eK#~Sh|iXnC5na?pYL%pBF4R7*pw|VQmg=;PS`9NSr4MHrFzg)hJX{R z+D<4p3?+jOMu7!&bB85`Qz-4~=5YlhYEkJ+Ji&S3MTw~%e`JYzLe~RCGL(E2PYR1H zLsVW-7?3Z^zvLMXW!y*xj&1W|)5uawVckOWPO(yS{uCi*n{rJR*2$`4gbp^Yu- z>bu}Z^kGM8__kKvcgWv}`1QkYj2K26B59x_@~JuzouaA3C0c$*;YC{A8yn2CmQm9t zxZq`3$>Omce+C-H7K>#pD1HUxL%rx1gr??JPR)(4<2)t|O>`2ei(+Jd5dU^U?qyT* z6cpm*;_Bi`?9t7Djh0L!4PV;-@{dtovJi_e?_T>tMeU1k`8m-c^2-<@L9KD z<996MLceJ#Zn$tWiU^reF--BE_HP|U7Qc4jgN7`b9v*Pq0mmJ3+&#yk@0chAK7&`m zlkvzoIDn9i+CV+t9lA#&J}{}!xNp1cA31b4KK1#s9~joXRv6FeNq{s0(_AO@w(4~Z z$a&rHf4eaX6o-Upz_b2>3%kd?mciGMSn3ayKuvwG9SRBiwVgUrO<(B}r;hZ^$6~~6 z>m5BYTo{qqa31%=$Uzjn8vv$qoDGa9AiaqVrd!vlLeue!ke>J`ux1LVW_Z=9B8R5_ z2MNK)xE&8aBnLl*ZWxIOykn2OfVjv0wPcuPf0=jbx-7YI`5uo{T#O?XH!xE5IrV;k zO|R)BwKd-x{x=T)Ttj>{ID?X$^j~Y{`NEBpfokH$mWAi~w!ui=@y`yfK3u)GbjXuX z_~ATtR3&eV;r*HJ_v37P`G4d64>jINfM&_#c!}`wW$Gp(j&+2bIGOSMw0`jyeOi63 zm%*$C6qk{L1{DG~Hpr9q% zW-C!HB_(yyAHTz!EGe?ryx1~Dp7WjWTxs9ic)s`c%(vgKug_k*2~saZSscXP_1eR6 zhA<&s93?0qk#}8qH;Z*G&zDgUEk3=%FF##k_&8tU#q;Oqch|pr*hgWMdP|IdQ5M+~ zK_RQgoXKU;u+}{Pvh9RA`S});@F#8-C0nfEc z)?HavT3;*qSF{nG+0pX0WYf)ma~l;GN2v#e?S5zFXjK}Vl3><^tRJkA*jHj+ydj}y zL9_4>I>XiL@X@ znV$gm6p=JFN{nEI?6ROOg}C{sWC~ibtDB3#BzfR?@cD4H;35Y*7b)fFz@EIja#^vip^XoFwp*TUAeh;m(3^zO%2j8{H}8OuIhx zD{#{CdyNIq-Hiea;ujABOEI3H42qaW*GJM^6ao7naS%jfx#NHZlkp_kel@ah{m5_N z1KUKd&G*V>Gj9&LQLzHS4>+;-bJy}3ib=V@um`vH1Yo?F3m-s#ZMQ1Y>?0M$3s<2< zrwhi4+26G-M+aJSD#qxV9n1Nevtq8?^`*bvy2@Daiirk*TkUI|NlZ>mu(TiI)@oy_ z=C%`U*1Xl;Vyvr*HjbuuR)_;oi_-2BEN4wa<$lb2v(qsTR_!OCbkxl#9?&;SUC*@r z2079+fX^nTpQ#^zKR>fTMZt5hu{mg;(RokH&cWK&U0ICXlG8Hpl+;qQWy-m%ZHc1y zl$Uf>4#-BQl499MZ-laNR1`@{Oi_f#U;&lUR>c#kfH46n8NAYh-mNE*Wa(l}t4V_T z;`&ivhM8b;vo}R03y=?Jml#??Xu%y!UAgMAt9Koc!E4=r6D+x1!^OsQF+4OFhZx~B zb`am%Exs3{EFr1d0^JRY#1f;>F~ZHRp{X(QQ|OL|J+G{!aHz~j5$<)}`o2GdC_%}A zvd=SkA`N(m=k<)37^Tqi2b-=RgHBNz_i##Kr3^OcC)M-)D083#e+Cmc8mG{u)r!^k z!EDPs@K%a{iNQnw;}Uv977v&_`|bMd%NYhBzK1=MK?o+9M-qZa6nc4ec5~-@1-yjZ zKqO1O2fejYr8d%BmEPZHf0;3L1gn=7CnyZklfV?Cz8{!9?^R977SNC$ae`wMszE3M zYU7?1Y&MR$+^eRaA&)uD;j8^j9icn}^rq`swP?GKAnJ(YWB-=}6T8m(;rJ+7@9l zaz7rWbiuH?KFrt{Kw^_@Hr0+88IFzY5eET5X%J3^cvlz1qrzFk8IlVsQ6(&@bDq zn_11OcG?65m9u?l!|$1x^f%dpyK}CGQg*B{xJjzbS)q8)c%I(yMN;v*=W zPv~3NHZC=ppKMShK{3>m|LIR=;#c=R{*4`fYc{UYH1#AF0Z}p;2b<3Jtoq)u1k>Gg z0t4b`>26@iR|uZoyUpd~96EM;l8Fc-lJsrogBtedt<^W!qZZq_re!d~mIE72Rzl*& z_)%0>OZ_df>g2tZ!ACl5t^w zgzMj~tlOnzIx7k_eAzhSh`Cvnx=5orn-OlvmWs&P0hq2I57Jw{*)mrdYFX%VEKO48 zI?Z8`i+;w+_j$>hr&Db%wdz89g6l9tK@^|hS{EY&?g#jOkaRagqOft5AVMZ7#=f?a zPNMfX#r+~yH<`obYSIt}v{$C)PaWfbjxd^U-8D$ETxE+x{ZGTz-JoFVdr@s2r6Hh= zE8&GM+FMqrY?_M$v3Z&gasTI!9q4a;5)S@{IOfkZL@~^_)}Q}M?xIMtrYqZKT-wdQ z3HCy#T`>f1KU^^KsZu9H2}3K%ZV|i-{xisF(H1h^U(0MI zG}%oXV!J6~ zS?|_jKYe#X$nJ8xV+X7D0?09hU}dCEb2e7&7p zV5U~aS3-(hmIq#k{9S}*Q6CcDMI6s$CD(6z?c*iSCAlJEcUFsVp z6CIPCrPb0t$U&9cvZOk`oGqxaC4ZW&6iPw>WyAJY zTJg%x1ick?=gkPmJ?RRKo`~S1Wrcx=2SIjkHuJ_>Hr>@RjgDAWgn4pQ`?$@*sjJzA zA=;=&f{-3`SI);EU-b29(p;Yxp9cD$1`?Q}QVmHE}@7}v|Pa|5v1V6Fp1d2DUL6g+ptej>xDIvTjda3 zceGq!22tuI0O{*TPfQnz*R?qS*p>Ue=+XI@Kzj67!cbx#QFsN(?lah^ALGsyBdZ)L zsR-G?;^EH4h)Js?|LSq=O{nz%w$MnN|9)adk<(U-2EkPvBA1y^OMcvbmcc0aZCo|N z+Eatv{-{RAg^&TYPV2~WHOa&0FU39NP-+YdEQe=tfcd0KAw)gzm_F1!#Sx|xnV z19n&}&>QyVoB(j(u+)D!pELD3^IPHR!kJH50LNhn=X9-T5;PJPnVNiUONrXBjcQ8@ zsSb+(M|TMp_E9?HGZA~pCtOn?_(!;Y0G1>o8Cuw9Exmi=sPA8fahb%M$*T0ZVi4rX zHa)01s`xRH*V1(3u<-sb2b(-Xs){?wFenLR>I8bFgNCd` zfPVl9^RM$wHrj!qM%LfYhU!c~7%ogj4mzP;`1Tw$rC6gE!)I@{R>z-1e1B%=ue!tn z7N*>)(|hrbo`1&gh{}LF#MY`AwRBj+7WsNwLV{ZNI%!f4c}NdJm_M|_v)$BgLdPr5 zSb6@gXBfOqi_@F|H?PJI#>1q@3<2G90(N<|2Gj++=eGdJSjF^v^FiMo#3T9!g--z;0Nr<7({xu?PMo_yH9z14J69X9* z%A_PIPiaRNS{89OV>(U=P;qB^GYdCJy-TFU%m!W|@ZnZ#Em9Cb{V#*SKhpw<0N!I+ z@#ec0xn@as_Qt;SX_CJvmGcdRb=RYUqWSqgP~_)teK`u)nf>`z=>>a+2plopay zHz|Y$PgGJD1>wK~%DZ^85+TZZ@O%5VNSa?S__Wb}Wr|AI-ac@}sJe(h-)E0jv`Ia2l8z(Q zUf_?&s!i7tTW_Ds??EIweSAbic(I7CyLtrI3)Q*hTW;>d3?r~F9*5t*xIU@w2j-kz zV(0g!b5wzT6}MI-!SEk50cpkXr){(b?(`j`v;rDS+|mnK2zy3mZ#9r;Fo~4PgcOMT zeBK~b`^}ePTkXEVe+(a#{(E_whx#|mOV0X--|YThhYZqOD7WxbLPzBlh_y|Fvw61R zogat=1SJL@?A-8DvBk%Qn>fB=D&@T*+N7|w#$mhn2@ht5W-CVXx4Y2Y!^>0RiCK&l z(;wp5#_FeK!x&)@{w8k*!j=P#i=hrp#>~21^YOGRy1x+=Vk;N?(MVr!pHm2c>M)U> zD_Fr6DdJ!1QlTiLE=jl^u4n#9l@VrlYR%iKpN*Q`o`7$>MOxXR!p=6$@X!a+Y8-Ed zl;P{c_g}?HDg!j$p;nwebeD}-WHp$YQb$?LdF41gqhF3b1^5{lsCW^|3%ne!PmA$)FWVk(x+#>uU*+^7Z7FHLGYgyW2En zE9WIwe2%}Tk5;qnN8SvS2p8Zc76Q=XWtV^B?S&voh*XJDC@ejix#fa#9>eHF2(#u? z{QXW0L;V_u)o$U??=i}><%p!jcxs$P#{oDK=O$xWla)J zVI;V|z9a8*%qtZ14^5G|We5B8mT#mmS@M^T;h+;KQEl;&UGkj}_JTd&3i?m3Hc~}g zu^c?w#jRaP(n5>g0@H|q5QJfH5YoWShXTdT`6$*8Rm{@YrJz57c>$Tyg0I$T(-gCd z@2F3dUFKx*gx0rHjA3Q9YeeTa``VQl4Vt>vafC;%l7(Dp4{f$IIk27r7$~oDTjqDx zF7!D!YRIhB+Yd4wG9I%r&~$N6t>efDw`FH)h5T+?#TmI&32e-9vVfNp-4^W z*{l0^`UYZg|2{w(FwJc^LGZ6CmkOF^c)RPiXBg@aUG91sm$PN(RXO)Odq)Yza}@zU zf=EE&d5WV1TfIjr<(Nq7_Nd-M*L0^MM^q(TTGU6L_&G?qj=>c6dxK46^eR&g9pWCb za@r{~41gad*vYb0&*{)qN+6zs3#rC0(FPQ?6)*0kPyj_ndk0z8D#!D zxq2PdI1<2dRQVM1MRc%fPQ{*^Nv@9)f8BGCmnI#y93x5{I?#q?Rx(dgKVoc$ji5OO z%=5cs-lk(MdAz%W_>>#JC$5K0mQz2SgPPU?n2kp^gv!&k#kU-O?qFD_!s*~oENpew zvRijeVzOJ8r+4&tKkTUP*qd%j#&u-iQ3w-30v*7;ZMzTdgN$xN6Xa0_n+ATR(XwDJ zTpQ)RP}b_t(7Hag9bDJ;U{OLv%!cURm0qdcJE-oLJLN3~i9+AUgm!gs{2G&l(>zU% z#OBcP_ks=pL0Os1c1XuVAtAL$7bmTiCSU_u8KUXp%9WFne6-|xQZVu2z5+YtwkjOZ z{s|aWjfdtHk5S-TB0np6apnf$tV(t3uAG*Mg_4_A5m5awsA^Y*&{@4+gR-*&kRc=7a4dh^uMVSxfOSJX;T>lymQ2Bgn6Ae%Eqwn>;TE)aaCZN;uZH8WSK%8{Pl6I+ zP8oUGAHVRF2mtn8GL-x8o^p{2fsy)w4@?dCAH3qo{eRwv5R3iH2e*!%`k?+cIb?!R zl#bmA1CN?pIGC`+izgRd7X5P!v~-Q}T6_>s&&(4X(`>goc1?u4;I`94oG0bd;{QsozZdgO09RHO)9z9_%S_ zN;$9WYMzqXlG}w0+D4zsKU1k+;jZ%;_aEEHujeKP&qd&s&K530H1IE<1+G6ci-JQ& zOFJwo{ZMw;wLNr|``7Z1HcxLG>52)68^`vP=Zb>vj;PvOg*xfM>H%pB0APTfgw)FY zpT*)Tr5AP4`RAq`2ncJZky)GBlAp6zvgZYmkXQ(3xZ^ zoh0RRi)6z)L&RSGR#YsH+6kwSNVEz zxTDHpLAXu`K@w4l1SF2&9S%kb%E9tcrrTK|G}ENo7ToZdd1fytfMr9XxwlWlM}4A8 zTFo-2$^vDE^jC-ZA^9`Pj0~u(oj{FTpD00VSSspwBjWn{c$M&&6S9LXz&4T&y{qZo z)C{kC>svT*k87NiA&b0c=0o}P+u|{Vnjnx=k;tKM(J#cqCVIISTI7^6czydJpiQr1$n&?HV+}GXWK{T6zeftQ_P7dBJW+pEsZSFR5Au9U% zr=4~}5QD^HvJ{8|m@v6=72H@&J{kJQx!fZp)eET|op$6ajYVa#Z;`&Z3?AiEGMIds z+r|^VRx??kvgbnoO$N_Kt}Lg*9-;S2r_rc*E~sY)C{YtJfI8kG3l2dKYWI3EfL7XX z2uvlq+Fh~ibtjA)$QtMTm-mw<<*e2k;V>$n6Pk^5Hps`O3NYYHZ{}`YrPwN$;q1BN zOMjuPXYeYzncZFO>PGv;dk>~m)#M=g6Z^8o0pqiC&1gc9S5FP{mtd(}^?& zL$O4Kvng*hpe!LH2^bgMIp>7clrDVrbd_?9Xe?dbq}_zw?l)%W!L|&}^{N3$7?@EH zKmG?shA#>@0j9EWsL6yWHj9rIg9=Asl|=WumT_AQQGLoQHqfBA<+-u!-@&w}Suxhj z4K`4hKnK9#a9VvGr=yW26Lj|DwxlduGX*vG+5YJpKxp@v629#qFkkr{9St|ko>`7o z@`N(VK%P+LmXIWlXW9}Ev8~hyFOsdu5~TRJ zAzUkQRhAx?%fb-CT+2Cz=u9x) z84t)7@MZk(MF+@1d!2dCP{s}fZ1nI*K^es(juD{;6=7a}cTo|1S3{*VhfCl=0}tsC z3XHI#EJBvh@iY=5QtuYoT!K>G{;ptql22}oGA5Kfy zVPq55<<^x#tZ2iIIF01M+;+R(+`#ozr_&y|z?%CPThe!NX_i)61?R8(T$@J0Q2#{k zx@yKsop2LMIX{gvQVvM?!$q6_ zz#E&-qQSB3^dJUB1CLcF2Y9iM&N(CKTdz3*ARA7w2xxmG0@I-ua0(pF) zZvgrOKL_3bDZoRuXRS*zH)}d&hPwDWp}6c$l7C5zLP}52v=mkDU6~~NtB2GZ(4St2 z=yOdNrKiPs>-I@hA%JCOppAP5oBShv-+I7k!f&amG!Xt8!8MtZL+12SyDnV ztP~kX6t2U`@4JDGmRE}t2%;Z_MVS-Qx?I*9aOH#p@hNmFvZdz^!=sjb13j+yxLt;> z!rTtUpR7Vb{m-RlL6VF7b}>c(=)w%mPnf#7^I+i;Y@_p$A=(t@!umj}0RGN>(g_F5s%nb$-GE>wbuhMT?#!k!ITP*$imADT)D~2#L4^fN#%Up3Z?`)GTYL0eKJ@z-+1YJ^vwW{ogEB%bL(sL}7&;s&2!&#ioO94&pSB zgVH?GHSE+dOfif0{SGfdSVOVvxr70kD&2{T#mN-Qt|8q$QSPQ|{$X&dGZNZ2o<(YLdk3x8)X;G-GU)!~4` zM_jse%8(rV}B-Dn+(7t@g+idTb%m>ZUru{l**i=03gOtXgzdHSC; zoxyS+1tH0m7YCo|086~fX0?!S^CYBSLBf_DHV@w8YOxPHQH74_olt%z1>0ec!Sc#- z*RXZDSbxqGS1OrNGAf1P!m3KK<$lVVE;mnIOjc4u+<4$TmDe<_PZnXjfh7clIrAh7 zi#_0i#aWT>Si`%QD&k&tWdEIJ8R_dlh(mi+*Lo}7#6TSi0OR$3ufCW?{KK+i)1Vp8 zK=e5B>}(9W&8aWBwMw z3ZM(a@N-s(*@VR51jLf{&HHAzK-FeZGH1;`npaWas-3>UF=u40v2tB16GeMbvK|(I zAI^uw@-nG-Kq09D_1bAF!!zV+Oyw^dFy^|qT`LkIp3gK==m4<3dFK*fxYBlQ*u=>% za~~2*Ujao1h(6Q-6z66hJ9|y%He^_2yuM zRtlxfm*tY+a+P?F&2deap1}&u+U%7`89G0|IL`0pSZ&Iqo)t}$o$B@XaTNBbL{iE9 zc(gK+J91cgx6Wpr*XWV8f^Tw0l$YH_0BBiAx2*CL;n+4%_8Ww<+f*ZPgL z0j}325Ng9e^SkiBk2@kZ>i`CAPi?XKCb%LqqrJO*_NwQNuD!EDf@tJ^mWYAaDcWd6 zJfNqomO>gtZ|H*14RE;KxyppvedR4gXMH6Q{nLat)D!@7+D*Zl=cccrpgK z(h}DQIB45B8DE)ZE2pwwl|(Qv1_5__97wU9yN{t12fae?b(eQ_y#=m6mSGyq1#V`m ztDXJwqWS#6@8si(DDJkF5`q2cv2HitplH1DsdtxPXsM1V&THE{N7)5^B0u@{`U;TomY1gN9FPjze+v~egHrJE$ z3hz&&uhHqsy}Zf`!tAUBN`Rgi@YLM1+GvZG-XFn38|!wiloqGW=9jrSeEhZhF&y;r ziep!e!x|a{1O`o=5UMfM8v5qvM)AXjWIBS4f?%C>y)hPH7wkUz?1AesIc3^1_EAtF zA%7SMHw1yH$tr3MbcMHg|!40+lIrmE3d_+Z=E3f)kPxVz*R9Jt(7~)^+ zm|GCEza15wSmT{8MF0UW+Ti<%*c^#jor6C5X^lgJ{Yt`%cmPjwhJ}WjO+C1kEhKD# zJLRJy!E?)X_{P=nS-{we=B%I{`8oQKABNmj4s?^VjTeSV+l`GZf>lHpRv=_YRhJcxj6k|bH$dX6pyx0lWdi$)PuXR8 zwU3LgbGviR!{_UD_A3^!Zo!|_*fdmbM1LdQmED@4vu3&eC?L`UCjiG;TjKL3I=H9o-l(`N? z!p`pr$GF+lF~IDJ&iPPmqqW4@PH6Q`+=m_Vmf49PRX`F%NbEZZ{pf~nj*Q8O+(6Ul z@M5&1T6M?EZwS>8y;} z;?nL+Ax*4=n67gsm)fZyb^?X1kMSM$YY|n*NaKgu;5TD1c2p(3Xb&1sy%p;yyGKXu5^gJA;>%i z=DKL`OS*JD=V~6vR8czQFgETydi3MklQ6pH*Ix%jgCPtDjHTPTd!P!mUJ^+p0)NM@ z`JBxiMgL(jXRj)CPlJ*HSbzXp8pof2*#JCwg3x)S3w7cOsGU|4QXy0XlcB#T``hxr zO97K2x9H}?aKPeuo+QFrBo>MleuGE^Eba*9SeRPaBY4&do#(&UPb6}Y1mkar+86Aly1K4V!gA~yIG zm4lK7YU9r-e2$S;+*)H?UrzC*w=M&?fdDGslCkN3G?P_LeC;~YnWEz%jILe3XmW`= z^z;KINU`+zIRQOTM?OV!;!B4JZqt@Di#DmgE>FsWm(~l)K>aShehi~eb$FWKM<&ld zN^L0{We&a${RhfkD^`@pUiq6^UTrj6rT0-veG7!yg=E?3B3*NNdaZwnW7g^g!zhLAuDl6W2F1vBSlHMIh0T?5?{C3iqY7a zyAL`HAPe;dGnHs|P%6t5*tiBe3gTl>4Hh0r^zFE`OZv`HithX){h-JGLt&wo!JxNhX38NFa{;@R=F%47igT|@4{qNIrJm=DS>Ld1JB6e1;d1OP zv>uEE=T4W?cv=!*^1xZueEblP`Ow`2LmqcUUQsVK`JG|@)8o5RWN^wO_*_lcKk^3( z1ytT#ZOvUHyZH+H7H^vS5Yk{>Sb-oG(CZmySgciFPUhn^e&~``o@$`l900HQ3qN#D z;sEN;pid)3c7KCvfjz^vNi~67m1vTJ^PmRwm5LDRY^71KGVS1dp$>ZQP)h{1y+oYf zd%>Q|ut<=x+IOQ^tAb3n#labH4tz`mRc>%aDuxx83!c?HAin2Ac-#O z_#}9YxkO0OT=}cJt2wERi2!mjMP`MpJ$oT{7%n&hp;bcFINl?wej+iQ?R`N?oXezyxJsWl1F} z|A+aufPGg?UrllaZu!7&DefC-Ra%%ODm?JbyB&d?>p@xN|jI;C=6+gY%n$#Fy1>CgL{W7G;bAiXa9CDHug>B zi0co?9^a?EBOXiJ&$Xv|@gIi+IMa3vgKDAy{JaQ>W1gchI|D&=&yNKzbd6CN=(8SU%pW*#=gsjbJdOdyK$%xV zzl;rCSvbQ!A9d66JiuK&Pcpze$j?y^)BM+Xa}?%EnuxKmq-zv1PRl`bfIa9M&gpBV zT`sonT=yJ&4(&F^s2$l?5udS*Kk|=mptZBisYAS09%DkqpirwsO?|IFX_a>ASYTy{ zLZaCM-Gjw)E02S43#%MlYs3IU@?b>o&&yA;V=+;v6KY&lM>b8A*bdzn=ey3%1kBRt zUw;{GTsRjy*(eCkTRea=6#b#dTPhMsBGsy(bVw74k${{`wD=gcIlgu)KpBWefX|Np ze!vT0)birPxBl)afUkMA$Tu?ilbMg!mrsyxg){fron0)dEf5sXR=*0UklFuRGCAim zoVky8&csMDOc~^j24aYr1DV#YNA~-!U!QrE6cLp`+5;f9E$PioW0)PoWsN935FI?u z@fV-eD6`~X3X5?22vMDvm9DtF!N}>tqBzxesUx%=uFlp9sib zPMoo5K9v|8j&giAueYVCNR$bATtjtbdckHl|4@5f4}}^fE29N~3b}RJx7Z-W@ORCe zu54BLF-M3Jk1~1Bo>L7}4kdOTzk(aGPUFd`W1mF-|u63L&%av#TGvw+qD5~hQ*AaCO#EqGMD%OYV< zAzOY~h>SET)m{LQdT6lgMB^HJnc4J6Je-f!--jP0Od+BjJpgp!UqgVk8v*)a|B*5( zik0VZex%Jp0C!#dJ6qN$?^ybr+umFfsHyi-p_)9s#`1mrj=PIdxhuQ!oA9SOyd=tB zZ$9?Vd?I`RsPol0TNHXRo4Tt>dBpcZERXUhHeHY$w6G$;KFYK2GOf?SrT3RvnImKI z3KGI#Ss3HlkTkbH5hNVd`J@M=NX#MQ@vlk|oRTa}q#c(eYeAX~uDMbNuiP5WA3Cnu z9MXDKJj^0A^nrn-NDC^npUPV;k%;~!ITUXgM300YI#_*VNQBXKG5u#!)-_&64MRum z>5+Cz{vYsQEbN@X7 zA}H5un6)sLz<}iu`i?@q7#ffKC>9-vr^gL87G=6s<52jp<+)lo-ZW~;YmvIJMkHFX zvli*lMofyzcKTpJFC4;q;S19U=JzqyRbC$yZWLeu-1C+buB;UP6z!r0jYnJpQ{sbr zPcxJJs*$BtoYd6O-<=kvrQq<|4(&87HaMuAKTeAAY+|+t@T>1w&2a%+kQLvaoUutO zmajG6YprFEe~COlM|&NlE-m>7jvdh<%Fp!|Q>~s(R>0R%VW4y5+}zpQk)nfGgTeHA zTfKY%`J}=_4_1EZ(JsS7Pm*_KhSd}0ydVz+StUbM)dK^De#c4nX#MSuDW}VKV4|w% z#z~>*HOe(ce90$?oVWL+Jzfw_Wo-@4hI5!LNeJSq1VEPyD)LEzl zPVEy#694)o{l>u^+6E|kIrltNC?r!N9hWr#th1|)m!=C^|B0)wkA*B2JB632Wa^p8 z{TQyxSI+B{^-3&2bquyt0h%cUgkldpfB z)n&zdnrRK##Q`b8Kxyz2CXKHhBlRJE-Ww{sAxH~Q2J{By-gnFtERPAS@M1UM0M*qO zyznz|Mu(-I724YZ^tyhio*fQ~9SVwX5EtH>(kO`X*QIri0jxEN9GWGRf$K12j55E| zKVoAO=&Ym0(1GR*&XrFu^~bZP%XI&p(-`qKPIVRP)_w6E*s){kyn zgkzYDyej%PuZn3S8$Vyq2R{J%+XU7i9pbS|HlGB1Ni@w4XCkxR(+W^27mn` ziNq28VT(yPmggVy}9g)yN&))q5YW zr~M^J4qZ}e=q-DPW(`ikssT#%Ai*MS26gSDNGq$>n#xaa>^A?WJ37EckCFefa!w8d zUSx?k-~xUkm;SeISLf@LWVJ2KR>S7Yf`V46PpMm*QT1P&!A6dlf8-Y3uX=myBgi|$ zGfrygq0C2H9xl8;)T3AB(s&sK=YMOq)xL79Y2~mz#wW~V^s?*Kv3@k^+_ujCG%hZU zxMrWJqIG6#*qlD=IWI3wtK~?+>uc$pkzYN60q8Iunxw}0y{@s{oh>zSzm+&LJJufZ zXxTexp55C_PZuKcD0^G%v&)n18FN&dqCDyBs4WCanmmrIJkp-7=DiNmJK1dg#BOe* z)CjWl2l9$9Du-frVNbY`b3f?m8hc2wcxM|UIt&GE{jB@;O3Gg--52Rm079yox3o5BADs)M{Un>l1phpgL)6CHkp|%zGruaQ?7o-ZhyVE;+oj%1L1v=!2Jh;iHtZP*Cm%KPKZfV)4s zm&h5{e#b}g+1Q>2P#XsR&w%q6E4s&lRjm2=AXQM$L}h{WJVOm7)k)W?cKQk;Ohwfv zP!pbO2g?xcR&<1AM)}$8G-fdJV*#?ByouUix1643mKPcAqbN*Yt4&Q=thzNq?HimX) zecZ69=QjDAPh~d~^$*zoX+x>y<8qXdJmpj=`6H^6_?>+?8jLZ7p?Z}8?;7wJ#iYo0 za+cjgash%-#Su++AZ^aOD3Ddbh{sIIA_xb>L)ttX5}05F`POmjevi?19@kc?IHIP!iQCRMug)u)4~tDEZ#X0Uj!kWzQkF9#~G8j8VKI zz>z*shilM@_I~;zBv=#xn62|8vea8KECVh%9ID&1Au{)XdbO~UZjeWeW~d-R5v)F_ zPqk8*tgp5vF2(idj?W+PX{>o+vI5)PcFCa!qcz~OvirQ??s8sEpk$^^Qo||NIC{Wp z?{I9vU^KRW!TLU75>_~!#sNM8W{@uD_~U?ted!UGbVxABJ-rWrqyL%qB5r2&SHb>T z3B6NxZn%m!Ulu+#?r73aNyz)_t< zWee73zs*O6dYaK?GA5J(h(~7i0DTXkSa@Uovd7o(*axfRLM%RyGTJ{zqzFO z^6txl4hpK757{!nH|1|lKI*>g9_%ZP zb7~4A$SvmXv2deZrHnw zGJuqO0~4Lr^|4G=ocDWlM2K#<0RJD;=tUsxHga6j;%I<%ZQkK-czYtV_YW0nnf86W;piNGN zy99<#ZK>d&iDXd!Ng4_q{McuH+mlU9{sTTGtWQ7R-{5UKH>!=(zZ*vob7irZnZ{h! zKGOi6-s9~6de#JdKIo>ExJMRgH7!fjtA~{1fhrl+@>pb*Htp-xJ1q$ff3iFz(yC}i zrg!TPUl-Cbt26)<1pF*c1Ybh{nO_6-q^2e4MKtMd>q~x)V zX&9j7bJVSPTd-9HYCbGK*A{Wc!5-T> z?xE{g3e(ThuP=RogHndSG0Fj1hnEzdx&t*$G}Mxgy3_L2n&69SH;i^{p;ddm!J}% zd$8yqNNtxAhW?6kMc&N?=lqDCPU5o5Is<$g4`}+ z?p^>dWn#2Y@cL!?@Rn&?G`*V}rhG4bQM8)cS;aoDcx4B;qVw)t6#JkHeP+|Xf6n>3$*22>v`(1O z%lHn1I-1rhB!tt- ztx@ITJz4ye3@xZA!*G$>_|aWc*I61`b2`L|Z5M}PD1)A8u&}{hN-q>5KAUdfdX>eP zl3yDm9|ataA}tKrJoepD3JIZ)MbF~zhfhJMSkBm_W2hM z*u%+US=OwvEiU^rLeGoUA%gymq8krE|a!Y4cKpC@J*yy4l z0{$F6ija`H+e!SL;uUv7vu;Sgz8OvANFP zWz_z_zg@N7K%XnL*I^(=38e%)HhOscGS2nA#HHG8YJZ*k(pfFi2SVx1A0S45H~&_s zI*MH@dX9|0L<>MyR`c>%hEG-#o{+;47JlxlwsM4_F%q;O-b45sP|aRK&lWBQ7E-a0 za1VUBko%nB$rvzO(ndacIq98L%=e+IP^wq_bRDwr{m^n_k_Dv`=T!wL!oaFA?+jy` zN{J^G_TV)-5jRRdpk?l62cG0kE!H}&d9?kwzxucW?eoXr{R^W>MEfGKBWCe8@S!EK z`s2j7yf#p!(}7s2=OQ-e=3fwzlJBQvAl!+vfWnTU8e-Y;b=idt^IZvq42uJ8j+e{Z z%8ST6m$q?dU*cxA!KF(>Ohq_T_5` z=L^uv&eBnL$H?nr+gh{a8;pt7o=tC$36-8quP#`d{v@Xm?NHb3VFQe2?!ZsVo@$7) zpZwhW0Yb&{dSXziwa^a) zO{Ffq#-8cS_0c$CWOXWPdvIcq=h?~=B_Sy#m$d7LQD!^mQU4JC>c@(LfbxQ)l}Us7 zU@Q&<(L$C-H?DWSdP7uIyKpv0e~>)tG!>Ps0Gt&uA;ckba-Yq*B(K1bgMAAQMi`j{ zmk0)BL6rZq0W*^w$$`gnAc8(-LF`g6r3f2Xy-880q`=&zP+k(zz-UTlG?w++`-gf@ zUes=+Tpfu~MV3Xhlm=@`s}5yy9?Xj;P?EE=7J`?XI}b$|RBMcFXN6M5QYCNjrje7> z0piWtG9r6db1Y?!M6-e&=$R}L6vcB)r!k`#%%+$n0?mkLUp`nb#T8qbF?*v649?JT zK%;NDh#1We-S5ta#=rca9RYsO1aB?n>8~W))*s6D@($l=FzFqUAPd)}+xcQ}Yj^eT z^k;P^jyPkk(;LN}^(h}lSsAH+6^o^B0Z0nna?U?MG+65O8QlW~{f3tF)CH27Vj$lQ z;568dL?vgGA==moV19=i$YZ%qvN~LG6F5DHZ*X?L+53H`X0tLxH&|f5Z=9u<+6AGu zWO*P-{R2m48CI&OhNK*0M3+CU+l{gw|1S0<<6qu65>`+R`pknb%jm^uwaW( zZJZTo&PVf9!3}h3ogeOs$Mm?Nzi7IqCL$8bYAx#vWt(ekjQ9LMp4K)RUDe-Qv&KQ5 zC2}ujpi9w5e#<67?nM8cLa$KKIPsKj_EFyGM2(0jL=*L(u#Vu(^_VJF2Aq8fP~Ru3 zQXhW=Of<2A^pvhh;He5F&AL{(VN0C7$;7AZkmHTF#+g8P7Wa6+S12B^EEBTz*I}q- z!M}cyvtY!)7eN!r(#TG8AUvG|f;^u(C}lDwu2iEcrV8;P8xNjH56CBlh2cPFInv4)su*9)bfF{$6AK)A>gr zzwItc)3SxnwVC@`D~Wq5X?#xpy0MN5+V;xV;bm#N&`G36MV^SI0<@E7J~XwHt#tR? zx~44hun}1>Py4Kqx!GwUZ>6eg3}0ov=y1mli9hdbh8J zb2iB@ZhF7v8M%PAV4N@@Pb-7C`r$UVPx$7WJjjL50BPUd+ZCfG1U0wC#xq4YyzE{I zFZdgEf{wqZz{LjU!EhWNoFDRI%Qkxc_q-f84uJ;3#FmJBPX#E(T8lYsx%25Y3;^=~ zm3ABvT9o1s#AD@-hqKDN`j9dWDrP9>q$VaN7Q3OBOO4^uGLl)PS7cZ(j&VUB;x_BH zw-swA^aJ>LJ-&j(-zuh!7WIB{@A_xEB>lZPtS}H1b&dbf2>?e-rmyME+|n&ku(zEW zUiQ(_w#Ygoz5u{(rDu(d{LZYXa`SS4a$G=Z@I7y;hDb0{N*y$9nYh%z^sVjS?F@D? zslfZ}-0CF&K&ZZCHL~7cnj4o6R|m)Smoma!ko0RrN78pvA~-j0JoH_2#+ikbNNusP zv(VA*1(oBx|CFv&^PQDc(PSc@KPPRTo2|}X-EU-#wE(oN2TUY&j@+>}t`5|}2+R}} zh;E@ex!B=j3iB59T#2D~&uzsGiG^M=Bk@NG%@^Vh9nvmj7bcdSgM?b>SiZemiR|B> z1Cv;?YhOpK-g&s!gMCDh4}|(q#3Tcf9QMJ&e@0HT2+@2*^Hw+nGJJ=qgmQ(-VT5lZ zuVis99RSJ9;8SlbnL0@UeY=HDt+RBAlFU>G2Ef|C6%}s3&u7`iwlKH&qEPDEgj%b? zaa@0G!Y0=xO%@BthuvHheBdo=%qOc*2ed3?+jRC%F)Lfbkk4af+-Q|z39LmhZnw;y z(zke)j9(v~QRD*0C=msO4pZwtl;0LrTU2TM(g8?!6;O<^;ziA3YhrcfX)XH1FB~Rr z%+KvAQ3@sG!<;Cd5W$*oLpj(Y?K2i8Mo6$Fm^^{w#Ah$Ii$Waq8GBI-D10P5WNOH0GdkOi$@CC(y^h0R>5C$T*j?>C1>Kh)mRyj2! zqgR4#Qpc!OF*9U}Hwb6Yn>+x=y2juKJbji-IvK>F(x2J-Wp7Q7vSANQ%Qf~hKmk6J zPxn+6a)G-^Zu;qfYG|f@8F1$$Z#-+8lLA=Cj=@*!i?PkFZz-VvhE!Nk*<7|8mMcz5 ziKA@Uq=1mx@0bZw2iC`Gl|d>hha-22s%~^YP^}&x-Y?b}PYED{fk>5)S}bEvxs9!) z`Go#R`hc_{pw0o?Pb0#Kw|DM)yWDVUnZ!9n@y;*S{C z(JLydW6Z?)f1-9DA7)(IzI*1cwys*>L1%5R>+s_1t2ni1i?iyf^;`LeiL=c87q%un zt4+Mif^S>;TMBgt5YBPphu7JX!e?{??0<05v$ajaqphNE5{Eh;p?fU$7CT9g=5&o4)4%QM;xgYCL9%g8@yT=-cHc%WQdaTr^35aD!^QmUGW1>)RfgqVW5KwM zoWKmmqCiZo#}`%x^}+HpJ=(XC8tGS7)*`kHcPrT`L+vQo;+r#j!l37$uhtDq8)iEmfDL#1q7GpVe-0Z-W|GLUZhYkDU zdaz{Qd#`)?O4^tr#<1_xDPBkaT*OkyS;^#Q{`w>Q8SmZy%;sa8uA5%{^r3JRybO}t ztM;ZO;^L54is+p#zAwitB=&ey_Y9s1Dk0KM+snlpfnqx0r?oh{TV_vzzOxm_S}yKV3r2L6bK60;HF zXO%|RA}qM}mhf_9@*AF0>B;WY)WrY3A8|&gOXIQ9Or(?wULKD*^W|0-l{~Z7uR4CJ zdF6^HS(nWxOz7!Y>M0H}BQ}XY*~D>qeJlPWO|>(A>Caaz?M9O87wQV$_Ow0MfAf*( zY@PI1v)tcyZKAsBl!KwqGkZnyU8d`v;p6X^d;Kj9S?k;`kHg%lsVEUMKt1r!!=7Pb zJijpE9x0+2wCHo}G7_q@e~vipd4_i&UliHFCuB{ToI*AF;R7lp+u#~vAN4H<8yc(O zRMHxFUW>@a>3pQxvy8myM|MpurMH89&L96C#cS%n^p$XckCV)0PyS+*_K}j~TkHLw z8=gMAc?_zz+Gr2{N|8`zDF!jbP*`He(Kg2n^J!;-nx{O38A8j%E(?)3H+b8JlpL;P z_}*Bqe)Tjln`yVVqcmq@?K@V~(hTn8w;`+R4YzKaUD3!ynQZ3~y8R_DA*K)Iu%9Al zy7rfxHQ;U3hgvO(TO4u;XbUH-pZ9K>E_dcXg4ZS~D%GV?WeJ?X;1x05KV^WO{GG7j zoU-W;-$D4mUP(`PTSx7QPXM!ue3mSgOf?Y91S2^kUHGhgG?m1*O zeO7IxP@iV&W?Mg66WTcXFw4z0H=nP%aLQa(r@tRA!&Vl_X9jj0{fw--*`K+%t>{(1r9V- zQZMA^%r{(3xm)N&7_GD!OdW>6|MsB1?v^6Fu!^M`1t}^1BzNh(Lw1zsh2A!b6Wrpc zHP_v?cte8?`DWJ=4=?_vG8bgVW%Tg48mWRW56yEBcMoC@Lg?=O_jhfChYorea&6yi?aJeJEb-01l>WGvV{@7yr0i!8$%T21L7YJ0u_ zUW@B1DZ;A`B~BzyKq=Sf2!XT4XMzKgvQc(+!ZBRVqoNODE1lS6J7`-cgRMt6THcL6 zZywcTtCDWPYEe$>i4{cf5or%C%b}NwMMKVLRSq^1_ZEzWo|&vHEDPmKtTPw6k~FG7 z7Znsuc;|IZC<8-v$)MpATNim*Gd)&({K{GLATz#Ua_(l{J=9Z&AP;a{?cp{H7;~*yKtdIIr~$Qe&j;i@j)G?%c%@|Pax6h>dLdBf8VE4<7Q?z z=)3+F?f1-m>sbz1={_90Rx_c)O9`KZt9aKGiS#xOMB^k;AqNb)r*?={n1Y`nK7L9!a}yEa?MF zI+HLr_a|YB`1fx7cwpkyFm~Hy>S<8}Y2`-CR{HTjuO;lnb>%qun)!df7SpBQ%XFtQ z1ctAN5DjiT_|e;Ooi&xJA*;-n$f+&L^YU#%SAK_wf-&`#edRq{6ZJhk&uDW9J*OIp zVYW^)r!np@=)ix%!($n|kIyA)W!%_r)@P?Q7gBN8tf(w_T076p`Q%T1t^d7GBO}uH z%fh2e^gm+_oKkL3+;o+z93MM7{N0eqAyh-=&wl;vOV?N5-!LSIIh?1|i0aFLw?~hPc;ml@lIzz&o6&s$T+bzEgV0#gKX7-;HLHkQmjLOerG6!wW z$N#ai_X(CG3&&w(MPu;iqH@&4@rDM(?FR2rhR*RgO-B}{hc{N(G?Lhu2=zCperaHO zq~I1LXv=wp_vl`zpsLj({7(oG=lA2%`uPQ+EM5@KMkRewOmb1{BW7|M_RXAo|1T<(jEydf zUH(Ia7A>XNB^$<}s!W~GS8DWh8l6HPO4rx58`H|Vu^qvL&-&dH29wJtRiYd&>k+H} zuqJ-PL#BLd>>{OM`qjAes@}J6)=j3Ge&kz}`Tc<^?S8q~Lx%RV61)RqyX%j5)jKqO z3eG4~RU9b=TQWPZ7t`Cc{aD>dLmG&pD+sL5Q`Dpi39Hvcd&@U$i)I%n&%KTr=#(^N z^DL%FB&NY;Y13r2$aZQmDLrRCYi261rS0(cMRXCldi{jdHzJku8vgHPb{yVpzt^!! zX;hOieIP-1Z%Z-gQ#3wZ+U4;BN<17YsEYt^q!2HiEOSXI4ohm+lKD5I>=nWKV?T=@ zrqoV@^Xl8mF#lbxT5tE{UX}x zs)hn%YT4anErp5x`{bF_ojC`WJF(mFLPbn@J5C7);qI+5Bz5}cr7w@qHm^_ z-}uJ8dh;)Kr|*NCi9p7yzxCH0+DYt!`n&UUn&UoouDM@64yfo1w`>eyxx907Si_RYDU-~!Q zcy1Y;d`Mrqn74QO+5TsKctkiqJan}UP#Hq_!Bq#eCm^v$(|{%i^k?wr1d`!H3jotc zXf)_^M8|?62RKF{j+~AWfdVJA4S41V$DX%CK22wI6<}+G=y?}(1)y+7(}MTA2rnRU zMb`mB7f4{WNAm#!H*_)RaY0i9lWWlKrW@J~e0GIYMt(@maz|%@xJLvuzz@OjfhQj5 zF<{}2rp7sWqD@fXmIq`EN2B?1q~2&g6fp8c)8bTp&^jp4>jg=P^ibvZ$LKX6;RDh4 z92hBCBldE2u8D^07($U%bB3JLr>7N;7Tw=HCoX8;BN@J1SC9xsPSXy$9yPy z9MptB=f6Bf8=ydLCS! zSOw6>0BJZ8@7L%a@RbKD(s+XK<%cB0^^tfDQFT1HM=dGn0dPARIsli84-BWGm%)=1 zNG~h)-N`|J0QT9CQ7sDY;C3#W2?eTiAepEE`lp_c{tYT~A*xq^J^(*U$JUWA?iFW*44qZqvjl8Yd8>i%vCdIeM!LtSb` zMErog3_SyCOCa?`;feo*K%pGH59Uh$ClT}(O5l~lbqjol7J)Y3LYoiCF}z^20=)np zy@QNSZqQBID)dKCUV)~?tyZD6QDC?dT3NjegE;_)CPjhKDySp9=49$rEC>y5x%OoL zwSK|CeL)8^%$XV?2T$tJWnj1#N~%^92>{mybUF~NhZa-MB7ERMBTQC<_vkht(g4R| zLjE(>2dLgdFLL_fS~`6|;{cT=RB)PqFM6PaLlgQD5c&Wm)|${$fU5~o$@vjMAkzis zOZ5?^=x7sE@aiKv2Xr+|xg3yl;l_rMsV;w*f0ryX6MNr#{b_X|FAYmjE zu06{Dv;heq1jH_@=x(iJLMBAV>fzMF3whi3`ew;wC zz|4Z&HV8F3L%y;Ov@h^!hv>!?7=X5&=tSVu0e#-?gA+WRk$e~07VvgLmeZoM-UW-! zaVKOc>4sA%>q65cu0dXCkq9L9pc6n+H<}(Ck-|N8>V?|Xd(d==eQ@uEdjF$06|D9` zKXdz`Co_FeZ}Jy(5_r)Msq$aZufek~5PG2oiz8fyC~)~JL@C0cvnvBI(q>N3UPXi- zM=*pwj{?O5Fv>-T(Wx-M2ccTQn=q`&N6``}z&s3C^Y{UcCFwHNAmIu0X8X$%tl z@iC`?Km=Y)z=r@AB4?96~y1sw79@Iv@8lJ&O&X(Hc&Zg0X+ol=b){h z3oz9_&O<^BJzT2`i|AS~ya1IgEuwFrz_%Zek|6|Lw)%;V0Yi&WglHLE2rmAFgw|zf z;P+2BhJ6R4{>KWM5CzJYPxqn&HNA!wMuC-8$PIa}0)q{h(4A`#C7Xo>IDQk2 z2g}t4RPllcro`|sXkT{|+RxfTyTg+B3sM9Kpv$_yVOqy*LDWzlE_=w%NlByo4e{sR zaFa&pMHFQAXWF_^*|zySjx+bTFeCu;by5Kj^$74Q+mvC~STMT+zRK@x~UeX!V&L48!D z2n|k|9DzpyD;Yw2npH0;kTh_O9HGYDrbOU=X;VNELn>qlnEOBoX5^{1;9ZIjli@09157wL5ctk^naZJ=>T7N3I1E= z{+>g6K`8^A``zvT)f`erBm~r)gAy}wP@M(%2Au6p&|yK2@_L3MdfC21ide3NoA& z*8bm6yYEG~_?mPWT2Nku<^|?l$OMqO2(_QCo!=#71aNRcZ^Or7Qd;sNYG8&Nc?oV@ zf)-CJ^E)2I4QO+pOo$w=wiPcj3EX%f^}RD92EzCt6}~5w=A=;P!B!3Ooo0Tr8-jo% zexwZ?@ImV7#{F+h9%u_9fuNcn5>BfywGiS1J_$e+CT%?!5QGqn!Y&Xc1fkR8-A4o& z1V+M8{e_bTjr9tQiv&^R6_6BxEbv6&1A%rl#P3+{92O{4*=Nu9VNjqC!)o6vcf zIQ#%7gOq?r(vVN-5{$uYS>z}1k~wie4%q{ivQSs?E#w&rM9INHl9STsb_Y)X)h$SY zbp&>5m+wI7(~3K)fXo2}d4vYnr-0uQKtx1G}gmNuvI}i0k!gp z6GAYe-R~lA0j~;Vek6h6$62bLgtgdR$UWfzh2N_oQQ)E~B*UXh0EDZ<1oc;g)P`Lc z1=1Qw9Z*t-)YH>MToXBuO4NWO4IqBk7zXl;A%sq+Zhjx462{QtT@yqN1vu_Qu*4Kef*qp?Tq*-I#2PM@DYV;5 z0+;vL99ajq%@A4~o+Tm%%cwcjblN?8SRvzJ-2$Sg6*}4)`3kODLAek0Ful_s!XQ<* zhH_XN1O?mNhmgYG3n?FLA)}oQL{GQ;iyhqZKwGG0<072c`$q^P3dGvMby%~9mD&Fh zWc<&ueFcUjnIkM1BleIAJ1*EXIwDP=%>lBU-bTJSL5Y`+5QS|>K8SOI&}n|C%y{In5P!$R}z{*XI51iEe$2xmf$h3M&` zq`1Ls#|9x!0CymiI6e8~f}wL)gCKfR^74J3ASiG)_&*Bvfz%U7IPLY@Lt*auhCsq; zwLN|c-3kx=pIxaiXt(LMeuE!i`dBIKBL&{6&{R7qQZChnU~N zXpJa8Vf2FLcq`!PIsF!{DqH|n*uuSo;G-(U0|j;~pxM)qV8OwVxK{;bJ~*HSfPM{< z3}&h!BS9@?r(V=a{E86DGuK7SWv*Z4z9oCd&CHCbv*L5P02yMf`j_^!GovsFN|9 zPQ(se>VQh7$l-}k*@aXA)=tPW)&-ffaPSva(z=m%;ByyLrD_Zp?L`mr21Im2RIL|T zg_jN}tKNq!0f}A+QSkqV1p|Wn;n{k<5AqRyLCS!5KZH8Iz&f{e0+oG*8D8}TLQwq` z@O=PoeDYU_COg6k`=Ar1aNZyi2a*P$FYv{lEMPJSsjK$?QLhGkhY>R%J_M;7!$=`m z9fDz3Isyy%#4vPYO%TSxsg~YR#2dUCfwGf7VG9yH2AA*YC`4iJ$Oml3kyao-22t2r zg-t~V7o_}wBJf_s4;FVJI{p_DPJ6I7ALX+dk9+FDpA>{{W5&^ukQhIfkoB za)i*GBt(K0rU%d*L)z&pE3PO^M*tY^eaYV8UP@| z(BR-943qp38bb%-F_T0;(C!`CV&trL}g6jTE&uMegJVYh(=IgzMYgtY7nyvV<(Ri(+#{RpdT<9E`vHM zsPrKvR0>n?)P^irqQ)2l7b?hdS`$NPpo9rEME|#$&EO6V)CVs}Fx%;%`XCg%h^Lnk z@#YuP!z1h~8dilS2FyBepof}ohQT#jJBOJ9#S9RgK9A`FrRU((8yPVdP@wBPT->z_ z7#N^$7@=Bt8|MeA%$PPXb^#h~5{Hx3VZnR{q|8u8i4`LZU)W@UjHef-8aB8LS6Lw? zksSkX!#-?KTLT9Mwq2CXsMB&G07NcgUY)j#;M+w^Du7?lMuUS#7%crA7okU|O&cjU zW&`+gL5uw12ruqB4+fsyy_X=~j|U51I4@=w9B@Oy1U?Kr)|7dn;OR@R^ZXbt6mThl z_4xEf0^y4oBETvHpY91_fBZM!?9g1syaOe05FGcy#UK#FRDnTJh{9U~KiCt)%!1FCAv1iXs2S9WK`5;p5y1Vr zih&36#1+VBU5Mt#mh`_>Zk3@RoRPk;`ccdWC9;iI7Y-tg&WWoFhh`*%qJs@FyyLH> z#&34skn5yT89qmQyXNZ;KI>Pnu+@K_webo1d?HdX9+?z7(D*_u@SaA+@!)UQA$7@h z*O7+b^S!xuOY3KFBYzcKSGu%I+Z}fEX15aSNEy#G6OKOM_~+1^7th+QtixqhL?Y~f zUpXhO7_5rQYnNw;L0_J|6`T>uq(P!wF2kI|F|GWP=~xtrG@TVxi#Lfq>nVDX=?b5h zvgl&S>4hS!-=9%-Cv~^P7PB9zncsN_Mg&8vPzsRIJiaqTOsY&|u>IL0V`f+Zq_z|w(+}|YSiq%_* z&Cu=0ZR3Ouf#{mj3YTBeeD(Hp>7s(2RQBR-r3{YPXg*(F6)|H84P*29PPY1s618Yz zi9sIEOq$x((Thf1^nwBsS*;=xgcn7GF7}@@jnAdwBItp?>3RO0UxH>T^`nL66uK3> zk;{{X9$zm{@mPM;HL>s1dO9-K>tKzS`u3>%j(fWoUE{|Iml3~yqe_O)?V8{@|1AO0j%r6RLo{0(5G~ng8LvEuU)3<&;Why>;#| z?}do`!LVOP)KyONPfr{L?&zG^cR+o!tchPBIG96~^Xn8m{jyCddM8B1Ckibz6KuSd zgs(hV`aK92D;)hwxQTGG^DbYEhuOQ!)Q@;J!(8r@SOta9Ct5L6OX-Cr^U!RcGu;ws ziOCTBfQ?t}zUU+@dDX3uzIxk6!g#CV;3>te7mHSMg9x?2(+eI31(yZcQgzsu-yLw$ z6c3@07%3`F=^iGj$o z=wgRX!_~J^Z^QL&;q0UXw*Oi0&A+trwYNw$+nThX^L>@v?lQEtQgp4a@qLX!8vn#d zy0l3(R@(Hyz2djt(ZTivaih!8?5zEMlu?7XzuWUG<8ylzYsrx-*EUMi?i-I(8Fx0; zc?b9${Y*|zy!4c~d|lH;y*7KBFTgPGh0dT;LTUitC8s-Fr9T8wseJl$IXZeKnCKFR z!}HO_j1~7qtYV7Sd<++|Cw}O6e`Yb*KeF|BxQ3n4YdOC_PZx2CC;5s%es0jEdya)y zqW?*&_oWGwZ5CnQKJp5UXntw> zuwi5?&YRC9D7cQ4!^2c z69Ro$CleF>7wptC4;P!@7XvZ+$`paMPYEp-CfJX`?^E4A(q|hn^9j1=^%1;SIKyid z(wmug%XVd4g2wN`{1?}zw-?tX9*o8r3ZmE-ulBM&cgtOfVAUIN%?PXT?34W2^7GRs zL$lYj;J%p;!~ykb1nJYyb0b_DZWwHyqkGh$;0XMh){WxG5B!a`f?B;g$OEj3-9iqW z6}ow`$`+&i{7E+5xAv^nXBg>7hHpnTO9#7rw)s3(!0@mKHCKGYG6jA?G7{BdLE=Ca zGBP$)dsX0ETzt+Y9+l*WSL|ZUtC~qrp4aqxF2=}_v+f5W))OoE84FaUm zWVy`?MgxjDvh%?n0qBi{=l28tV2h$QKTLkDNnY(tVi(?WX;bXIui~6=kyE&IQUOi# znSn5uN_j;C-9Jlyn^gNQt@&O7VicyQX_NciRfXq4NA$4QmnSLXZ27a?VJ}C=#llN+ zmF%%=FSKpn{4VyU=;j?@8TG{L+1Xm}zpbR{j)~1zR(V9~qr0ew z%a3RuXH01LeDM>0fMlAe-^e>zN`fzOJvO=c*x%H6)6e7b#?@RK0%=N{TdXxcofFfb zGRX^!vJcg`+P}xfG4FErZd#-qUcXyM72nhDyPiMdjhBRcoZj4{tLbr8sB@bpjwaGBQnhnU%RpwWKHsC?c4j-zh>Jd0nC)1YJ(m)jHSs9gKz}PM_HeHQOi!Np z3=w0~e59N=pTxcmE}gZTq$fW!wp4=m?#p!o%k00Hc6k<>7taHV8EeH!YK8dKb|imz zFYbu&l=aj~|LK1gV^??RcFgqcuiwMR?t&Z~14>{2O3mLlxP|4S6)``{r*W4vC$?)g zZj61CY@IOuiPVq%_c2OF$-H;e`9H3tv8SJfky6zI;0 zJNDBTv6X(}kt!$tL~=2XlyF>xv*+9s)!7MKRqX8s3-h6NRq9qjE}_?|3`@)PBFb6j z`sR+8ewkm_)K?SZE4m)@S6M>V#NeCDT$FYPU98(rE_$i1a;HKjs#M@s@M3zPf(jkj4#Z9{XzJFX9~QdcPZ433VhC~ zLP?ILmBfD}Jod`qGv)w|XCdreqefB7x|VAcA!o6~-3r#4 zmO(wgsiThI4nyU4XZ+AzOig2d^GgCA)&I?;F#F>%RJ!>>_y(V#8Ny*`r%FXiAF_V51GMlu!ZqiyTzO3XEhbq{aunVihJZ^WcV|=$X5Sy zTBAjQ&Km2$t+0!FbP=D+v$#=Ia>}l_-fb*D|D{!2b~X3Dk$gHQk)Ncd@w#a8Te^g+ zdVzG7Y*ZT01K~$eB4%@qb+05|L?^1{gLU|eP{5HGFyf)~C8Re$V4A<&={5`K= zarNJ0UCWea${%(UMyRqK{5L;mlqtSPkqHae)FmoMn)^QG+s>(fc-Pe%3+EOM5%NZku&R zGA!ipU&YjdGkeXwaU#7t*c%>0Sk8X(|@NAtD=36Jh9JyK3h-!7qY(3;6>cU ziZ{1Jwum908-0+oCe{3uficSG`r`Kt*GErcv3~jSCwKT5cy82q8(&!NWGMwaozFUZaLn*9Jyet!8X&2w`{#<9NpZg=o{w&p2g zthT}~UG{0!_OeLUm|LxUB1O6=X`|j!OUC}yx%5edm=oT`*9|{;)Te%nrL3?%v0juM zvuz8ToPTsldT-oTeB@U!4NF~nNXwni1$RDAVyoJvl)E^H(yyGE{VDm##5%rH`8%r| zTWAY9ltw26@1tGf>#0oc(KIW@V3e87Pk&;0J6mlH`(rj{$ExzZ@*8_1KPMS)Jx7C& zoxurKZjmL9!yK)LEhRY7UUP5D2gmJOA0Oa&7g}}9v@ZWss$!$#_$(({|L%pUX&fcn z>jiAD%1ZBV?BX%Q-_S!Id2B=8$fM6n=q(GHl?j!>t;^xjC0ZzxD8iHB6Fr%69yDc5jOQ=0n zHja>oyMXfMgvw=U0qH!p%cQS-&6_p3x!kbmH;3hpGPBXTXvNk2sxxs?fB3BK%LSut zs|c1iQw9PtkBnl}VsiyHRCt4ZEn?JM?m7C-N3_^Y+|tFPo0xRFM_DnDOwJJ@|8IgG z+sAsi##h(c`k2Y-rJvp#-o3199<5QHTbx(r+}rX9>~j-|+jrKUhZ#>#y=b3guHwk- zzF|kCL!WQ%Z60$-d1uT5OY!`i@2Wag<7x@T?z?CHZ}HhjID#@_JCEe?-}D^Ghdog8 z?T>0D$-QCnbb+ciHIbz8w>Z-JKA)K@HW%BIN_YETJ~L-f(!W6q&Ue3Y1Hnmal?rKL z%j&-U;RQ#Qsal7&QzYu!*4|pl-&xfho<5PRI>*duZpaZNcC{r4|LZ6T5tX9Fxj}q-d_P8p1fs)BbABu<)W@n zu~SthM#O91&b4vg9xX*Jk~^o%%Ku)owQsnMY{bS-GyPgRVpxzKlo}Y&eg5!H$N<0c z&x-Zf>4tpOEW(c(jaloLIHIl`__Pl9`O+N5eZH^x_ivG3I7Mjaw%NtL(M^_o?Bqt2 zo}^wM{CwX-*SdwLno8eXTQc9!_!^(l!`UQcsc9Ddpz(;8S$@N(oNQ9qIanfF0H2yU zOK5U=wQ&TQ7qqXPJmK(9ApAQLp4VS2^PIQJ&vPV*a9x$p;BIp~3jwYZ00jZU4DKe! z<8ZvyAL$S{#D?SLi1WxOzr^EwO~bmNgwlFN^B~PUNW1zaUbmxG;Fvl__?Z&whd57D z3Kv0=tybV!RK({ltFPW1at2~kVH$ML^(S+Fb<%_;o;;LO#7vZ4e&!?pGn7iTdeE*< zOMg@T)3mBd5UD`;o!1(Q!pcVEg zrD59c`Pb1}jz%+=N_{B`4(b>03Z2tmGf*dmN&02F|boo z413eJTGIM|orL zYQ19C)^qJ_tj-6U61JeX3H8snsd0Aw?hEgV0)o%97&1pwE}i*)k<5;U2+5tDZjSp? z%*b%{pS#MhDO*!7f`*Av!?mfkV;Unyqp<9d2Yj-~X3WYZ^hb^E;=dHlODr}ow_|*> zYK-RE3Kzc-F0wFjKWx-?cHYr1Eq$BwwLH#wxWY(B%@v!YBjG`f(208xcibW=(c2z$ zxN&cBa7{tNaAHlN{iertVMRXKvE{*%kF1ppR;k)whb26QFv-bvb|v}xCBbBaB}x*# z<0T!O?w<9A(ZmV_q6LhBXU+|-Ne7ZF|42`1SRFQvYpIfzC0nj$p}W1DmwrKgJ&}c# z$fJdPv8WI`;V-U!*JNUOzN$p++tOL_Ovi0hipI6QGin7!erGNXlHLrYUQVMpd3wSB zp5Cff`)|k$z7zqeMS-uJW#;l!Ap4ao1(_MLq@l>5Mv~0``6oq&^b;KX|NDb|Ekl|M zdH$b&o&}%x?IfJfX3gMg;W+FHJWo$s(DCg0i@j*3_F_*~N-FeJiS4!bD@UGxiq0{j zNNh6nsQrFkQ4N!Rfp7Zao&oQ&n^unCilBQ*^C;e9&98cQzB$AfRNv%+pAR(j{ldfk zW!2>4Z!9bS*@spX@d`~`vQqy>dFTRLHQ1^mXpcocD*CqAXL5KlnCu$n(spf^kpKFk z-&=LlPF0CggCUQK@a4T88lhzmfij164qc-X5xjrn;gPpSY^4epm^UVNIV|*u^mWU0 zy=$^^X#Qqud#!ZABkKcIR@jjO!CfhCak!o2$Uc$19DjkkVplR!GGMXEUd@`}^?u@$r6y54TW6<{ zuoo|}_F}ht^qi=yvaf}^68Y|a)L4Je*R`nfM^Ea(t-q4qeqVCW%F0Yk24&DoUhluN z!L&4+EL7W~k81wr!Nyv1-E{e{m1Je;YvyEwjNI#2@L9dmRcw?5r6d&vA~V)IVtJ{2Rez zwlNQw%b6=l?vWc37841a9e$FCT9NGv+{wsodEB%;Y35HTz%+d8{Q}tvYIqwCTQy-H z_lrKbVRP>P5Mvsq@BeCcgGSh6+B8;V*+o(`M)C#yt*A;=Ka;=knJrn-|h$k>GZB{`ao``_C@9<$KplXsVuxb=tDvuMNgyob6Su ziqysAdq$oak$#@pG_&9!dG<|C^u}(H|*9v#3kxR5JWwFU%SljFcR=-)<~~DMn=GC^2JtIh17Eq zQK>ae-TT-#GrRFo^y;~iCd<|f@y5zu52_<}cK#)$sv5nG@fGGZ@vB+L@@Khe{iC5x zY2WBpr^>lWiiU&arf$3Ac!dU%O5)5lafRaF(bs~UR21r)aJd|2GZUiaLci79;$_Nj?6E5s7!!F%`@61GCiZ(+vi661re;-xXJu^b7@0g8FYO2Tm zx(t<+S_GM?;3NahBEPVi<{FVVm0M-p?-$)HiNf%Z7)dr>j1A6$dUxsk;G>en4@+!F z*hnC7b~-pQ$Vs-V3726tW7Plkmw>SS&t1AxWB+dqqMHIXCkovCh)=W%u$Sa$JYsIw zXnAEPeKepvW(#&gZKVXRg=@UB$$_48B$<4|@6Q@lqS^;QL78-6`o}ky5AcT87L@GFRbmNc}5hbn7c*+~Zs& zxw|k>jteaGzB zV%b1nT{N*My5r*+MZ75kJ(qwuoq8|AU({9OqpKSu1ow%w-;c_1bM!OVeMxJ+L>~7j zW+~6vgepX|!qt|4VuL|E`iav|kv5-oGVZ>7qS(2C;BB^yiJEZAU)V67Z6Zx;D!E2W zx4V`Gbxev!X728bi*J`_@l(Xs=VQj38a?XTNhoro8O(MVdd2vezh%-m*NncIQ%E5` zE9WdohHkDEW5r*zkwNz^pxDIq2}5RhY~WK06GgC_8sBcQ*K?KO`p*|v?$5>Tl)7|0 z9;tDcVN;e;U%s{yh^>h8XfoiDoGEs%;!>_!3arrjJ!Q}H*r__+_W%B%S&RJj*~XOQ zp~u}T|9wr;r2slLz8>I|A?qbZ|p(BT1P90vMXUpZZdP_R|=T>-7KN`1? zkZ~ZVX}()IU243V>H5}-o4zO>+vVTJq%QGvZ{;pFS?73oz;xv3=P%O$|31eb^A%?P z#NFou2(AO|2gbX{Q!VqR|43Jj)so|wqw9W>78Li)= zz8jzKW$69l{MlKM{9)9!Z#i52VX~=#b4N6UTh@L7j9_|B5r?=#j#A0WT z#YyMS-UA}V2Nz#Pn_ad!HxZJNYcb0H_5A0j?2BKWdth9Qkf_v-Yy7sZaku zEGY>emMh=lUN3E^*?UbES)wa*Vb~y)#9UYq>c+{FEPRqN42_nJlWWUR%Cw-!?H4IH zzr=tZA#>-)x>{veNO<4q7h%sXmTOB=#4uTK@wi77=JbpFfC5RQaKM_}ebv&Ak@Q)K zT$^ZZs_UTloRWl{_)2{C(+UFzp{@R#?$Td=P~Wte;nbqsSI);`FJ2G%nf&woy=U%c ziqA6Pt-SEa$#V8PzGgXAqFwki_s2{f3(N1K!Uu2CY-(6$AIz;CVsDx)ca02C_>QS9 z)I^{bNq=9y*`-8i82QL6#wDwLm^%8q7g6VTeJw7~>8>?16LWd+B_4}&$^68#zi*N) zJC5FCo0h6*(Mto%)Ub!+7z1?XefBtT?Ylyz>J4(e8%=AEO_V6M8)nF#zZ%ew%fTK$P$9Ep8)M5d zRP;|>Oqu!x5tR)$b+u`3Ot6_^Ekn<@ zOSZqIsOMz;8Ra;gGOp}tTgtO>PQ1k(XjU#&>UhSR9G4r^7+tvep(eo7WAD$89$lsp zUJ_Hk?fi|#-7V9b`^)6mX(M}*21%^Td;A+)F&CaP?=wniZZ^{%pbDo=rCV5D1m8NY z*v6R}8#j9UR5%l+&V0@e`B3?5p=YnC>){9N;p`BBV}P&QuQ{)VkEssRxJ!7!mcmUG zY1r|p**>lJb-Ukx40O?J%P$-ayXo6xjZC^p@_Cd^AMVcW^(-WhVEcs1rW+-XeP||) z58Su=c63=xMZ>%64=AP%+vu@}e|}x-t}knBi1A{^)CAykZVE`;1IqmkDr0=>29tx6 zz9D_zbdO@|0rtc6&54`SN98xBjy(45_UHcW{MneeHi4T+$8A)9u%C8XoS3eDI8gz> zM8&o4Cu@72Zx74gV@FMl@25-~8#{mrti$p9_v0q_jZK}T8z)ShoT_p3`oJm8$!RYW z2PTf~^(Th9u1)NC`tJoB)dYA19J4$|KqzhCD#B#^$bOO+4U;9En$xjYOV zF7_veP6Ej7;8Edf*@^Q+Kps0W9nM3CSpEWdphHZH>vtyp%K_@0;pn$`;zul?Egp{E zuOv=q1d5dqr0gd)V+MXd;TwCT_6eClcQg^^tmsvVm(5>zv4$IN1Bz6K>mTG;HkNs> zT)y$4sJa@1mJmlfcQ_k4vk2pawX`<;F76k zjT`c>V=cLH^8q%M#72bBG8-=u8aMCdMkGbavYeS*OCfCPVU~y~C^*A@X7a+F8rqFP9)EuoFrO^pH( z89#=zVdYV80;hLna&t4Buerv&(5w?lsMHaueIgedfu?{AP*y-@j|y zcKse#3EuT@!OpdHm3`V!cq7hE{1T(mXSCOLTguQhF;bQHdAPUedmp{t(wpBO^;5?x zey!q25WG2kcd?>NA+pFPoRTEPsdI&R-i3YYhk^WD*Gu(*A)i{FBQECfw%L0rVcNfx z4~$a6-CWoLXtjTt7)Nw1?Cj|HQRMD&m+CFJYnQg(Jz9-O474DG*xcxdn-hrTSIwmK({stuw1)hc#(S6I-ve3^UBs^&)O6r zdh=~t-9&Tj_9E5tfo^Fclg|O$RzraQuk~HqxVH5|7CnXRf@edq)46J+WojjNh=2)fJw?7S$mOCwgPiXGm zDT=tlvvFhKS1eY8>QoTeEEUwqujiY^WRdB>S9~uWk2|UHC!b;{2MohvzvEC*YlU_f zS9->7Am%+|Xe8)^!k5!iUC~=iV7uPY8lJRf-TlRIzmE44OX(juIE?GF^TDG9{A0aB z)Z@3qHHTvw%O&Qm$*V=8*Sf)4$K?&DKH|&1+o)CtLy>aAI=|{cO5i$JPVc1 zG18q~3b1MLJ+-QtHN>D>`c4)LlI7tmPwaC2(QRhB4;VJ0s^$QW+50gP)WUgL3yx&G zCJW`(nvb4Cr)=uWmK?FT2fWReEP^{2(o$Hf_honoxY3J9;xH3&dF;uz0NT!+990{@ z13Dt`oR&VVkexB@dgGG?oILRWnYvg@xrt&z1RwomhfaEV)gCBe@Z-6%&V+g@bW?_m ztD?&G2nMc{`C6U%pnhbk++0IT%Q84$o$Oe+8@pWWAmLlmQIQ!yTA5EVTrCtb4lNEi z43Jlk^$FszT{WZ1UPAILS_XHPt7k@H>O?Haa3aL^qpVHV4;M=V5Laj%FY|SZA~1?$ z_FDs7av{PxJAl4syid=xE-VGQxtuW581e%mu67@qbAE6KCW-7Y6WL8DiI;~g3nRyv zVNqHJYH4KGj7+v^cqp=W{k+!lj?==PkJ*4BCmU6EK#qQ3u3PjK;?)4q3XB zFQ;P0hp~_CaU%wy@^7G4U97G;ayF z=Enmo&L7et{RDy5Da)eM(WfvMjoydj?l`?xiytl1S($_}5m67#3iq&qo8E2vxR+u% zIOQE6kAzSI`knW$CAEgTiK5jfkLzs0cO+~{U|UOXE_Q*PGIn~y9#5P39dCReedfPk z#OgfJ|G2-f5Y{UyUqb98=c&INsCl)NxNqjJymP;_u>-D$Uek`Zj|+b;@UkEQgZ5gXh zVVVLz*)eGe{^{G!Aue>V$n0NRjMSYFHtk@e4vTiWRv&pJGK~Q+w2|rM=(d#2q}tgw zyVPfAznn(Vb3#3_a%S#0uH<}NFrDOa_E~UXn7Ov+%=R3mSHzeyON`E|00*hT$(wIgFy&ZAUeHPc_W_r9Q}}uSNCXQPnIH zbFCjUT{F^l2c0zrY@zJ4Fux79a=^cs&I6Yecb_b5bFQC>UAW2lER=TJH?0J%pQVCJ zlzg^;wXn^lso)MJAM2)4v5WHJZqVgI4*rFevX8ZulHa;oVK@GwVRKWd@P)?zOSUv= z`K?b%`|y?d+0a<92(;i|9Oi)=io4Gjde*E4e<%`|$oZ@l`T@aPtZi4J%RK~7EnBm; zoyLwE$klc{?kSf?f*&E+SngNCeSIq$H7@ROXbn6`c;T2q0e}K zQjDKGPR}}7!ykSWB6r)-{7iG-8P6U&lA0i9 z7o@}SALAy&Lu{o2N(bKxs?5JH_M0p#YB|kNesw22-TyM`@ z_buP$Y#fAvotrcqHB6($SS102s+BEoTA!Qcz@APN&gx&Ur&6OmVzO^suc)s*rR)T5 zSPcxgj=Gp=HgV!!6J%T-A0JsUyKetBh2r|oZIG>B&zp&W8s$cBONgXl&H{k7j)vXP zIF7X8b|dcNhbDl9RR`ZVyX9qG13Ej}p|TW|K*Tlt1n=C<&Yt;&mE?1zuqq2CnBYbi@W0;VZVoc(BzrWJ@~d171X<(S1a;zw@cE~y0h)`CTwnH z7Q-Cn@ke6$j3wZhG3z&D-gHAUl}4&eYLm`B+nG(8lFn_;VdtcC5hFu`;HuB$HinP- zxzJw%-KTa{udX$Ts(9*wqR7ZU^sv?sbKlmK=sysBygj-2>O#wJxRa6XJ#>7r3t8$ zxVCD|%;4?Na||ErX=CY$2vn(+c}OSsmf5T*ncAXrOLn$=sFu3fc=_8Ry;*DGhTP_A z?1o!TZMmv}u|XGnmhe(NSy~0?)df$gX%72CKP@#FMZ8q3)&k;Eh0vcg>SgL=CW-&{ zP^=c3UquMfgW|7u&U& zA%D$t%Q_OKr-ap+$QL+pwT+hZ#Xp62I7fjR(m*eWjG)|p|CV!06Beu|ymQM*e`;{!-zfsu;{Hxirq03O>_5}_ z>UX9qSJUm5-oU7DQ*Fc4zRa+q+qSH`qU&6(QPb>L-CWarpj)YLv#V=kU^CvV*}go` z3djIiQ%;lr3dLv{a@8f5)&0sb-C zT0q~JEPH0Xq{4%Ra3YY;udyW9wm49j8avRPCav}+CAL4nj+iQp&0I92?K+J=+Wk3m zrkOEjLC9|r79&mUG0Qr=!{UxT?efFKyndCBE>RI8bM^JS6o?5qen2B`gs=lx>{Kjn z^Rr+w2s?#(6Jc6I*FgfO0P%%G0h!nj3m;!l$2Gqn5x)v#XCrc|s(gHzXQkDRou~qF zii%F37b>oc08}&pvU(sd-;!4K7xj0$82}xP@R5*-2_lxOsm^q~iBpA34fLze#1xFw znUX3r^Q6wiK}G>!7r**k34)?640oEY$wizdf#L0Tfni_wg8p?2HYbg$&<*UDn9AU1VhDaI%6a!skbbY{*rhwCty z-|z;&U$YzY9WYlmi|VL4PbFoZ4J9ygEZsN0oMI|Ycl>pRVgMsN=Ip!IpH%b1vwEWV zQw40^T%F1rIMsKCT)6m~r7#jfVZb{2YEZn)g3X-P!jFo68>7mK*3L_TGUV8j#=I9~ zpuu_$$k2y=eQQ-qHIpE+3Fshk*WSX-f4Uz=LY(XW^!OrT#H~TpX+)CyS}xa>+UxrY zKJ49@BraGvb1-^+%bI#Q9VOYi1aDWzCvNdK&1k*7-joUx5FVTkIy+?`GHa=8(M)PI z>a*9j$I|;TW9?5-2~7_(;}qyi>#44*zW0LY9onOXO3&Bo)53)ad=%FjO<6wqaD%fN zXhw4=z-H$sAHbuR;oNw39dk7=y6~~3BJU6Vo;(=NjdDS)laD8*Z!h@*Z*!+Fm|Gu9S$>9@H+hAmSctZ6r)nS_uiw!v}xPvw#9M ztLGh!yKUnAu7g|$1h5_SBjJEI#xV#Pd?|k5mUZQCp@L|&Zw{4Sq>1j zkL)m%aM`sMb)M)t+Nm(N=rc`n)lP$ywfv4VD5j~HaiH5RTJ@8UtXK49Qd;$;eTte! z9os3#xT0S^uX&8xNS}w*lkk9rPEA3$J2^KOu~%zLX9br^rS2*_wclIYHWIeXewHI` zjiaURD8PI%N28I5Geb)fQ^gC`ulWqahDS*R>7FRd31x!rME^-3f)XF{ugNU`n#}f` z&Ri*0NalQeRc#`N&?j~^slF6-wbxxuGBcW68ob=yi>to_-fdPClI4ikU`ITZ@BCchXsdMlQeS6zDp~q~xqXzz-JM-=|9ENwv-5iFJ zBZwS^pC`8QSjhK7@K`9#ucC1ht_PxV5*s{5qQ*RpM-Ua)1ooxOx>)z2OV6{f$=MCR zU4K;tfA{=^$1>$9M(1PM6&{Ua(Uq6n-DoVyjnrsNYK{sT%^?RL)qLKUP z+U#f;HD+UM7`14#+l8I5GSG!xzB1s&L|z->#Uy;QZt5Yqv5AGX23tnGn8IWy z#!uZy=xEmC}oDapcc(>!@U2+4WudL-^eVWqZed{Y5!R z%D$lsr00_}t}r-^TyQ(%sJVH$8!wU{y9b9cq_H%i+mBUC+@p-0cT(}o9>R^Wh?XK5y~cDi_xR_N@6>v;>qvJ zGvg_+*%>T?d^E|UC#>X2gtN)*vYQG2l)VBUPPmHnaMW8fTo=G|l)163&G%dTQZs8w z=nE5wW-Qkt)c1DZ-)_bCl0`&3K27ZDJq3ZqA4K}J7+55fq`vp3*&(_cv6Q1Rez~<{ zGQXkl%{M6^1_QLt2+o(@-xB+?J}X-TJ5c@|WnQ|ccFJ!6{jPP~a{nq-+Us9|~y_9aVr;XJFGWcX%R zOeV#tRZF#e)2&E?18tsf9CSTv4Us!BDLE&iYM=m(oymB8cki&b$F&m8d2u)lvI%-7 zDBlXI6t|1RYq|f#FJMBf(5#8}Wc@%}y`f67oT(*!!y)Q1lXcZVn`vOwsKFAF+XJSH zyI>g=TC%JX3Xuwz{!48Q-fnn3RBo>5DW8r4dVL`{w` zkl1Ioqo*@YV^u1SC(G|%M7lHS=l7|C6N}caCl4o*h<29#tfoMzf@J6dL(t$?ZKgG) ztp{ndM^XUe4*K*4%mJgV>&f#<2v!~jW3kCsOZ!JmAM*t;I*~eI#&uKpQuCLQKezF5 zoih{vJW_2de^}TgrydLa-rlD~lS_83)-_!WMO)B_K{C(-Z^-k+SW3k=?Njhc#KRc{ zsNkG2BZypcMz9O#jYDWhrX|tU3`nmd4dY_OXm!o!2hpYHb%sC(Dopg zXJJR}TR@{I^(G@8B^EM73F#vs%Z%|cn`ao>m&%!-_)b`PQQwi5nk!WzurQA%@4Vck zUG+Rq4o;R9Y8%>vb9emy*jz^)o@EZ6JUo0w?0LR*eWs}qe!ITE-eFN2e;%#%U$UbJ z*lrpB1DfEj-x(p;C~_lkupdlu0N%-Z7^YX~;_2HFeLU`rIo+t~_2U)lt@`=u>S>tr zd{bGfwRR9yri4Mln~S|<#pZ5FB{A4^lYMkOv3i@0)U!X~F?GZ}Df>ZGpDs3IkAveQ zn)A#Q9!mO`jP$xS|7MBR)&5!XD$Y<(XgP<5Mv%Wl-x12{9}@)|O`vytx_P>`Yi&FEw?_{BGuw7Xs@)x1h*DD;>)#(+O|R4# z%*bfAULx7E8LLw>KGw-a&oMh}HefA5Ujm5i8`>#7KK}aiAoy~D^LFq0Hr&b* zwg8$W+9p-Z@xKmB+duw&89O7&l1`@U`-5?>YM5+>Tq!{pmy{m!#lTU>*WYuEut)Lv z3450vtzF`jM}fRTZc`wNU8)@iD^ixF0+Y^QgDA181-dP#Td{Fh#eAjkmcxBYi2sV# zx%)G=Y{k5Hf+H1QlrAj<<#65HpU%lD$Tm>$Va-kg*ilI7VC9~ZO4d7-ok~bMo**@| z6E^j7S%pv$d&K9=Vut(VHoofQmS-+M>2lrvehcRMSNhm2;SVk8_VrAOa^%p_2 z3uj2L8=DNbOAGt^3#CdT8Yt#muKfPOIl*5g5f&|KQNN-R?}IPvhs)Ad>0gd?0If(N zN6SQaor9%eqgT0RRHC1kW@BDn9UqUcY0A~uug$sco*7et$R+j@*u5gcz93#m#MI%c z5kr!=xE&%Tl05eyiWaKj)$-Q8W*pGE?CbTft=ktFRwh}n>+8v{M$yODT|7+}>jMqm zBCCw6q4&z^y|rNrC%d%E@UE>nO|>Fjudcq&dxL|wh&)wWt8cyq4%4wd(rWF9-!Jg> zY8_%r_h%+!T^vFj6qRy8f;l;sO4QkWu0OPEH*<4)M1*`jgkg|Jt4qNxeNJ#v7v+|r`x<^1> z{NwxHAbmnja!H5!USg7*;gttfMl$Qt4Fr$I0nN+(vttsa(cA#>{<8G@!1Q|T}ur6Fq~Rm zQ9K2#^EAUj&MqD8SD}4X;D_Y7gM~x z7}mh|n|(|g(r^eoM-E?H3T^kkjE#`&6I>{ zchd*EbLJJoF6Avso7oJSmMFA!oc=2ZB8je&+dM1%D-iFdPsixw^InsUBzoGtAbY); zsZTOaJR6EwBF}|>LKjv{VwZ-S7P(g?K9$!(O~1Tfgc8h{t4BHyMEYkvH7B8Z-Sm60 z9QMPH5o@LHshD(XeQJAyCw9sIW}V(1vW?xvccE9VTCxP%?Ic=C8uBioo#2>k;SE27 zcsGn_`WJn5>={bM-p}bnCpdITZB_A>=&gZYs3(4a4<;$n!{W@SoiskBeaZ6oXOF0q zB}(;sJfx?rq0419D?o;_=n+gbfB4*Re#<|=l->d!XrL^6^jtPKOz)~|x|SVOr6uTN zOp|FqA{hrk?fK5K5|~furH^ZJRe$AumYki`?y$zGFe68{_i7hRbbA-Hcdg^4O68Fr&{_~I)kh}1T)Tkm8})hqt6F!QZ`uQMcCq_MQqZp zYRj*Av3}C0Mgfq&M4s~9(IT0x6qvu!kntrSFX^}S7BwZtjp;x%Ejg`Lmcpf`a zkY#QaXFV#aN(AeM3R!gvLrTG;>=|z%m2%PG9Bw#0uz{;_+227{0XM_{Bj!w~CrTi; z>(eQpIejXUO3J4y+yaD)z!9|sq4q0#WPh2l!Qk7F3I7->A1DQQtDAuq2B_|XzFptscO3XSz& zm}8ywlVK8te@o`Gqa}RRqEm%UDq9@(K~@} z9WucqK*DG<#!#4r)!vgV`jRStuVD}=Z$y3HiXG9TE+XVLb}Ln6A@*nQB3^6RtT~pL z!ku&*hV8(TS? z56Oj5#LT4$2>C?|GiE1La|9p4t}$iX+Sh`H9X~bhA?eW=M+tlf2^ge3S`iPxQggi^ zJQrWC@0y)7yEttR)upiSqiisJbG<6F-1ZCc`f*|zm%w45q&KQBOQ1k$vZ+ZP`{}sp z(?nM@wob@^`PLi%;^5EY*k8>3b}!8bjOJmIQD%nouaNI=Kq#}`(^#YRyd4&c^k(u8 zjWfD8ayS&UP=*`&fD~qGcdADN&Kq!M;zwN`^JRjAe@f>c2-WUcyb^b3pw;EWi+_tg zj@F8U%XjJycuxcyxs*<^CpFGLHkUU4>X78l`+KtH^yp`t74oS!T+MzaA7x}|pf|ik z3;Lca-FgKMQnAe=llJd3dphm<#_AAW1KASnU-{MzU(>j$)IcJFs&tu;yVvr6QvL70 zYkNqDboKu2Kvo=HM%AQm!LCcZ$nzYX55S)7s&)VUPYxUdml~q}lyyaHns;?FkAz9F z;QJN%dS?gI@mVRqh1!G`UpffhWy~BCpIOivmu0-O_`cc%Pb>GwSBGfR)uPrATjNHq zU>$&z);isIFeT%gF2aaaQA+%OnjQjA!~B+KFStGFkQ7vU z@VImvvVEV|G@+#FThLim+5DhU6&ZLI~JGGy1yKp@%Ra>n>l0_u5<+UE>0D5mwGT2B6$ z;-fQ!RKG|~`w}2f8(V-pk4*bKJRsPVn*?-6Eyr;$5!bMuM$|pQzZ0Hko?Eb#Nr$hS zvy{F1e$Luu}G8WUMz0biaezKice zxeSqdp%6J(?=97pBUod}5c>UV4gzrH2GaC~MoSLl6PG11!c(&uO27Z~viSLmgdLNQ-Ch56Gcn#WKWc=0P^$ckGF-FmX_$DK zxta*3c=f7f;L#b+Z12vKWjN1LP{KK4IDgq14w{>CBMYg$9XrnM{JJaI(UGG>yKAeF zy9D1y+f3^rn@p>r4U-?JlUFP*&7re|SzE6crnK%no6bpX55HFyuwhUrcxPXw&GNoP z*NFywv>Y2I9EqWx)$p8_m25F^R={$$*2h4%#$^_j8ng}cTM;@KOJl`#9@@5PjTmc@ zt0_l^oNkkWC&1&OViIl~O+8_kmQC96?_gLSyD6p9!i?Pb8NY&Z^!5SeppZ2!um>LF z0|y&t8WJwj1|)CVPh2EQ1~x7>PA*ms9wt^!8dg>sIs_I42Qx`yS94NYNq%-#Ha=F~ z|6xhv!9&tU<0Yk4H`gYWw{kZpeIMsZ%f&+?M&qDE5ElMhI>*7p8i9kIClQ-4?F0`A zH_a&+mL62BJsC^LgVZ&n_46lkyiP=sVSq7doy!DGPqB>4~)NkO$H)S?dH;n zTNOfars&8p!=h6;wW#15>QlGLTb%}q0R>O;J3Ixmx;+I>XTi!4#uaxedA3n4nlT5BcEq!#h(s#GLR>8d1s&}OKR z+0<1L+Fp)4(ilBmI}Oe2zuL6ai*GKN>B}Hfb-2dwuPHKF-AiTrxK2i zRz_(QqpnAxj#fu$RB|Ko0+WF&mw-Wh3RA^o5AI&WN==H6ljVL`xOO zmy6GCau0YSsM8{BK-?l|fU1{$u+)!D<6~aeJid__{iA)Mg;H;s9pj3dWi@|pva)?NsF5*z5YI!n~Jdsw<+P>C6S_ zd$b=NM#@T)L@b^2cFQQ2ZIy%$hTQ_ua$LV3ALiQdO+{GZ&Mh_$qFT@6Wt;6My#TJx zeDDvu;FzB2au#z<34j?cv~FYq?uiW*uqzIL+ZppDv zZ~_1gDU>ayh|N&7eK(MuY(TMB%PFkB0{_(X|`yFvM3ZXxrh|i!R-8ZKDZ+IzipV}SKiUltR@+3 zR1#DX-rv@2Ity@8bCeEMJw`&B_}ilA*|fuVuGo7x)0l3jLE9&x0Zo5fo;wsd`_8Fi z{{FsXEIy>id;1-YU=cswl=1U?fsg7b>sy0)_!wFO8F^s_UbOQ@8?!c4j`Pv^L2wR~ z5VSW!q+XxRZKMhOL~>&@pJiYLlm>JK!l`~=<;X?W#W_?ZO(;hwy`)BX1)7G+IfAa+ zS!>`(Er>uwToInENc^)zAVC|aqt&y~tD5iiR-i93J-#|8W@~;Ma+dB}h0@ohee5qj z&!ABvx8#CCOkN0hSkhex--jVXflEzG;#j%U__)S6CotQ0;n*hOTV`h4k-&)B+iRn{ zep=R3t*YbZueY8Ag4>#7@h-+$OAf>k|3l=JxFQ@M|j`TU9?6AomA# zGl2r_fA(>%v~Ut63J|ve0*jiJw>c>X2OBp6ix#OK8z~1V+k3C7gM;heUJg=LQdI<& zFZLD=fB$m+_pcpAUpE$P!E4!pL8#k9EH^=*rms5^c&SQ|N>@~5#%vbW0QR@mS)Sn!j};8y4;_Q|-EbM7q9 zMam;(B@+AdfTki6?*utGNRKRlCRcb_|3Nahbt8~$vJYMMlc(i zOjH3fwR`~OI-p4`nD}!(vQ#By6+{Yfa2E6QM?Y|IPBr*ZVhKnw>q>8MpxiVE{4!t+ z;MHZyU^e|&CPom1O+yGToxfCAj#wzhr29cP*%1jhiW^>GtMm%zYI~3jUI!KVbc&5#I!eDR=fPG!ONg`OSPrV7dDJXcM&$Y=M2n%N!K;6XM^b*X` z&)BpC6yCLEg|$Q0W!cqdMYZG5McwtSgRo;+7aj-!e9DMxNpoZaa#zP?k*rCqLZ#+u z6kJf-Ld(tl>Q68To)jg(Zx?Jxa^wTbRElkgtwL(%#el>&6x-0N#0kP$VjQ`&gSd(9 z7>sGOqqsj+XF3vT$4N8C*^+HSa2F)BQWykh_E|Kg12f^5>Y(kgGf?Kfv=&UuY1bT6RmJ%i^$>zu>RKbhwY_nAx0tEN(i@}AY5TV$e|k%#I8BrY%}LXNf~9zCK1_b31v;GBGNOs+OUrG;`Ebj`Lcm_!Rj9Gqi14+uwIcuO7*|*3z(>iR&OzVa*@KqZZO}4u+Yq= zqN$#HMRsRN4ubx1r3p1zh`+Og>eJMKo;*tR$8=z4=ymeHef4RTbHDs@OlH*156OUt zaCBh7VM7aY0rBaB{~_7q;{OhtQ#Vj7>iy$1r!J##`7i5Ne(~ye<=(GKEw}vi@5X|V zG5j-#m_9nv4&;mQwBQE$>bDXR#d=U$=4=VZ=>Q$LA-YDST=88do*#WaOtcBhjP$~x zbsZg;J!vChJXjXH5Dv4U0QsNSc01JhI=4Ekpu2gxKR+Q0g0 z`YodW)3*ytA+_MI5xsEMY5!vskHYcTf83SW*8iEPCezr&e|thP{$1t^r(iDn*OoWN zP5Hs#aRcD6p^C#Wu>;~9>&NLp)(nd)>jAj%0xNJ0d2}G}Iw`2BKNvn zp`_*?-q@SPi3D-&72@-Q9g;elifxAHVl7DbZV%ra<=%?kxQ!~It zww`lci9wHrG@Ub(ck4xomRP7Fq^^S9pTjP+`bixS0h|U33LC6KQ0F^&d`x~9Y&SZ{t3f^LK{kz%%DU%Q!qiFbQ2x$Kd8g|59*Ln z&0WJ4JFsH1EVB;8A zQTJAnsqdlYx#e4ZHp`FzZt8PX!c9AZb`2T8r%$wCf8rf>hB?B#)u-Qu)uho7k=<_) zy0FGb+VE5rM7jdTP?U3=b(8Qy@!|^s&7U*_WH~LCr`QL?0IV7OjmdQg>V@ST)xi>d z%ADyS_+3_3!kE%%`S*G*%0VNH8CKMP_r>w&D0rC<3}8v-*p~3WJELYu3n;$seX)|@ zanP27u7MQqC*u?$Fy_n#yn>Md==gE@>Fs}aMtqOGDZ&9d0DZ=Y1NSZ`D^ZM0#}wlL zULL$1)4II4F?I%ZB(-=p00rM8Kl(NE^+Av`X316D-~ozL0rN5G^p2d<%k&*pQ_}2s z`$zF}@3>0K8;4DTPXUz`12ZreHK^K=6_N%h#qo-W6q3G%emT+&9El<2NKhWp4=FKO zL92zandSVx4=>QK%Nf%+-}t^p`!>23gu5_xpJHU5qE$QLZKX3Kxl`*9ZN)M}sr6ig zs)6#4>AC}U00Dd@3OpTa*wu)Vt{C!7X!k-ZsCc5^0o_1BFOZov-0whQ&O_KosfZ-hZ%5U`W45C!{aeH_n{GP1%aSQ2~`LD`c?*v}9EDj&0 zLWB}J%7;PFT(YOZ$lp~ew5L|&MUNKW|HNI$91YV9%n&RHHto~qyw~~{zQFk*@Mry= zoW%X?_#N;7@{G$wvH=)Y_?S^9BhwwYD$YcyoV@3M0gUs~_4U2XJb)kH2=VztFo!6{ zU*ca3;~eSx4R0C52aFEf`HLx##Qx>+Z?x~Qh_kpjAd+42I=GlpQqVi5MC)UzWK zpq0;ui-Dc+dXU402VX{Zq}q^_&!whLuPF6VXu;&BS68Ix5STun8sF1>Ye7YJkC`1f zQ0ODS54{)fct_Pcv%Rc8&n`b;_|lC-?2jHCPe2UNbN-5a^CNd5s2=;kfm(eKn=?n& zkEkHYUdmMbn&%(1*t{P2yZIY26!yDvlt0uH#EcKF{DanY(P{0!nC7W^zw|e#nO651 zNt6wdi0;!3uEdXVhRu4?|I&I7Yl&D-cg3x*5TGF8ls);;Fzhv4h zbSw=}!J_OUAYzYErI~7?UGgOL8-y&TZ3JDRWO`~SRAeJ3p>kp|z9?C$G~`6BIwEby zytZh0$_OMv6(_MgOp-ou>LY!HWJ*faL|33;9W3i!F^sTVkV3#AywQrdT}lZQeFZ0x zvP?0x=zX#Uq;&}=tTJ7(SHL4J6NI{y)u%iVw(N8O0xc8dLr)Xt53njh-+r-sK%*SK zNsLfBT|xtTJ7dBD`WlOI6uLj1aS=KSgK;-{4&&}PIy3X`P$GZKn`{iT`db!Em@cgf zfC@>gfKqO?O7c@0t5!q$S$~Rrv)zTp|b@m0x84I!Bwy*=FPz9tgd!&${ z#J&%7%8>N^q^!8=f!3@ak)Q|r9F;#ve^mi+mO;P!A1m<~_J#p)zOtihdN&?CO_}@@00;pLrrl1=830ZNbKHT<0)uAy$MTv0$A>~9Fv=LGR zx$6j7$9sysuuuJvj%b8X52UjL!f78Fkf<0Sh2egQ1)xItqD4b|BkMzgw18ukP=+-N zWyZ1gQ^rw06Nml2Q0kON{39_fh5?Br4ZwgT0XpF6gX-7DS+7%h8ODAW(E+iBI_575 z{xFYg>zdER;l^qK=cpKf2Q`HT2!-JI_>F8;hUlciQ8Iu6DmCgkf0OW=Z0G$7N<(;U z7Kvm2mW-RXK00AZAl9zMXnAV;tc-ccdd-}$;%3M^%V)U&guIwc`XBAHkKazk(+p|t z@MpopNH`s*TJud9>=?i|Q||Y(hQ2r*Y_$?i?fnDhwv0cxf&JJ%j5)|g*;U|{CjnK4LTkIn@^Afa_hXsnPsk--J`mTpf%$KLhu67hv zUz<&Gqq4obnZ#4BQ3*CH0d=jkAA1*FnD!?#CNpuclok=W?oRqvmyHIP?_RthP>9Oy zI~@yW&@n+aan`sw%(NH6bNv!pBN?P&a@A^ge8%SjGU+33>8=qU4Wt;U48kd+p7wpl zAzrS6sdkTp(Jjq^!0+S5skxXX)2+&ZqHg~c%xo0{fcbVhpvL`04U}S~p+7mQz$|!X z!Zr1g3DEPNCU_aTn!5GtAs_;=HLDaRHDp5@*4{LswR$T=%P21tr4GN!b@%eNxc+Ek zj;2h6RrBwa9!2L}i`bhW}lS+C$m=|4lH_=YoFoTDIv!W0Kv+c&mAx&8ki=T1rzn18%aEp2o)Z2a`Ys{~5 zpOC;*71>u>CmYQ62bc{=dNiVS`Wu4tIx@!HknX6f^?(tm3nH~~$ z8*obu#KriL(Oj!FfP415AnCjE8Tu?j>Q{3BwDx5|r` zW=UC5OESi-iWLZGJ!!+joR9J=H301=S&{vq zaVw=ELD!)9>`eG38g?8Yjieq)qrqK?joX;-Ypv!E_`@mB3KDk5PolIlG`na(BVoyS zbK6h9+r@xP=gEF{9KSko{91~KjzyzWG-6>TsljRF`J!)7HH*sK8|^i0DT&v^u%sQR${}Xl!$f_3{!!1?iY$+Kj&2% zn&Z2+pQV+qgq1NLKWkn9tXS}IH;gcT(;ynG)wqqI+9U2Zz#vDAz@(q9iI#7gDu36G*s#X$h1&Q zsLZK;cxT?J>UNYmK$>-Xcs&bWyDqMNf;jWsq%zLn0ke~=gZ_$zn_y_(#>Y) zTMa|R?5>)Nuj!19e9m7tGTcxKs?5g#PFikyR?wvD$cHJ*yc9wzW1mPZ66Rfw-7i>|D*1mCe8@dQSHpz&?R_uV!CqJ)46J zuh7Vl#O=^kYAm}S)ATyZiSzV8XNBbC&E1E6qQgv_Q5wo;7FFHA+47m?1hC7t?aiOe(7up@nQ_;akIIlXA|D!V}~?0$b;63orJV_WASfr_)pJ zfZBNh2zyxvx9eCAS5|x3{j57;93z^AA}2L(5nd!;oE_&xd{!;c9AuUOQoH9B1t%t( z0^9i>$qJoWRU~nE=fiQ!XF2w8+!}4jI`vPLm}<=a1I|S3aKAJoYJEK+YO$OY>(_^s zG`>gS){IH2R5@KQaMej!q>$Uw@70l~(#JYoH^v%}2!u#6YE*e#J7~$8YSooh3tn7v zwLL&Pltap4r4L4eR!ez@hgl}d#C&|aH1&AX?926IswQdt7&Vd|6n|1TSf}J-lRKMs`6Q+koQRi`v$Q zNggGU*KGy~q`Fi#KQ4DXV0e^aRbsAvV6k_w%L>KQ0?@){HDCt3ki$t$qaLUQ`ify8I;}yl9UIK8&RbWTw}H zfY%Wu)?uoQxR;%B05{M%q6MSa$>(&LBx4^v+im^`DkrovvtK-I)in_RZYwfV^*4K; zR@tTI>>j#+#YsFBfKMATwL{WD75O4BNUres!=0}vNXQ6!`D+TZ)22UB7e0{1En6<@ z`75UooDGR)=C>{81UuyqPn92JhsDw!ayg2v&U_Q(gnktei+t|uBTakQg$yaOeGl)4 z=s3N&&Jk}XYek)4c|vo6X@-RB`A=C5^cgZ>6w+lYilBP8*a6S*$QQKxv6d6xFx!mQ z9>ms#&E2JFu?=MVD1hu>&;6Wo(2d3Z{6aXm;X$zNHU0~=N_B+b%DKYh&BXJHY!J2) ziNw(O;TSnI?$zpHMGHAM3;4ik`Jj}t+gOP#bD{o)XA`XjiJD@Rpg@Fsh?$#`{Krmc z5wiRI{{N}uTHlgB80eFhHHTxhm5OGoHzN~^)Ijsra?{GnH%$vA{W0%XG?#!Tzc;I2 z7c-qqR7lBn@ip_3gs4GSEKL!uG!-|{OhVCwzzeA0`~%)E@0atOujku2=Q+{Tm=JTd4vHA+bE}qm`eQp4q!XRXIp`*S<6L&J} zT)V?!N^=SbJyj5;9MWT`omYSVEq%{RWAb@|rxqlKU}zFF=8g|prQ z^=rz3+5lPmcEMunhfF4tMoN1?3UVRgF4?QRJJaAu-P{KCmDQGYX*>tk2@;b-kILXw zkM(FkAUpyex?gy)(AVw{BHSQStEV9Uy$3wI%C<$}HGMBw7VFxT3;6br@ddO=xw1^PdyAr9!N!YDtcU(Z#*Jb3}?M~*fO4X|BLc#?dyVmKiSV}(6I5TfwaN5_9hl=0{U=9XrDFd~F`M6n^KBd) z#ckpJE_LOb2k_2NAVQ(^zq}V1YQ%d5>^;Vb;!91m3PZy{cKSRyUsh9Xw$I8>vUR{F zMO$|!sd}zGE%ylqOSwR_QF_QXq#8|EyKhCl&mdw~+NT@zg!)T9-jjonpUpv{h5|Vx z!Gb3{Tpc$Mv$m24&>zEmY45LL*}dqzo^A}0obk}q(TBLVL(KlFpE>dss!w*fCX#rn z{mtLW@gMBwe;z+Z!qsLyv%c(gaX3jfm(^`j zK`fh}&-iWqV}O5oCC)9HquOeG@F4@!0#aCULjr*NohmQXO_siM)U76LU&;bjMP^#@ zzw3S?yK!r>Bso{}{J2@5a}60uE2{^f;N(=zNlx5mm1}F^hrW%^Hj*Q^Cb~&K*{W^@ z!y<)0M2 z)y`h-a8_a3m6?vefU4Ux3sP8Z7KsHkcf?XvvRdUMJ3Ovv+t3Ve$!clo+M0ug@Mi*w z;NBIx(Y5elZQep*F?#`hRf{h9a$S0a@Bc&-m1(-5`IXbQZQl@Y${>pcROyRLYdf=x z2CBB$7}3^1w~u!MnUR$Z{p2Mo^z5)&PFMq&knQ@Gt-)*o(8pOQl9uyJ^hw~84o_9F z01;srQZu|lDD{7#RQw<3M|5x0yL>gfQWvM3Ac5rg1pgIEP4d)Z{J^+2HT+JnNeq+E zCw-oo=J)DlV6S?Oy}nX>Mdq)YjVjXApMwIAELUL+*((oaMqx-!Y~a6Tr8r&Vk2IFhkDv1U zfow)VNO~GaTw1*y*jM}X7MS&V3xGmqX@LV&^6`fUCq zcRCp)dG&f;dR)|6KNP5X#_VUvmHLSR)g`veYP1JO8!No@21S{ri57F&l zy1W#n`YBGI6Gi8yvzKG-Yeu59!41=%ZH_HCYGbLq3cx)-9XCm(&-_o&Cj@4$12*mw zoeZK!3Zp4Btmt+oeRZwR>t~#UgJ*}u8t)5qu7SO5?jah+(-*4QyhG)3u@OZ?MN8iH zC_D7apOB)O&p7G^TFhr3QQ*Nw-f)Hd+&s!>sjE?0w5kNf?8imK_i7G!ilz*qYl?-q zzDFpvOMwaV$hwGsIj9e3Uz(Gec<*kw5CI_hZ3C1`CuU8umFwu|Pnl2Ow>(RCY~Te0 zHw0}eldpF^KQ#7#z?+FZ*CZu>bqzcEuE4_s&(@Y)JPfn@+Qr$`&D`Gp=RiO6e*gps BhqV9z diff --git a/doc/cpptraj.lyx b/doc/cpptraj.lyx index 35887a3aff..18cc1c59e1 100644 --- a/doc/cpptraj.lyx +++ b/doc/cpptraj.lyx @@ -30504,6 +30504,10 @@ secstruct [assignout ] [totalout [ptrajformat] \end_layout +\begin_layout LyX-Code + [betadetail] +\end_layout + \begin_layout LyX-Code [namen ] [nameh ] [nameca ] \end_layout @@ -30556,6 +30560,12 @@ secstruct frame, similar to ptraj output. \end_layout +\begin_layout Description +[betadetail] Record anti-parallel beta and parallel beta in place of extended + and bridge secondary structure. + If a residue could be both only anti-parallel is reported. +\end_layout + \begin_layout Description [namen \begin_inset space ~ @@ -30680,12 +30690,20 @@ None \end_deeper \begin_layout Standard -\shape italic -Note that when not using \series bold -ptrajformat -\series default -, data sets are not generated until +As of version 4.18.0, this command now produces output that better conforms + with the original definitions in Kabsch and Sander 1983; namely that Extended + beta (i.e. + 2 or more consecutive beta bridges of the same type) and beta Bridge (i.e. + an isolated beta bridge) are now reported instead of anti-parallel and + parallel beta. + To restore the original behavior the 'betadetail' keyword must be specified. +\end_layout + +\begin_layout Standard + +\shape italic +Note that the residue and [avgss] data sets are not generated until \series bold run \series default @@ -30802,9 +30820,11 @@ where STRING is a string of characters (one for each residue) where each \shape italic ptraj \shape default - outputs). + had outputed and is retained for backwards compatibility). The various secondary structure types and their corresponding integer/character - are listed below: + are listed below. + If 'betadetail' is specified what is reported and the characters used change + slightly. \begin_inset Separator latexpar \end_inset @@ -30825,7 +30845,7 @@ ptraj \begin_inset Text \begin_layout Plain Layout -Character +STRING (betadetail) \end_layout \end_inset @@ -30843,7 +30863,7 @@ Integer \begin_inset Text \begin_layout Plain Layout -DSSP Char +DSSP \end_layout \end_inset @@ -30852,7 +30872,7 @@ DSSP Char \begin_inset Text \begin_layout Plain Layout -SS type +SS type (betadetail) \end_layout \end_inset @@ -30901,7 +30921,7 @@ None \begin_inset Text \begin_layout Plain Layout -b +E (b) \end_layout \end_inset @@ -30928,7 +30948,7 @@ b \begin_inset Text \begin_layout Plain Layout -Parallel Beta-sheet +Extended beta (parallel beta) \end_layout \end_inset @@ -30966,7 +30986,7 @@ B \begin_inset Text \begin_layout Plain Layout -Anti-parallel Beta-sheet +Isolated beta (anti-parallel beta) \end_layout \end_inset @@ -31210,7 +31230,7 @@ totalout \end_layout \begin_layout LyX-Code - SSS TH HHHTTSBBBB TTTBBBB SS S + SSS TH HHHTTSEEEE TTTEEEE SS S \end_layout \begin_layout Standard From e7213b215555919b8e1dc5d12809f32a4fac069c Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 30 Aug 2019 14:59:14 -0400 Subject: [PATCH 58/62] DRR - Cpptraj: Secstruct has been completely rewritten. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 9605ff09d8..864166cf91 100644 --- a/README.md +++ b/README.md @@ -180,9 +180,6 @@ Original code for the 'xtalsymm' Action. Alrun N. Koller (Heinrich-Heine-University, Düsseldorf, Germany) Original implementation of matrix/vector functionality in PTRAJ, including matrix diagonalization, IRED analysis, eigenmode analysis, and vector time correlations. -* Holger Gohlke (Heinrich-Heine-University, Düsseldorf, Germany) -Original code for DSSP (secstruct). - * Michael Crowley (University of Southern California, Los Angeles, CA, USA) Original code for dealing with truncated octahedral unit cells. From f9a01796cfe6e65df0507bf0abb4ead4573b0ad0 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 30 Aug 2019 15:30:11 -0400 Subject: [PATCH 59/62] DRR - Cpptraj: I have no idea how this was actually compiling and running before... --- src/Action_DSSP.cpp | 20 ++++++++++---------- src/Action_DSSP.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Action_DSSP.cpp b/src/Action_DSSP.cpp index c2ea6a8e67..5dfae709bf 100644 --- a/src/Action_DSSP.cpp +++ b/src/Action_DSSP.cpp @@ -703,13 +703,13 @@ int Action_DSSP::OverHbonds(int frameNum, ActionFrame& frm) for (HbondMapType::const_iterator hb = CO_NH_bondsArray_[thread].begin(); hb != CO_NH_bondsArray_[thread].end(); ++hb) CO_NH_bondsArray_[0].insert( *hb ); - HbondMapType const& CO_NH_bonds = CO_NH_bondsArray_[0]; + HbondMapType const& CO_NH_bonds_ = CO_NH_bondsArray_[0]; #endif t_calchb_.Stop(); t_assign_.Start(); // ----- Do basic assignment ------------------- - for (HbondMapType::const_iterator hb0 = CO_NH_bonds.begin(); hb0 != CO_NH_bonds.end(); ++hb0) + for (HbondMapType::const_iterator hb0 = CO_NH_bonds_.begin(); hb0 != CO_NH_bonds_.end(); ++hb0) { int riidx = hb0->first; int rjidx = hb0->second; @@ -763,8 +763,8 @@ int Action_DSSP::OverHbonds(int frameNum, ActionFrame& frm) if (resDelta < 0) resDelta = -resDelta; if (resDelta > 2) { // Assume (i,j). Look for (j,i) - hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first) ); - if (hb != CO_NH_bonds.end()) { + hb = CO_NH_bonds_.find( HbondPairType(hb0->second, hb0->first) ); + if (hb != CO_NH_bonds_.end()) { # ifdef DSSPDEBUG mprintf("\t\t%i ANTI-PARALLELa with %i (%i)\n", hb0->first+1, hb0->second+1, hb->first+1); # endif @@ -774,8 +774,8 @@ int Action_DSSP::OverHbonds(int frameNum, ActionFrame& frm) resDelta = AbsResDelta(hb0->first+1, hb0->second-1); if (resDelta > 2) { // Assume (i-1, j+1). Look for (j-1, i+1) - hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first+2) ); - if (hb != CO_NH_bonds.end()) { + hb = CO_NH_bonds_.find( HbondPairType(hb0->second-2, hb0->first+2) ); + if (hb != CO_NH_bonds_.end()) { # ifdef DSSPDEBUG mprintf("\t\t%i ANTI-PARALLELb with %i (%i)\n", hb0->first+2, hb0->second, hb->first+1); # endif @@ -785,8 +785,8 @@ int Action_DSSP::OverHbonds(int frameNum, ActionFrame& frm) resDelta = AbsResDelta(hb0->first+1, hb0->second); if (resDelta > 2) { // Assume (i-1, j). Check for (j, i+1) PARALLEL - hb = CO_NH_bonds.find( HbondPairType(hb0->second, hb0->first+2) ); - if (hb != CO_NH_bonds.end()) { + hb = CO_NH_bonds_.find( HbondPairType(hb0->second, hb0->first+2) ); + if (hb != CO_NH_bonds_.end()) { # ifdef DSSPDEBUG mprintf("\t\t%i PARALLELa with %i (%i)\n", hb0->first+2, hb0->second+1, hb->first+1); # endif @@ -796,8 +796,8 @@ int Action_DSSP::OverHbonds(int frameNum, ActionFrame& frm) resDelta = AbsResDelta(hb0->second-1, hb0->first); if (resDelta > 2) { // Assume (j, i+1). Check for (i-1, j) - hb = CO_NH_bonds.find( HbondPairType(hb0->second-2, hb0->first) ); - if (hb != CO_NH_bonds.end()) { + hb = CO_NH_bonds_.find( HbondPairType(hb0->second-2, hb0->first) ); + if (hb != CO_NH_bonds_.end()) { # ifdef DSSPDEBUG mprintf("\t\t%i PARALLELb with %i (%i)\n", hb0->second, hb0->first+1, hb->first+1); # endif diff --git a/src/Action_DSSP.h b/src/Action_DSSP.h index a4a919d3f1..ca552d5c0d 100644 --- a/src/Action_DSSP.h +++ b/src/Action_DSSP.h @@ -74,7 +74,7 @@ class Action_DSSP : public Action { # ifdef _OPENMP std::vector CO_NH_bondsArray_; # else - std::vector CO_NH_bonds_; + HbondMapType CO_NH_bonds_; # endif int debug_; ///< Action debug level unsigned int Nframes_; ///< Number of frames processed, for total SS normalization From 33a231e3221f9400473e22011cb5c3cf0d784427 Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Fri, 30 Aug 2019 20:50:40 -0400 Subject: [PATCH 60/62] DRR - Cpptraj: Update for revamped dssp. --- test/Test_Datafile/byidx.dat.save | 270 +++++++++++++++--------------- test/Test_Datafile/dssp.dat.save | 64 +++---- 2 files changed, 167 insertions(+), 167 deletions(-) diff --git a/test/Test_Datafile/byidx.dat.save b/test/Test_Datafile/byidx.dat.save index 5794a4a38f..eb80567fd0 100644 --- a/test/Test_Datafile/byidx.dat.save +++ b/test/Test_Datafile/byidx.dat.save @@ -1,14 +1,14 @@ -#Frame DSSP[None] DSSP[Para] DSSP[Anti] DSSP[3-10] DSSP[Alpha] DSSP[Pi] DSSP[Turn] DSSP[Bend] - 1 0.182 0.000 0.591 0.000 0.000 0.000 0.091 0.136 - 2 0.136 0.000 0.636 0.000 0.000 0.000 0.091 0.136 - 3 0.227 0.000 0.591 0.000 0.000 0.000 0.091 0.091 - 4 0.136 0.000 0.682 0.000 0.000 0.000 0.182 0.000 - 5 0.182 0.000 0.591 0.000 0.000 0.000 0.182 0.045 - 6 0.273 0.000 0.500 0.136 0.000 0.000 0.091 0.000 - 7 0.364 0.000 0.455 0.000 0.000 0.000 0.000 0.182 - 8 0.136 0.000 0.591 0.000 0.000 0.000 0.091 0.182 - 9 0.273 0.000 0.500 0.000 0.000 0.000 0.000 0.227 - 10 0.227 0.000 0.500 0.000 0.000 0.000 0.091 0.182 +#Frame DSSP[None] DSSP[Extended] DSSP[Bridge] DSSP[3-10] DSSP[Alpha] DSSP[Pi] DSSP[Turn] DSSP[Bend] + 1 0.182 0.591 0.000 0.000 0.000 0.000 0.091 0.136 + 2 0.136 0.636 0.000 0.000 0.000 0.000 0.091 0.136 + 3 0.227 0.591 0.000 0.000 0.000 0.000 0.091 0.091 + 4 0.136 0.682 0.000 0.000 0.000 0.000 0.182 0.000 + 5 0.182 0.591 0.000 0.000 0.000 0.000 0.182 0.045 + 6 0.273 0.500 0.000 0.136 0.000 0.000 0.091 0.000 + 7 0.364 0.455 0.000 0.000 0.000 0.000 0.000 0.182 + 8 0.136 0.591 0.000 0.000 0.000 0.000 0.091 0.182 + 9 0.273 0.500 0.000 0.000 0.000 0.000 0.000 0.227 + 10 0.227 0.500 0.000 0.000 0.000 0.000 0.091 0.182 #Frame ACE:1 1 0 @@ -23,58 +23,58 @@ 10 0 #Frame VAL:2 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame PHE:3 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame ILE:4 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame THR:5 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 6 0 - 7 2 - 8 2 - 9 2 - 10 2 + 7 1 + 8 1 + 9 1 + 10 1 #Frame SER:6 1 7 2 7 3 0 - 4 2 + 4 1 5 6 6 3 7 0 @@ -110,7 +110,7 @@ 1 0 2 0 3 0 - 4 2 + 4 1 5 0 6 0 7 0 @@ -119,62 +119,62 @@ 10 0 #Frame THR:10 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 6 0 - 7 2 - 8 2 - 9 2 - 10 2 + 7 1 + 8 1 + 9 1 + 10 1 #Frame TYR:11 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame THR:12 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame GLU:13 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame VAL:14 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 7 0 - 8 2 + 8 1 9 0 10 6 @@ -203,56 +203,56 @@ 10 7 #Frame LYS:17 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 7 0 - 8 2 + 8 1 9 0 10 0 #Frame LYS:18 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame ILE:19 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 - 7 2 - 8 2 - 9 2 - 10 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 + 7 1 + 8 1 + 9 1 + 10 1 #Frame LEU:20 - 1 2 - 2 2 - 3 2 - 4 2 - 5 2 - 6 2 + 1 1 + 2 1 + 3 1 + 4 1 + 5 1 + 6 1 7 0 - 8 2 - 9 2 - 10 2 + 8 1 + 9 1 + 10 1 #Frame GLN:21 1 0 - 2 2 + 2 1 3 0 4 0 5 0 diff --git a/test/Test_Datafile/dssp.dat.save b/test/Test_Datafile/dssp.dat.save index 49ed7020b5..96c0bd256a 100644 --- a/test/Test_Datafile/dssp.dat.save +++ b/test/Test_Datafile/dssp.dat.save @@ -10,29 +10,29 @@ 9 0.273 10 0.227 -#Frame DSSP[Para] - 1 0.000 - 2 0.000 - 3 0.000 - 4 0.000 - 5 0.000 - 6 0.000 - 7 0.000 - 8 0.000 - 9 0.000 - 10 0.000 +#Frame DSSP[Extended] + 1 0.591 + 2 0.636 + 3 0.591 + 4 0.682 + 5 0.591 + 6 0.500 + 7 0.455 + 8 0.591 + 9 0.500 + 10 0.500 -#Frame DSSP[Anti] - 1 0.591 - 2 0.636 - 3 0.591 - 4 0.682 - 5 0.591 - 6 0.500 - 7 0.455 - 8 0.591 - 9 0.500 - 10 0.500 +#Frame DSSP[Bridge] + 1 0.000 + 2 0.000 + 3 0.000 + 4 0.000 + 5 0.000 + 6 0.000 + 7 0.000 + 8 0.000 + 9 0.000 + 10 0.000 #Frame DSSP[3-10] 1 0.000 @@ -95,13 +95,13 @@ 10 0.182 #Frame ACE:1 VAL:2 PHE:3 ILE:4 THR:5 SER:6 PRO:7 GLY:8 LYS:9 THR:10 TYR:11 THR:12 GLU:13 VAL:14 PRO:15 GLY:16 LYS:17 LYS:18 ILE:19 LEU:20 GLN:21 NHE:22 - 1 0 2 2 2 2 7 7 7 0 2 2 2 2 2 6 6 2 2 2 2 0 0 - 2 0 2 2 2 2 7 7 7 0 2 2 2 2 2 6 6 2 2 2 2 2 0 - 3 0 2 2 2 2 0 7 7 0 2 2 2 2 2 6 6 2 2 2 2 0 0 - 4 0 2 2 2 2 2 6 6 2 2 2 2 2 2 6 6 2 2 2 2 0 0 - 5 0 2 2 2 2 6 6 7 0 2 2 2 2 2 6 6 2 2 2 2 0 0 - 6 0 2 2 2 0 3 3 3 0 0 2 2 2 2 6 6 2 2 2 2 0 0 - 7 0 2 2 2 2 0 7 7 0 2 2 2 2 0 7 7 0 2 2 0 0 0 - 8 0 2 2 2 2 7 7 7 7 2 2 2 2 2 6 6 2 2 2 2 0 0 - 9 0 2 2 2 2 7 7 7 0 2 2 2 2 0 7 7 0 2 2 2 0 0 - 10 0 2 2 2 2 7 7 7 0 2 2 2 2 6 6 7 0 2 2 2 0 0 + 1 0 1 1 1 1 7 7 7 0 1 1 1 1 1 6 6 1 1 1 1 0 0 + 2 0 1 1 1 1 7 7 7 0 1 1 1 1 1 6 6 1 1 1 1 1 0 + 3 0 1 1 1 1 0 7 7 0 1 1 1 1 1 6 6 1 1 1 1 0 0 + 4 0 1 1 1 1 1 6 6 1 1 1 1 1 1 6 6 1 1 1 1 0 0 + 5 0 1 1 1 1 6 6 7 0 1 1 1 1 1 6 6 1 1 1 1 0 0 + 6 0 1 1 1 0 3 3 3 0 0 1 1 1 1 6 6 1 1 1 1 0 0 + 7 0 1 1 1 1 0 7 7 0 1 1 1 1 0 7 7 0 1 1 0 0 0 + 8 0 1 1 1 1 7 7 7 7 1 1 1 1 1 6 6 1 1 1 1 0 0 + 9 0 1 1 1 1 7 7 7 0 1 1 1 1 0 7 7 0 1 1 1 0 0 + 10 0 1 1 1 1 7 7 7 0 1 1 1 1 6 6 7 0 1 1 1 0 0 From f936521c6bda6af7c0a4eb5bf3fe34cb9666f13c Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 3 Sep 2019 08:10:52 -0400 Subject: [PATCH 61/62] DRR - Cpptraj: Fix printf format string; Range.Size() is int, not unsigned. --- src/Action_DSSP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Action_DSSP.cpp b/src/Action_DSSP.cpp index 5dfae709bf..9075abcc06 100644 --- a/src/Action_DSSP.cpp +++ b/src/Action_DSSP.cpp @@ -511,7 +511,7 @@ Action::RetType Action_DSSP::Setup(ActionSetup& setup) } } // END residue is selected } - mprintf("\t%u of %zu solute residues selected.\n", nResSelected, soluteRes.Size()); + mprintf("\t%u of %i solute residues selected.\n", nResSelected, soluteRes.Size()); // DEBUG - print each residue set up. if (debug_ > 0) { From 77d8eb0c7bc6221340b6df18981a14178bce2f5b Mon Sep 17 00:00:00 2001 From: "Daniel R. Roe" Date: Tue, 3 Sep 2019 10:15:29 -0400 Subject: [PATCH 62/62] DRR - Cpptraj: Protect test when no netcdf --- test/Test_DSSP/RunTest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Test_DSSP/RunTest.sh b/test/Test_DSSP/RunTest.sh index 5e8a1f1fa4..71413a1e2a 100755 --- a/test/Test_DSSP/RunTest.sh +++ b/test/Test_DSSP/RunTest.sh @@ -68,7 +68,7 @@ fi # FtuFabI Assignment test UNITNAME='FtuFabI Assignment test' -CheckFor maxthreads 10 +CheckFor netcdf maxthreads 10 if [ $? -eq 0 ] ; then TOP='' cat > cpptraj.in <