91 for(
auto ui=0; ui<link.size(); ui++)
94 link[ui] = link[link[ui]];
98 map<size_t,size_t> uniqMap;
101 if(uniqMap.find(v) != uniqMap.end())
105 size_t mapSize=uniqMap.size();
127 if(aStart >=bStart && aStart<= bEnd)
130 if(aEnd >=bStart && aEnd<= bEnd)
148 vector<pair<string,size_t> > fragments;
156 vector<pair<size_t,unsigned int> > symbolIndices;
163 set<SIMPLE_SPECIES> molecule;
165 for(
const auto &idxPair: symbolIndices)
167 if(idxPair.first == -1)
176 species.
count=idxPair.second;
178 molecule.insert(species);
188 moleculeData.push_back(molecule);
190 ionNames.push_back(r.
getName(i));
199 if(badIons.find(ionID) != badIons.end())
210 if(ionNames != other.ionNames)
212 if(moleculeData != other.moleculeData)
214 if(colours != other.colours)
217 if(ranges != other.ranges)
220 if(rangeGrouping != other.rangeGrouping)
223 if(ionIDs !=other.ionIDs)
232 unsigned int ionSize=moleculeData.size();
235 if(ionSize !=colours.size())
238 unsigned int rangeSize=ranges.size();
240 if(rangeSize != ionIDs.size())
244 for(
auto &i :moleculeData)
250 for(
auto &name : ionNames )
256 for(
auto &rng : ranges)
258 if(rng.first >= rng.second)
264 if((rangeGrouping.size()) && (rangeGrouping.size() != ranges.size()))
272 const std::string &name,
const RGBf &ionCol)
277 for(
auto i:moleculeData)
283 moleculeData.push_back(ions);
284 colours.push_back(ionCol);
285 ionNames.push_back(name);
290 return moleculeData.size()-1;
294 const std::string &name,
const RGBf &ionCol)
298 set<SIMPLE_SPECIES> sSet;
300 return addIon(sSet,name,ionCol);
305 ASSERT(ionID < moleculeData.size());
310 ionIDs.push_back(ionID);
311 ranges.push_back(std::make_pair(start,end));
313 return ionIDs.size()-1;
318 return addRange(rng.first,rng.second,ionID);
323 ASSERT(groups.empty() || groups.size() == ranges.size());
324 rangeGrouping=groups;
329 std::ofstream f(fileName);
340 f <<
"<multirange version=\"" << MULTIRANGE_FORMAT_VERSION <<
"\">" << endl;
341 std::time_t t = std::time(
nullptr);
346 timeStr=std::put_time(std::gmtime(&t),
"%c %Z");
350 f <<
tabs(1) <<
"<created date=\"" << timeStr<<
354 f <<
tabs(1) <<
"<molecules>" << endl;
356 for(
auto ui=0; ui<moleculeData.size(); ui++)
358 f <<
tabs(2) <<
"<entry>" << endl;
359 f <<
tabs(3) <<
"<name string=\"" << ionNames[ui] <<
"\"/>" << endl;
360 f <<
tabs(3) <<
"<colour hex=\"" << colours[ui].toHex() <<
"\"/>" << endl;
362 f <<
tabs(3) <<
"<components>" << endl;
363 for(
auto mol : moleculeData[ui])
365 f <<
tabs(4) <<
"<isotope atomicnumber=\"" << mol.atomicNumber <<
366 "\" count=\"" << mol.count <<
"\"/>" << endl;
369 f <<
tabs(3) <<
"</components>" << endl;
370 f <<
tabs(2) <<
"</entry>" << endl;
374 f <<
tabs(1) <<
"</molecules>" << endl;
375 f <<
tabs(1) <<
"<ranges>" << endl;
376 for(
auto ui=0;ui<ranges.size(); ui++)
378 f <<
tabs(2) <<
"<range start=\"" << ranges[ui].first <<
"\" end=\"" << ranges[ui].second <<
"\" parent=\"" << ionNames[ionIDs[ui]];
380 if(rangeGrouping.size())
381 f <<
" group=\"" << rangeGrouping[ui] <<
"\" ";
385 f <<
tabs(1) <<
"</ranges>" << endl;
386 f <<
"</multirange>" <<endl;
404 std::ifstream f(filename);
416 xmlParserCtxtPtr context;
417 context = xmlNewParserCtxt();
426 doc = xmlCtxtReadFile(context, filename, NULL,
427 XML_PARSE_NOENT| XML_PARSE_NONET);
437 std::unique_ptr<_xmlDoc,void (*)(void *)> cleanup{doc,
XMLFreeDoc};
441 xmlFreeParserCtxt(context);
445 xmlFreeParserCtxt(context);
448 xmlNodePtr nodePtr = xmlDocGetRootElement(doc);
456 if(xmlStrcmp(nodePtr->name, (
const xmlChar *)
"multirange"))
463 if(!nodePtr->xmlChildrenNode)
468 nodePtr = nodePtr->xmlChildrenNode;
486 if(!nodePtr->xmlChildrenNode)
492 stack<xmlNodePtr> nodeStack;
493 nodeStack.push(nodePtr);
494 nodePtr=nodePtr->xmlChildrenNode;
503 nodeStack.push(nodePtr);
504 nodePtr=nodePtr->xmlChildrenNode;
518 ionNames.push_back(str);
538 col.
red=v[0]/255.0; col.
green =v[1]/255.0; col.
blue=v[2]/255.0;
539 colours.push_back(col);
548 set<SIMPLE_SPECIES> mol;
549 nodeStack.push(nodePtr);
550 nodePtr = nodePtr->xmlChildrenNode;
571 nodePtr=nodePtr->next;
574 while(nodePtr && std::string((
char*)nodePtr->name) ==
"text")
575 nodePtr=nodePtr->next;
578 moleculeData.push_back(mol);
581 nodePtr=nodeStack.top();
584 nodePtr=nodePtr->next;
585 while(nodePtr && std::string((
char*)nodePtr->name) ==
"text")
586 nodePtr=nodePtr->next;
589 nodePtr=nodeStack.top();
601 if(!nodePtr->xmlChildrenNode)
607 nodeStack.push(nodePtr);
608 nodePtr=nodePtr->xmlChildrenNode;
617 std::pair<float,float> p;
629 if(p.first > p.second)
637 std::string parentStr;
644 auto parentIt = std::find(ionNames.begin(),ionNames.end(),parentStr);
645 if(parentIt ==ionNames.end())
651 unsigned int parentId=parentIt-ionNames.begin();
652 ionIDs.push_back(parentId);
656 std::string groupStr;
665 rangeGrouping.push_back(rGroup);
669 nodePtr=nodePtr->next;
670 while(nodePtr && std::string((
char*)nodePtr->name) ==
"text")
671 nodePtr=nodePtr->next;
673 if(rangeGrouping.size() && (rangeGrouping.size() !=ranges.size()))
695 const char *errMsg[] = {
"Unable to create valid parser",
696 "Unable to create XML document structure",
697 "Unable to create XML document context",
698 "Unable to create node pointer",
699 "Unable to find valid root node",
700 "Unable to find content node",
701 "Unable to find timestamp node",
702 "Unable to find molecules node",
703 "Unable to find molecule entry node",
704 "Missing molecule entry",
705 "Missing molecule name",
706 "Missing molecule components",
709 "Missing molecule colour",
710 "Bad molecule colour",
711 "Bad group identifier",
712 "Data is not self-consistent",
713 "No parent range found"};
717 return errMsg[errState];
722 return ranges.size();
729 ASSERT(ionID < moleculeData.size());
730 return moleculeData[ionID];
735 ASSERT(ionID < ionNames.size());
736 return ionNames[ionID];
741 ASSERT(ionID < moleculeData.size());
742 unsigned int v=std::count(ionIDs.begin(),ionIDs.end(),ionID);
749 return moleculeData.size();
754 ASSERT(range < ranges.size());
756 return ionIDs[
range];
761 ASSERT(rng<ranges.size());
767 vector<unsigned int> tmp;
768 for(
auto ui=0;ui<ranges.size();ui++)
770 if(ranges[ui].first <= mass && ranges[ui].second > mass)
780 ASSERT(newIonID < ionIDs.size());
781 ionIDs[
range] = newIonID;
787 ASSERT(ionID<moleculeData.size());
788 return colours[ionID];
793 ASSERT(ionID<moleculeData.size());
799 for(
auto range : ranges)
801 if( mass >=
range.first &&
802 mass <
range.second )
816 vector<IonHit> rangedVec;
818 unsigned int numIons=ions.size();
819 rangedVec.reserve(numIons);
825 rangedVec.push_back(ion);
828 ions.swap(rangedVec);
834 moleculeData.clear();
838 rangeGrouping.clear();
846 ASSERT(rangeGrouping.size());
850 vector<FLATTENED_RANGE> flatRange;
854 map<size_t,size_t> groupMapping;
855 vector<size_t> link(flatRange.size(),-1);
858 for(
auto ui=0;ui<flatRange.size();ui++)
862 for(
auto contained : flatRange[ui].containedRangeIDs)
864 size_t gId = rangeGrouping[contained];
865 if(groupMapping.find(gId) == groupMapping.end())
866 groupMapping[gId] = ui;
870 link[ui] = groupMapping[gId];
874 link[ui] = std::min((
size_t)groupMapping[gId],(
size_t)link[ui]);
880 ASSERT(std::find(link.begin(),link.end(),-1) == link.end());
889 *std::max_element(link.begin(),link.end())+1);
893 for(
auto ui=0;ui<link.size();ui++)
897 for(
auto uj=0;uj<ranges.size();uj++)
901 flatRange[ui].startMass,flatRange[ui].endMass))
908 for(
auto &s : splitMR)
910 ASSERT(s.isSelfConsistent());
922 auto rngCopy = ranges;
925 std::sort(rngCopy.begin(),rngCopy.end(),cmp);
930 fRng.endMass=rngCopy[0].second;
932 auto offFirst= std::distance(ranges.begin(),
933 std::find(ranges.begin(),ranges.end(),rngCopy[0]));
935 fRng.containedRangeIDs.push_back(offFirst);
936 fRng.containedIonIDs.push_back(ionIDs[offFirst]);
938 for(
auto ui=1u;ui<rngCopy.size();ui++)
940 if(fRng.endMass > rngCopy[ui].first-tolerance)
943 fRng.endMass = rngCopy[ui].second+tolerance;
946 auto off= std::distance(ranges.begin(),
947 std::find(ranges.begin(),ranges.end(),rngCopy[ui]));
948 fRng.containedRangeIDs.push_back(off);
949 fRng.containedIonIDs.push_back(ionIDs[off]);
954 ionMapping.push_back(fRng);
955 fRng.containedRangeIDs.clear();
956 fRng.containedIonIDs.clear();
958 fRng.startMass=rngCopy[ui].first;
959 fRng.endMass=rngCopy[ui].second;
962 auto off= std::distance(ranges.begin(),
963 std::find(ranges.begin(),ranges.end(),rngCopy[ui]));
964 fRng.containedRangeIDs.push_back(off);
965 fRng.containedIonIDs.push_back(ionIDs[off]);
970 ionMapping.push_back(fRng);
975 for(
auto v : ionMapping)
977 ASSERT(v.startMass < v.endMass);
985 ASSERT(srcRngID < src.ranges.size());
987 unsigned int srcIonID;
988 srcIonID=src.ionIDs[srcRngID];
991 auto it = std::find(ionNames.begin(),ionNames.end(),
992 src.ionNames[srcIonID]);
995 if(it !=ionNames.end())
997 size_t destID = std::distance(ionNames.begin(),it);
998 ionIDs.push_back(destID);
1004 ionIDs.push_back(ionNames.size());
1005 moleculeData.push_back(src.moleculeData[srcIonID]);
1006 ionNames.push_back(src.ionNames[srcIonID]);
1007 colours.push_back(src.colours[srcIonID]);
1010 ranges.push_back(src.ranges[srcRngID]);
1013 rangeGrouping.clear();
1018 set<SIMPLE_SPECIES> createElement(
const AbundanceData &abundance,
1019 const string &symbol,
unsigned int count)
1022 set<SIMPLE_SPECIES> s;
1023 auto elemIdx= abundance.
symbolIndex(symbol.c_str());
1041 vector<string> symbols = {
"Fe",
"Ni",
"Si",
"N"};
1042 enum{ FE=0,NI,SI,N,ATOM_END};
1044 vector<size_t> abundanceIdx,ionID;
1046 for(
auto ui=0;ui<ATOM_END;ui++)
1048 auto s=createElement(abundance,symbols[ui],1);
1049 ionID.push_back(m.
addIon(s,symbols[ui],ionCol));
1057 vector<unsigned int> rangeGrouping;
1058 for(
auto ui=0;ui<ATOM_END;ui++)
1060 vector<pair<float,float> > massDist;
1062 for(
auto &md : massDist)
1064 pair<float,float> rng;
1065 rng = std::make_pair(md.first-MASS_TOL,md.first+MASS_TOL);
1078 rangeGrouping.push_back(ui);
1088 vector<MultiRange> splitMulti;
1091 ASSERT(splitMulti.size() ==2);
1093 for(
auto ui=0;ui<splitMulti.size();ui++)
1099 vector<string> names;
1100 for(
auto uj=0;uj<splitMulti[ui].getNumIons();uj++)
1102 string name = splitMulti[ui].getIonName(uj);
1103 ASSERT(std::find(names.begin(),names.end(),name)
1105 names.push_back(name);
1110 if(std::find(names.begin(),names.end(),symbols[FE]) !=names.end())
1112 ASSERT(std::find(names.begin(),names.end(),symbols[NI]) != names.end())
1116 if(std::find(names.begin(),names.end(),symbols[SI]) !=names.end())
1118 ASSERT(std::find(names.begin(),names.end(),symbols[N]) != names.end())
1127 bool MultiRange::test()
1132 set<SIMPLE_SPECIES> molecule;
1145 auto feH2Id = rng.
addIon(molecule,
"FeH",col);
1149 rng.
write(
"test.rngx");
1156 TEST(rngB.
open(
"test.rngx"),
"Multirange load");
1159 TEST(rngB == rng,
"write/load check");
1165 set<SIMPLE_SPECIES> newMol;
1170 auto h2Id = rng.
addIon(newMol,
"H2",col);
1175 vector<unsigned int> rangeGroup= { 0,1 };
1180 vector<MultiRange> mrSplit;
1184 TEST(mrSplit.size() == 2,
"Split test");
unsigned int atomicNumber
Number of protons (element number)
std::set< SIMPLE_SPECIES > getMolecule(unsigned int ionID) const
Return the molecule that is associated with this ion.
size_t symbolIndex(const char *symbol, bool caseSensitive=true) const
Return the element's position in table, starting from 0.
std::string getName(unsigned int ionID, bool shortName=true) const
Get the short name or long name of a specified ionID.
RGBf getColour(unsigned int) const
Retrieve a given colour from the ion ID.
unsigned int getIonID(float mass) const
Get the ion's ID from a specified mass.
unsigned int XMLHelpFwdToElem(xmlNodePtr &node, const char *nodeName)
bool operator<(const SIMPLE_SPECIES &a, const SIMPLE_SPECIES &b)
bool isRanged(float mass) const
Returns true if a specified mass is ranged.
Data holder for colour as float.
bool isSelfConsistent() const
Check to see if data structure is internally consistent.
std::string tabs(unsigned int nTabs)
bool pairOverlaps(float aStart, float aEnd, float bStart, float bEnd)
std::vector< unsigned int > getRanges(float mass) const
Retrieve all of the ranges that specify the given mass.
void linkIdentifiers(vector< T > &link)
void setColour(unsigned int ionID, const RGBf &r)
Set the colour using the ion ID.
void fromHex(const std::string &s)
void generateSingleAtomDist(size_t atomIdx, unsigned int repeatCount, std::vector< std::pair< float, float > > &massDist) const
Obtain the mass distribution from a single isotope that repeats. Output is not grouped.
static bool decomposeIonNames(const std::string &name, std::vector< std::pair< std::string, size_t > > &fragments)
void XMLFreeDoc(void *data)
Free a xmlDoc pointer. For use in conjunction with std::unique_ptr for auto-deallocation.
unsigned int addRange(float start, float end, unsigned int ionID)
Add a range to the rangefile. Returns ID number of added range if adding successful, (unsigned int)-1 otherwise.
std::string getIonName(unsigned int ionID) const
Get the name of a specified ionID.
void getSymbolIndices(const std::vector< std::string > &symbols, std::vector< size_t > &indices) const
Return a vector of symbol indices.
unsigned int addIon(const std::set< SIMPLE_SPECIES > &molecule, const std::string &name, const RGBf &ionCol)
Add the ion to the database returns ion ID if successful, -1 otherwise.
unsigned int getNumRanges() const
Get the number of unique ranges.
Data storage and retrieval class for "ranging" a spectra, where overlapping ranges are permitted...
void clear()
Erase the contents of the rangefile.
unsigned int getNumRanges() const
Get the number of unique ranges.
void setRangeGroups(const std::vector< unsigned int > &groups)
Set the groupings for the ranges : this is used when splitting overlapping ranges.
void setIonID(unsigned int range, unsigned int newIonId)
Set the ion ID for a given range.
void splitOverlapping(std::vector< MultiRange > &decomposedRanges, float massTolerance=0) const
bool operator==(const SIMPLE_SPECIES &oth) const
float getMassToCharge() const
bool write(const char *fileName, unsigned int format=MULTIRANGE_FORMAT_XML) const
Save the structure to a file. format specifies output file format type.
bool operator==(const MultiRange &oth) const
Check for equality with other multirange.
unsigned int XMLHelpGetProp(T &prop, xmlNodePtr node, std::string propName)
Class to load abundance information for natural isotopes.
unsigned int getNumIons() const
Get the number of unique ions.
std::pair< float, float > getRange(unsigned int rangeID) const
Retrieve the start and end of a given range as a pair(start,end)
unsigned int getIonID(unsigned int rangeId) const
Get the ion's ID from a specified mass.
Structure that allows for the multirange data to be mapped into.
This is a data holding class for POS file ions, from.
RGBf getColour(unsigned int ionID) const
Obtain the colour for a given ion.
std::string getErrString() const
Retrieve the human-readable error associated with the current range file state.
bool parseColString(const std::string &str, unsigned char &r, unsigned char &g, unsigned char &b, unsigned char &a)
Parse a colour string containing rgb[a]; hex for with leading #.
Data storage and retrieval class for various range files.
unsigned int count
Number of copies of this isotope.
const ISOTOPE_ENTRY & isotope(size_t elementIdx, size_t isotopeIdx) const
Obtain a reference to a particular isotope, using the element's index in the table, and the isotope index.
void flattenToMassAxis(std::vector< FLATTENED_RANGE > &ionMapping, float tolerance=0) const
Obtain a projection onto the mass axis of ranges that do not touch one another.
bool stream_cast(T1 &result, const T2 &obj)
Template function to cast and object to another by the stringstream.
const char MULTIRANGE_FORMAT_VERSION[]
void copyDataFromRange(const MultiRange &src, unsigned int srcRngId)
Copy range from a different multirange into this one, including all dependant data.
unsigned int getNumIons() const
Get the number of unique ions.
void range(std::vector< IonHit > &ionHits) const
Clips out ions that are not inside the rangefile's ranges.
unsigned int getAtomicNumber(size_t elemIdx) const
Obtain the atomic number for the given element, by element index.
bool open(const char *fileName, unsigned int format=-1)
read the contents of the file into class
static std::string getVersionStr()
Obtain the version of the program as a string.
std::pair< float, float > getRange(unsigned int) const
Retrieve the start and end of a given range as a pair(start,end)