/* Function: p7_alidisplay_Create() * Synopsis: Create an alignment display, from trace and oprofile. * Incept: SRE, Sun Dec 30 09:13:31 2007 [Janelia] * * Purpose: Creates and returns an alignment display for domain number * <which> in traceback <tr>, where the traceback * corresponds to an alignment of optimized profile <om> to digital sequence * <dsq>, and the unique name of that target * sequence <dsq> is <sqname>. The <which> index starts at 0. * * It will be a little faster if the trace is indexed with * <p7_trace_Index()> first. The number of domains is then * in <tr->ndom>. If the caller wants to create alidisplays * for all of these, it would loop <which> from * <0..tr->ndom-1>. * * However, even without an index, the routine will work fine. * * Args: tr - traceback * which - domain number, 0..tr->ndom-1 * om - optimized profile (query) * sq - digital sequence (target) * * Returns: <eslOK> on success. * * Throws: <NULL> on allocation failure, or if something's internally corrupt * in the data. */ P7_ALIDISPLAY * p7_alidisplay_Create(const P7_TRACE *tr, int which, const P7_OPROFILE *om, const ESL_SQ *sq) { P7_ALIDISPLAY *ad = NULL; char *Alphabet = om->abc->sym; int n, pos, z; int z1,z2; int k,x,i,s; int hmm_namelen, hmm_acclen, hmm_desclen; int sq_namelen, sq_acclen, sq_desclen; int status; /* First figure out which piece of the trace (from first match to last match) * we're going to represent, and how big it is. */ if (tr->ndom > 0) { /* if we have an index, this is a little faster: */ for (z1 = tr->tfrom[which]; z1 < tr->N; z1++) if (tr->st[z1] == p7T_M) break; /* find next M state */ if (z1 == tr->N) return NULL; /* no M? corrupt trace */ for (z2 = tr->tto[which]; z2 >= 0 ; z2--) if (tr->st[z2] == p7T_M) break; /* find prev M state */ if (z2 == -1) return NULL; /* no M? corrupt trace */ } else { /* without an index, we can still do it fine: */ for (z1 = 0; which >= 0 && z1 < tr->N; z1++) if (tr->st[z1] == p7T_B) which--; /* find the right B state */ if (z1 == tr->N) return NULL; /* no such domain <which> */ for (; z1 < tr->N; z1++) if (tr->st[z1] == p7T_M) break; /* find next M state */ if (z1 == tr->N) return NULL; /* no M? corrupt trace */ for (z2 = z1; z2 < tr->N; z2++) if (tr->st[z2] == p7T_E) break; /* find the next E state */ for (; z2 >= 0; z2--) if (tr->st[z2] == p7T_M) break; /* find prev M state */ if (z2 == -1) return NULL; /* no M? corrupt trace */ } /* Now we know that z1..z2 in the trace will be represented in the * alidisplay; that's z2-z1+1 positions. We need a \0 trailer on all * our display lines, so allocate z2-z1+2. We know each position is * M, D, or I, so there's a 1:1 correspondence of trace positions * with alignment display positions. We also know the display * starts and ends with M states. * * So now let's allocate. The alidisplay is packed into a single * memory space, so this appears to be intricate, but it's just * bookkeeping. */ n = (z2-z1+2) * 3; /* model, mline, aseq mandatory */ if (om->rf[0] != 0) n += z2-z1+2; /* optional reference line */ if (om->cs[0] != 0) n += z2-z1+2; /* optional structure line */ if (tr->pp != NULL) n += z2-z1+2; /* optional posterior prob line */ hmm_namelen = strlen(om->name); n += hmm_namelen + 1; hmm_acclen = (om->acc != NULL ? strlen(om->acc) : 0); n += hmm_acclen + 1; hmm_desclen = (om->desc != NULL ? strlen(om->desc) : 0); n += hmm_desclen + 1; sq_namelen = strlen(sq->name); n += sq_namelen + 1; sq_acclen = strlen(sq->acc); n += sq_acclen + 1; /* sq->acc is "\0" when unset */ sq_desclen = strlen(sq->desc); n += sq_desclen + 1; /* same for desc */ ESL_ALLOC(ad, sizeof(P7_ALIDISPLAY)); ad->mem = NULL; pos = 0; ad->memsize = sizeof(char) * n; ESL_ALLOC(ad->mem, ad->memsize); if (om->rf[0] != 0) { ad->rfline = ad->mem + pos; pos += z2-z1+2; } else { ad->rfline = NULL; } if (om->cs[0] != 0) { ad->csline = ad->mem + pos; pos += z2-z1+2; } else { ad->csline = NULL; } ad->model = ad->mem + pos; pos += z2-z1+2; ad->mline = ad->mem + pos; pos += z2-z1+2; ad->aseq = ad->mem + pos; pos += z2-z1+2; if (tr->pp != NULL) { ad->ppline = ad->mem + pos; pos += z2-z1+2;} else { ad->ppline = NULL; } ad->hmmname = ad->mem + pos; pos += hmm_namelen +1; ad->hmmacc = ad->mem + pos; pos += hmm_acclen +1; ad->hmmdesc = ad->mem + pos; pos += hmm_desclen +1; ad->sqname = ad->mem + pos; pos += sq_namelen +1; ad->sqacc = ad->mem + pos; pos += sq_acclen +1; ad->sqdesc = ad->mem + pos; pos += sq_desclen +1; strcpy(ad->hmmname, om->name); if (om->acc != NULL) strcpy(ad->hmmacc, om->acc); else ad->hmmacc[0] = 0; if (om->desc != NULL) strcpy(ad->hmmdesc, om->desc); else ad->hmmdesc[0] = 0; strcpy(ad->sqname, sq->name); strcpy(ad->sqacc, sq->acc); strcpy(ad->sqdesc, sq->desc); /* Determine hit coords */ ad->hmmfrom = tr->k[z1]; ad->hmmto = tr->k[z2]; ad->M = om->M; ad->sqfrom = tr->i[z1]; ad->sqto = tr->i[z2]; ad->L = sq->n; /* optional rf line */ if (ad->rfline != NULL) { for (z = z1; z <= z2; z++) ad->rfline[z-z1] = ((tr->st[z] == p7T_I) ? '.' : om->rf[tr->k[z]]); ad->rfline[z-z1] = '\0'; } /* optional cs line */ if (ad->csline != NULL) { for (z = z1; z <= z2; z++) ad->csline[z-z1] = ((tr->st[z] == p7T_I) ? '.' : om->cs[tr->k[z]]); ad->csline[z-z1] = '\0'; } /* optional pp line */ if (ad->ppline != NULL) { for (z = z1; z <= z2; z++) ad->ppline[z-z1] = ( (tr->st[z] == p7T_D) ? '.' : p7_alidisplay_EncodePostProb(tr->pp[z])); ad->ppline[z-z1] = '\0'; } /* mandatory three alignment display lines: model, mline, aseq */ for (z = z1; z <= z2; z++) { k = tr->k[z]; i = tr->i[z]; x = sq->dsq[i]; s = tr->st[z]; switch (s) { case p7T_M: ad->model[z-z1] = om->consensus[k]; if (x == esl_abc_DigitizeSymbol(om->abc, om->consensus[k])) ad->mline[z-z1] = ad->model[z-z1]; else if (p7_oprofile_FGetEmission(om, k, x) > 1.0) ad->mline[z-z1] = '+'; /* >1 not >0; om has odds ratios, not scores */ else ad->mline[z-z1] = ' '; ad->aseq [z-z1] = toupper(Alphabet[x]); break; case p7T_I: ad->model [z-z1] = '.'; ad->mline [z-z1] = ' '; ad->aseq [z-z1] = tolower(Alphabet[x]); break; case p7T_D: ad->model [z-z1] = om->consensus[k]; ad->mline [z-z1] = ' '; ad->aseq [z-z1] = '-'; break; default: ESL_XEXCEPTION(eslEINVAL, "invalid state in trace: not M,D,I"); } } ad->model [z2-z1+1] = '\0'; ad->mline [z2-z1+1] = '\0'; ad->aseq [z2-z1+1] = '\0'; ad->N = z2-z1+1; return ad; ERROR: p7_alidisplay_Destroy(ad); return NULL; }
/* Function: p7_bg_Read() * Synopsis: Read background frequencies from a file. * * Purpose: Read new background frequencies from file <bgfile>, * overwriting the frequencies previously in the * <P7_BG> object <bg>. * * Note that <bg> is already created by the caller, not * created here. Also note that <p7_bg_Read()> only reads * residue background frequencies used for the "null * model", whereas a <P7_BG> object contains additional * information for the bias filter and for the biased * composition correction. * * Args: bgfile - file to read. * bg - existing <P7_BG> object provided by the caller. * errbuf - OPTIONAL: space for an error message, upon parse errors; or NULL. * * Returns: <eslOK> on success, and background frequencies in <bg> * are overwritten. * * <eslENOTFOUND> if <bgfile> can't be opened for reading. * <eslEFORMAT> if parsing of <bgfile> fails for some * reason. In both cases, <errbuf> contains a * user-directed error message upon return, including (if * relevant) the file name <bgfile> and the line number on * which an error was detected. <bg> is unmodified. * * Throws: <eslEMEM> on allocation failure; <bg> is unmodified, * and <errbuf> is empty. */ int p7_bg_Read(char *bgfile, P7_BG *bg, char *errbuf) { ESL_FILEPARSER *efp = NULL; float *fq = NULL; int n = 0; char *tok; int toklen; int alphatype; ESL_DSQ x; int status; if (errbuf) errbuf[0] = '\0'; status = esl_fileparser_Open(bgfile, NULL, &efp); if (status == eslENOTFOUND) ESL_XFAIL(eslENOTFOUND, errbuf, "couldn't open bg file %s for reading", bgfile); else if (status != eslOK) goto ERROR; esl_fileparser_SetCommentChar(efp, '#'); /* First token is alphabet type: amino | DNA | RNA */ status = esl_fileparser_GetToken(efp, &tok, &toklen); if (status == eslEOF) ESL_XFAIL(eslEFORMAT, errbuf, "premature end of file [line %d of bgfile %s]", efp->linenumber, bgfile); else if (status != eslOK) goto ERROR; alphatype = esl_abc_EncodeType(tok); if (alphatype == eslUNKNOWN) ESL_XFAIL(eslEFORMAT, errbuf, "expected alphabet type but saw \"%s\" [line %d of bgfile %s]", tok, efp->linenumber, bgfile); else if (alphatype != bg->abc->type) ESL_XFAIL(eslEFORMAT, errbuf, "bg file's alphabet is %s; expected %s [line %d, %s]", tok, esl_abc_DecodeType(bg->abc->type), efp->linenumber, bgfile); ESL_ALLOC(fq, sizeof(float) * bg->abc->K); esl_vec_FSet(fq, bg->abc->K, -1.0); while ((status = esl_fileparser_NextLine(efp)) == eslOK) { status = esl_fileparser_GetTokenOnLine(efp, &tok, &toklen); if (status == eslEOL) ESL_XFAIL(eslEFORMAT, errbuf, "premature end of file [line %d of bgfile %s", efp->linenumber, bgfile); else if (status != eslOK) goto ERROR; if (toklen != 1 || ! esl_abc_CIsCanonical(bg->abc, *tok)) ESL_XFAIL(eslEFORMAT, errbuf, "expected to parse a residue letter; saw %s [line %d of bgfile %s]", tok, efp->linenumber, bgfile); x = esl_abc_DigitizeSymbol(bg->abc, *tok); if (fq[x] != -1.0) ESL_XFAIL(eslEFORMAT, errbuf, "already parsed probability of %c [line %d of bgfile %s]", bg->abc->sym[x], efp->linenumber, bgfile); n++; status = esl_fileparser_GetTokenOnLine(efp, &tok, &toklen); if (status == eslEOL) ESL_XFAIL(eslEFORMAT, errbuf, "premature end of file, expected a probability [line %d of bgfile %s]", efp->linenumber, bgfile); else if (status != eslOK) goto ERROR; if (! esl_str_IsReal(tok)) ESL_XFAIL(eslEFORMAT, errbuf, "expected a probability, saw %s [line %d of bgfile %s]", tok, efp->linenumber, bgfile); fq[x] = atof(tok); status = esl_fileparser_GetTokenOnLine(efp, &tok, &toklen); if (status == eslOK) ESL_XFAIL(eslEFORMAT, errbuf, "extra unexpected data found [line %d of bgfile %s]", efp->linenumber, bgfile); else if (status != eslEOL) goto ERROR; } if (status != eslEOF) goto ERROR; if ( n != bg->abc->K) ESL_XFAIL(eslEFORMAT, errbuf, "expected %d residue frequencies, but found %d in bgfile %s", bg->abc->K, n, bgfile); if ( esl_FCompare(esl_vec_FSum(fq, bg->abc->K), 1.0, 0.001) != eslOK) ESL_XFAIL(eslEFORMAT, errbuf, "residue frequencies do not sum to 1.0 in bgfile %s", bgfile); /* all checking complete. no more error cases. overwrite bg with the new frequencies */ esl_vec_FNorm(fq, bg->abc->K); esl_vec_FCopy(fq, bg->abc->K, bg->f); free(fq); esl_fileparser_Close(efp); return eslOK; ERROR: if (fq) free(fq); if (efp) esl_fileparser_Close(efp); return status; }
/* Function: p7_alidisplay_Backconvert() * Synopsis: Convert an alidisplay to a faux trace and subsequence. * Incept: SRE, Wed Dec 10 09:49:28 2008 [Janelia] * * Purpose: Convert alignment display object <ad> to a faux subsequence * and faux subsequence trace, returning them in <ret_sq> and * <ret_tr>. * * The subsequence <*ret_sq> is digital; ascii residues in * <ad> are digitized using digital alphabet <abc>. * * The subsequence and trace are suitable for passing as * array elements to <p7_MultipleAlignment>. This is the * main purpose of backconversion. Results of a profile * search are stored in a hit list as a processed * <P7_ALIDISPLAY>, not as a <P7_TRACE> and <ESL_SQ>, to * reduce space and to reduce communication overhead in * parallelized search implementations. After reduction * to a final hit list, a master may want to construct a * multiple alignment of all the significant hits. * * Returns: <eslOK> on success. * * Throws: <eslEMEM> on allocation failures. <eslECORRUPT> on unexpected internal * data corruption. On any exception, <*ret_sq> and <*ret_tr> are * <NULL>. * * Xref: J4/29. */ int p7_alidisplay_Backconvert(const P7_ALIDISPLAY *ad, const ESL_ALPHABET *abc, ESL_SQ **ret_sq, P7_TRACE **ret_tr) { ESL_SQ *sq = NULL; /* RETURN: faux subsequence */ P7_TRACE *tr = NULL; /* RETURN: faux trace */ int subL = 0; /* subsequence length in the <ad> */ int a, i, k; /* coords for <ad>, <sq->dsq>, model */ char st; /* state type: MDI */ int status; /* Make a first pass over <ad> just to calculate subseq length */ for (a = 0; a < ad->N; a++) if (! esl_abc_CIsGap(abc, ad->aseq[a])) subL++; /* Allocations */ if ((sq = esl_sq_CreateDigital(abc)) == NULL) { status = eslEMEM; goto ERROR; } if ((status = esl_sq_GrowTo(sq, subL)) != eslOK) goto ERROR; if ((tr = (ad->ppline == NULL) ? p7_trace_Create() : p7_trace_CreateWithPP()) == NULL) { status = eslEMEM; goto ERROR; } if ((status = p7_trace_GrowTo(tr, subL+6)) != eslOK) goto ERROR; /* +6 is for SNB/ECT */ /* Construction of dsq, trace */ sq->dsq[0] = eslDSQ_SENTINEL; if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, p7T_S, 0, 0) : p7_trace_AppendWithPP(tr, p7T_S, 0, 0, 0.0))) != eslOK) goto ERROR; if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, p7T_N, 0, 0) : p7_trace_AppendWithPP(tr, p7T_N, 0, 0, 0.0))) != eslOK) goto ERROR; if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, p7T_B, 0, 0) : p7_trace_AppendWithPP(tr, p7T_B, 0, 0, 0.0))) != eslOK) goto ERROR; k = ad->hmmfrom; i = 1; for (a = 0; a < ad->N; a++) { if (esl_abc_CIsResidue(abc, ad->model[a])) { st = (esl_abc_CIsResidue(abc, ad->aseq[a]) ? p7T_M : p7T_D); } else st = p7T_I; if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, st, k, i) : p7_trace_AppendWithPP(tr, st, k, i, p7_alidisplay_DecodePostProb(ad->ppline[a])))) != eslOK) goto ERROR; switch (st) { case p7T_M: sq->dsq[i] = esl_abc_DigitizeSymbol(abc, ad->aseq[a]); k++; i++; break; case p7T_I: sq->dsq[i] = esl_abc_DigitizeSymbol(abc, ad->aseq[a]); i++; break; case p7T_D: k++; break; } } if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, p7T_E, 0, 0) : p7_trace_AppendWithPP(tr, p7T_E, 0, 0, 0.0))) != eslOK) goto ERROR; if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, p7T_C, 0, 0) : p7_trace_AppendWithPP(tr, p7T_C, 0, 0, 0.0))) != eslOK) goto ERROR; if ((status = ((ad->ppline == NULL) ? p7_trace_Append(tr, p7T_T, 0, 0) : p7_trace_AppendWithPP(tr, p7T_T, 0, 0, 0.0))) != eslOK) goto ERROR; sq->dsq[i] = eslDSQ_SENTINEL; /* some sanity checks */ if (tr->N != ad->N + 6) ESL_XEXCEPTION(eslECORRUPT, "backconverted trace ended up with unexpected size (%s/%s)", ad->sqname, ad->hmmname); if (k != ad->hmmto + 1) ESL_XEXCEPTION(eslECORRUPT, "backconverted trace didn't end at expected place on model (%s/%s)", ad->sqname, ad->hmmname); if (i != subL + 1) ESL_XEXCEPTION(eslECORRUPT, "backconverted subseq didn't end at expected length (%s/%s)", ad->sqname, ad->hmmname); /* Set up <sq> annotation as a subseq of a source sequence */ if ((status = esl_sq_FormatName(sq, "%s/%ld-%ld", ad->sqname, ad->sqfrom, ad->sqto)) != eslOK) goto ERROR; if ((status = esl_sq_FormatDesc(sq, "[subseq from] %s", ad->sqdesc[0] != '\0' ? ad->sqdesc : ad->sqname)) != eslOK) goto ERROR; if ((status = esl_sq_SetSource (sq, ad->sqname)) != eslOK) goto ERROR; if (ad->sqacc[0] != '\0') { if ((status = esl_sq_SetAccession (sq, ad->sqacc)) != eslOK) goto ERROR; } sq->n = subL; sq->start = ad->sqfrom; sq->end = ad->sqto; sq->C = 0; sq->W = subL; sq->L = ad->L; tr->M = ad->M; tr->L = ad->L; *ret_sq = sq; *ret_tr = tr; return eslOK; ERROR: if (sq != NULL) esl_sq_Destroy(sq); if (tr != NULL) p7_trace_Destroy(tr); *ret_sq = NULL; *ret_tr = NULL; return status; }
int convert_to_galosh_profile ( P7_HMM * hmm, ProfileType & profile ) { typedef typename galosh::profile_traits<ProfileType>::ResidueType ResidueType; int status; uint32_t pos_i; // Position in profile. Corresponds to one less than match state pos in HMM. uint32_t res_i; ESL_DSQ hmmer_digitized_residue; /* How many match states in the HMM? */ if( hmm->M == 0 ) { status = eslENORESULT; goto ERROR; } profile.reinitialize( static_cast<uint32_t>( hmm->M ) ); profile.zero(); // NOTE that HMMER3 has a slightly different model, starting in // Begin rather than in preAlign, and with 3 legal transitions out // of Begin (one of these is to PreAlign). The galosh profile model // begins in preAlign and transitions to Begin, and from there to // either Match or Delete. One implication is that galosh profiles // enforce t[ 0 ][ p7H_MI ] to be the same as t[ 0 ][ p7H_II ], but // HMMER3 does not. Another way to say this is that H3 uses affine // pre-aligns, and prohibits pre-align -to- delete transitions, // whereas galosh / profillic uses non-affine pre-aligns and allows // pre-align->delete. // fromPreAlign profile[ galosh::Transition::fromPreAlign ][ galosh::TransitionFromPreAlign::toPreAlign ] = hmm->t[ 0 ][ p7H_II ]; profile[ galosh::Transition::fromPreAlign ][ galosh::TransitionFromPreAlign::toBegin ] = hmm->t[ 0 ][ p7H_IM ]; for( res_i = 0; res_i < seqan::ValueSize<ResidueType>::VALUE; res_i++ ) { hmmer_digitized_residue = esl_abc_DigitizeSymbol( hmm->abc, static_cast<char>( ResidueType( res_i ) ) ); // See below where it says "TODO/NOTE".. profile[ galosh::Emission::PreAlignInsertion ][ res_i ] = hmm->ins[ 0 ][ hmmer_digitized_residue ]; } // fromBegin profile[ galosh::Transition::fromBegin ][ galosh::TransitionFromBegin::toMatch ] = ( hmm->t[ 0 ][ p7H_MM ] / ( 1.0 - hmm->t[ 0 ][ p7H_MI ] ) ); profile[ galosh::Transition::fromBegin ][ galosh::TransitionFromBegin::toDeletion ] = ( 1.0 - profile[ galosh::Transition::fromBegin ][ galosh::TransitionFromBegin::toMatch ] ); for( pos_i = 0; pos_i < profile.length(); pos_i++ ) { // if( be_verbose ) { // cout << '.'; // cout.flush(); // } // TODO: If this is too slow, memoize the ResidueType( res_i )s. for( res_i = 0; res_i < seqan::ValueSize<ResidueType>::VALUE; res_i++ ) { hmmer_digitized_residue = esl_abc_DigitizeSymbol( hmm->abc, static_cast<char>( ResidueType( res_i ) ) ); profile[ pos_i ][ galosh::Emission::Match ][ res_i ] = hmm->mat[ pos_i + 1 ][ hmmer_digitized_residue ]; if( pos_i == ( profile.length() - 1 ) ) { // Use post-align insertions profile[ galosh::Emission::PostAlignInsertion ][ res_i ] = hmm->ins[ pos_i + 1 ][ hmmer_digitized_residue ]; } else { // if this is the last position (use post-align insertions) .. else .. profile[ galosh::Emission::Insertion ][ res_i ] += hmm->ins[ pos_i + 1 ][ hmmer_digitized_residue ]; } // End if this is the last position (use post-align insertions) .. else .. } // End foreach res_i if( pos_i == ( profile.length() - 1 ) ) { // Use post-align insertions profile[ galosh::Transition::fromPostAlign ][ galosh::TransitionFromPostAlign::toTerminal ] = hmm->t[ pos_i + 1 ][ p7H_IM ]; profile[ galosh::Transition::fromPostAlign ][ galosh::TransitionFromPostAlign::toPostAlign ] = ( 1.0 - profile[ galosh::Transition::fromPostAlign ][ galosh::TransitionFromPostAlign::toTerminal ] ); } else { // if this is the last position (use post-align insertions) .. else .. profile[ galosh::Transition::fromMatch ][ galosh::TransitionFromMatch::toMatch ] += hmm->t[ pos_i + 1 ][ p7H_MM ]; profile[ galosh::Transition::fromMatch ][ galosh::TransitionFromMatch::toInsertion ] += hmm->t[ pos_i + 1 ][ p7H_MI ]; profile[ galosh::Transition::fromMatch ][ galosh::TransitionFromMatch::toDeletion ] += hmm->t[ pos_i + 1 ][ p7H_MD ]; profile[ galosh::Transition::fromInsertion ][ galosh::TransitionFromInsertion::toMatch ] += hmm->t[ pos_i + 1 ][ p7H_IM ]; profile[ galosh::Transition::fromInsertion ][ galosh::TransitionFromInsertion::toInsertion ] += hmm->t[ pos_i + 1 ][ p7H_II ]; profile[ galosh::Transition::fromDeletion ][ galosh::TransitionFromDeletion::toMatch ] += hmm->t[ pos_i + 1 ][ p7H_DM ]; profile[ galosh::Transition::fromDeletion ][ galosh::TransitionFromDeletion::toDeletion ] += hmm->t[ pos_i + 1 ][ p7H_DD ]; } // End if this is the last position (use post-align insertions) .. else .. } // End foreach pos_i // Normalize with 0 as the minimum value we'll allow. Note that in // profillic and profuse, it's generally 1E-5, so when the profile // is read in by those programs, it might be slightly altered. profile.normalize( 0 ); return eslOK; ERROR: return status; } // convert_to_galosh_profile (..)