#pragma once #include "stdafx.h" #include "OTSClassifyEng.h" #include "OTSHelper.h" namespace OTSClassifyEngine { using namespace OTSClassifyEngine; COTSClassifyEng::COTSClassifyEng() // constructor { } COTSClassifyEng::~COTSClassifyEng() // detractor { } //Dispose ClassifyXray BOOL COTSClassifyEng::ClassifyXray(CInclutionSTDDataPtr a_pPartSTDData, STEEL_TECHNOLOGY steelTech, CElementChemistriesList& a_listElementChemistries, int& a_nIncId, int& a_GrpId) { // safety check ASSERT(a_pPartSTDData); if (!a_pPartSTDData) { /*SendLogMessageToNlog("begin to classify particles", (int)PostLogLevel::warn);*/ LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: invalid CPartSTDDataPtr.")); return FALSE; } // the element chemistries list is an inclusion CElementChemistriesList listElChemsInc; CElementChemistriesList listElChemsIncNoFe; double dMolarSum = 0.0f; double dMolarSumNoFe = 0.0f; OTS_PARTICLE_TYPE incId; NOT_INCLUTION_ID notIncId; if (!IsAnValidIncXRay(a_listElementChemistries, listElChemsInc, listElChemsIncNoFe, dMolarSum, dMolarSumNoFe, incId, notIncId)) { LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::failed to call IsAnValidIncXRay")); return FALSE; } if (incId == OTS_PARTCLE_TYPE::ISNOT_INCLUTION)// this is not an inclution particle,but we can identify. { a_nIncId =(int) notIncId; a_GrpId = (int)OTS_PARTCLE_TYPE::ISNOT_INCLUTION; return TRUE; } if (incId == OTS_PARTCLE_TYPE::INVALID)// this is not an valid inclution particle. { a_nIncId = (int)OTS_PARTCLE_TYPE::INVALID; a_GrpId = (int)OTS_PARTCLE_TYPE::INVALID; return TRUE; } // system STD classification int nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; if (!SystemClassify(a_pPartSTDData, steelTech, listElChemsIncNoFe, dMolarSumNoFe, nIncId)) { LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: failed to call SystemClassify method.")); a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return FALSE; } // identified? if (nIncId > (int)OTS_PARTICLE_TYPE::IDENTIFIED) {// identified, return TRUE a_nIncId = nIncId; IDENTIFIED_INC_GRP_ID grpid; GroupClassify(listElChemsIncNoFe, grpid); a_GrpId = (int)grpid; return TRUE; } // user STD classification if (!UserClassify(a_pPartSTDData, listElChemsInc, dMolarSum, nIncId)) { LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: failed to call UserClassify method.")); a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return FALSE; } // identified? if (nIncId > (int)OTS_PARTICLE_TYPE::IDENTIFIED) { // identified, return TRUE a_nIncId = nIncId; IDENTIFIED_INC_GRP_ID grpid; GroupClassify(listElChemsIncNoFe, grpid); a_GrpId = (int)grpid; return TRUE; } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; IDENTIFIED_INC_GRP_ID grpid; GroupClassify(listElChemsIncNoFe, grpid); a_GrpId = (int)grpid; return TRUE; } // public // check if the x-ray is an inc x-ray, BOOL COTSClassifyEng::IsAnValidIncXRay( CElementChemistriesList& a_listElementChemistries, CElementChemistriesList& a_listElChemsInc, CElementChemistriesList& a_listElChemsIncNoFe, double& a_dMolarSum, double& a_dMolarSumNoFe, OTS_PARTICLE_TYPE& a_nIncId, NOT_INCLUTION_ID& notIncId) { // go through all elementS a_listElChemsInc.clear(); a_listElChemsIncNoFe.clear(); a_dMolarSum = 0; a_dMolarSumNoFe = 0; double dSumKeyElements = 0; double dSumSubElements = 0; double dCarbonMolar = 0; double dOMolar = 0; double dSiMolar = 0; double dFeMolar = 0; for (auto pElChem : a_listElementChemistries) { // create a new element chemistry CElementChemistryPtr pElChemNew = CElementChemistryPtr(new CElementChemistry(*pElChem.get())); // key element? S,O,N if (IsKeyElement(pElChem)) { // this is a key element // get molar percentage of this element double dMolarPercentage = pElChem->GetMolarPercentage(); // cal molar percentage sum (both lists) a_dMolarSum += dMolarPercentage; a_dMolarSumNoFe += dMolarPercentage; // cal key element molar percentage sum dSumKeyElements += dMolarPercentage; // add the element into the two lists a_listElChemsInc.push_back(pElChemNew); a_listElChemsIncNoFe.push_back(pElChemNew); if (pElChem->GetName().CompareNoCase(STR_O) == 0) { dOMolar = pElChem->GetMolarPercentage(); } else if (pElChem->GetName().CompareNoCase(STR_C) == 0) { dCarbonMolar = pElChem->GetMolarPercentage(); } } // sub element?, include Fe else if (IsSubElement(pElChem)) { // this is a sub element // get molar percentage of this element double dMolarPercentage = pElChem->GetMolarPercentage(); // cal molar percentage sum list a_dMolarSum += dMolarPercentage; // cal key element molar percentage sum dSumSubElements += dMolarPercentage; // add the element into the list a_listElChemsInc.push_back(pElChemNew); // cal molar percentage sum list (no Fe) a_dMolarSumNoFe += pElChem->GetMolarPercentage(); // add the element into the list (no Fe) a_listElChemsIncNoFe.push_back(pElChemNew); // Si if (pElChem->GetName().CompareNoCase(STR_SI) == 0) { dSiMolar = pElChem->GetMolarPercentage(); } } else if (pElChem->GetName().CompareNoCase(STR_FE) == 0) { dFeMolar = pElChem->GetMolarPercentage(); } } // not a inc if this is a SiC //========================================= // any carbon? if (dCarbonMolar > MIN_DOUBLE_VALUE) { // calculate molar % of C + Si double dMolarC_Si = Cal100NorValue(dCarbonMolar + dSiMolar, a_dMolarSumNoFe + dCarbonMolar); if (dMolarC_Si > SIC_MOLAR_CUTOFF) { // this is a SiC, not a inclusion, return FALSE a_nIncId =OTS_PARTCLE_TYPE::ISNOT_INCLUTION; notIncId = NOT_INCLUTION_ID::SiC; return TRUE; } } //========================================= //FeO if (dOMolar > MIN_DOUBLE_VALUE) { // calculate molar % of Fe + O double dMolarFe_O = Cal100NorValue(dOMolar + dFeMolar, a_dMolarSum + dCarbonMolar); if (dMolarFe_O > FEO_MOLAR_CUTOFF) { if (a_listElementChemistries.size() == 2)//there is only Fe and O { // this is a FeO, not a inclusion, return FALSE a_nIncId = OTS_PARTCLE_TYPE::ISNOT_INCLUTION; notIncId = NOT_INCLUTION_ID::FeO; return TRUE; } } } // both key molar percentage sum and sub molar percentage sum have to be over certain values double dSumKeyElementsMolar = Cal100NorValue(dSumKeyElements, a_dMolarSumNoFe); double dSumSubElementsMolar = Cal100NorValue(dSumSubElements, a_dMolarSumNoFe); if (a_dMolarSumNoFe < MIN_ELEMENT_SUM) { a_nIncId = OTS_PARTICLE_TYPE::INVALID; return TRUE; } if (dSumKeyElementsMolar > INC_KEY_ELEMENT_TOTAL_100 && dSumSubElementsMolar > INC_SUB_ELEMENT_TOTAL_100) { a_nIncId = OTS_PARTCLE_TYPE::NOT_IDENTIFIED; return TRUE; } else { a_nIncId = OTS_PARTICLE_TYPE::INVALID; return TRUE; } } // system classification BOOL COTSClassifyEng::SystemClassify(CInclutionSTDDataPtr a_pPartSTDData, STEEL_TECHNOLOGY steelTech, CElementChemistriesList& a_listElChemsIncNoFe, double a_dMolarSumNoFe, int& a_nIncId) { // safety check ASSERT(a_pPartSTDData); // try sulfide classification int nIncId = (int)OTS_PARTICLE_TYPE::INVALID; if (!SulClassify(a_pPartSTDData, steelTech, a_listElChemsIncNoFe, a_dMolarSumNoFe, nIncId)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: failed to call SulClassify method.")); a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return FALSE; } // inclusion identified? if (nIncId != (int)OTS_PARTICLE_TYPE::INVALID) { // yes, this is a sulfide a_nIncId = nIncId; return TRUE; } // nitride classification if (!NitrideClassify(a_pPartSTDData, a_listElChemsIncNoFe, a_dMolarSumNoFe, nIncId)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: failed to call NitrideClassify method.")); a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return FALSE; } // inclusion identified? if (nIncId != (int)OTS_PARTICLE_TYPE::INVALID) { // yes, this is a nitride a_nIncId = nIncId; return TRUE; } // oxide classification if (!OxideClassify(a_pPartSTDData, a_listElChemsIncNoFe, a_dMolarSumNoFe, nIncId)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: failed to call OxideClassify method.")); a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return FALSE; } // inclusion identified? if (nIncId != (int)OTS_PARTICLE_TYPE::INVALID) { // yes, this is a oxide a_nIncId = nIncId; return TRUE; } // CarbonClassify classification if (!CarbonClassify(a_pPartSTDData, a_listElChemsIncNoFe, a_dMolarSumNoFe, nIncId)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ClassifyXray: failed to call CarbonClassify method.")); a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return FALSE; } // inclusion identified? if (nIncId != (int)OTS_PARTICLE_TYPE::INVALID) { // yes, this is a oxide a_nIncId = nIncId; return TRUE; } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } BOOL COTSClassifyEng::GroupClassify(CElementChemistriesList& a_listElChemsIncNoFe, IDENTIFIED_INC_GRP_ID& a_GrpId) { double dOMolar = 0; double dSMolar = 0; double dNMolar = 0; for (auto pElChem : a_listElChemsIncNoFe) { // create a new element chemistry if (pElChem->GetName().CompareNoCase(STR_O) == 0) { dOMolar = pElChem->GetMolarPercentage(); } else if (pElChem->GetName().CompareNoCase(STR_SUL) == 0) { dSMolar = pElChem->GetMolarPercentage(); } else if (pElChem->GetName().CompareNoCase(STR_N) == 0) { dNMolar = pElChem->GetMolarPercentage(); } } if (dOMolar > MIN_ELEMENT_SUM && dSMolar< MIN_ELEMENT_SUM && dNMolar< MIN_ELEMENT_SUM) { a_GrpId = IDENTIFIED_INC_GRP_ID::OXIDE; } else if (dOMolar < MIN_ELEMENT_SUM && dSMolar > MIN_ELEMENT_SUM && dNMolar < MIN_ELEMENT_SUM) { a_GrpId = IDENTIFIED_INC_GRP_ID::SULFIDE; } else if (dOMolar > MIN_ELEMENT_SUM && dSMolar > MIN_ELEMENT_SUM && dNMolar < MIN_ELEMENT_SUM) { a_GrpId = IDENTIFIED_INC_GRP_ID::SULFIDE_OXIDE; } else if ( dNMolar > MIN_ELEMENT_SUM) { a_GrpId = IDENTIFIED_INC_GRP_ID::CARBONNITRIDE_NITRIDE; } else { a_GrpId = IDENTIFIED_INC_GRP_ID::Others; } return TRUE; } BOOL COTSClassifyEng::GetGroupNameAndColorById(int grpId,std::string& grpName,std::string& grpColor) { if (grpId == (int)OTS_PARTICLE_TYPE::INVALID) { grpName = "Invalid"; grpColor = "#000000"; } if (grpId == (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED) { grpName = "Not Identified"; grpColor = "#000000"; } if (grpId == (int)OTS_PARTICLE_TYPE::ISNOT_INCLUTION) { grpName = "NOT_INCLUTION"; grpColor = "#483D8B"; } switch (grpId) { case (int)IDENTIFIED_INC_GRP_ID::OXIDE: grpName = "Oxide"; grpColor = "#FF69B4"; break; case (int)IDENTIFIED_INC_GRP_ID::SULFIDE: grpName = "Sulfide"; grpColor = "#FF00FF"; break; case (int)IDENTIFIED_INC_GRP_ID::SULFIDE_OXIDE: grpName = "Sulfide_Oxide"; grpColor = "#0000FF"; break; case (int)IDENTIFIED_INC_GRP_ID::CARBONNITRIDE_NITRIDE: grpName = "CarbonNitride/Nitride"; grpColor = "#00FF7F"; break; case (int)IDENTIFIED_INC_GRP_ID::Others: grpName = "Other"; grpColor = "#B0C4DE"; break; default: break; } return true; } // sulfides classification BOOL COTSClassifyEng::SulClassify(CInclutionSTDDataPtr a_pPartSTDData, STEEL_TECHNOLOGY steelTech, CElementChemistriesList& a_listElChemsIncNoFe, double a_dMolarSumNoFe, int& a_nIncId) { // safety check ASSERT(a_pPartSTDData); // get all sulfides STD items CSTDItemsList listSulfideSTD; if (!GetClassifySTDItem(a_pPartSTDData, INC_CLASSIFY_TYPE::SUL, listSulfideSTD)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call GetClassifySTDItem method.")); return FALSE; } // check if element chemistries list contain any sulfur CElementChemistryPtr pSulElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, STR_SUL); if (!pSulElChem) { // contains no sulfur, this is not a sulfide return TRUE; } // calculate sulfur 100 percentage molar double dSulMolar100 = Cal100NorValue(pSulElChem->GetMolarPercentage(), a_dMolarSumNoFe); // check if sulfur amount enough if (dSulMolar100 < MIN_SUL_MOLAR) { // no enough sulfur, this is not a sulfide return TRUE; } // this is a sulfide // any sulfides STD items if (listSulfideSTD.empty()) { // no sulfides std items. can't identify sulfide // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // mapping Mn first BOOL bMnMapped = FALSE; { CElementChemistryPtr pElChemMn = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[0]); double dMappingRatio = INC_SULFILSES_MAPPING_RATIO[0]; if (pElChemMn) { // there Mn in the element list, map S & Mn if (!ElementsMapping(a_dMolarSumNoFe, dMappingRatio, pElChemMn, pSulElChem, bMnMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call ElementsMapping method.")); return FALSE; } } } // need to re-calculate sulfur 100% molar value if Mn mapped if(bMnMapped) { dSulMolar100 = Cal100NorValue(pSulElChem->GetMolarPercentage(), a_dMolarSumNoFe); } // process mapping if sulfur amount enough CElementChemistriesList a_listChemistriesToAnalysis; CString strProMappingSulName = _T(""); BOOL bProMapped = FALSE; if (dSulMolar100 > MIN_SUL_MOLAR) { // still have enough sulfur, mapping Ca, Mg or Ce, La double dMappingRadio; switch ( steelTech) { // Ca process case STEEL_TECHNOLOGY::CaProcessMode: { //remove the Mg element first CElementChemistryPtr pElChemMg = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[2]); CElementChemistriesList a_listChemistriesToAnalysis; if (pElChemMg) { for (auto el : a_listElChemsIncNoFe) { if (!el->GetName().CompareNoCase(INC_SUL_SUB_ELEMENT_NAMES[2])) { a_listChemistriesToAnalysis.push_back(el); } } } else { a_listChemistriesToAnalysis = a_listElChemsIncNoFe; } // try to get Ca element chemistry CElementChemistryPtr pElChemCa = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[1]); if (pElChemCa) { // there Ca in the element list, map Ca & S dMappingRadio = INC_SULFILSES_MAPPING_RATIO[1]; if (!ElementsMapping(a_dMolarSumNoFe, dMappingRadio, pElChemCa, pSulElChem, bProMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call ElementsMapping method.")); return FALSE; } // mapping successful? if (bProMapped) { strProMappingSulName = INC_SULFILSES_NAMES[1]; } } } break; // Mg process case STEEL_TECHNOLOGY::MgProcessMode: { //remove the Ca element first CElementChemistryPtr pElChemCa = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[1]); if (pElChemCa) { for (auto el : a_listElChemsIncNoFe) { if (!el->GetName().CompareNoCase(INC_SUL_SUB_ELEMENT_NAMES[1])) { a_listChemistriesToAnalysis.push_back(el); } } } else { a_listChemistriesToAnalysis = a_listElChemsIncNoFe; } // try to get Mg element chemistry CElementChemistryPtr pElChemMg = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[2]); if (pElChemMg) { // there Mg in the element list, map Mg & S dMappingRadio = INC_SULFILSES_MAPPING_RATIO[2]; if (!ElementsMapping(a_dMolarSumNoFe, dMappingRadio, pElChemMg, pSulElChem, bProMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call ElementsMapping method.")); return FALSE; } // mapping successful? if (bProMapped) { strProMappingSulName = INC_SULFILSES_NAMES[2]; } } } break; // real earth elements process case STEEL_TECHNOLOGY::RareEarthMode: { // try to get Ce element chemistry CElementChemistryPtr pElChemCe = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[3]); BOOL bCeMapped = FALSE; if (pElChemCe) { // there Ce in the element list, map S with Ce dMappingRadio = INC_SULFILSES_MAPPING_RATIO[3]; if (!ElementsMapping(a_dMolarSumNoFe, dMappingRadio, pElChemCe, pSulElChem, bCeMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call ElementsMapping method.")); return FALSE; } } // try to get La element chemistry CElementChemistryPtr pElChemLa = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_SUL_SUB_ELEMENT_NAMES[4]); BOOL bLaMapped = FALSE; if (pElChemLa) { // there La in the element list, map S with La dMappingRadio = INC_SULFILSES_MAPPING_RATIO[4]; if (!ElementsMapping(a_dMolarSumNoFe, dMappingRadio, pElChemLa, pSulElChem, bLaMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call ElementsMapping method.")); return FALSE; } } // mapped both if (bCeMapped && bLaMapped) { strProMappingSulName = INC_SULFILSES_NAMES[3] + STR_CONNECT + INC_SULFILSES_NAMES[4]; bProMapped = TRUE; } else if (bCeMapped) { strProMappingSulName = INC_SULFILSES_NAMES[3]; bProMapped = TRUE; } else if (bLaMapped) { strProMappingSulName = INC_SULFILSES_NAMES[4]; bProMapped = TRUE; } } break; } } // set sulfide base name CString strSulfideBaseName = _T(""); if (bMnMapped && bProMapped) { // both Mn and process mapped strSulfideBaseName = INC_SULFILSES_NAMES[0] + strProMappingSulName; } else if (bMnMapped) { // Mn mapped only strSulfideBaseName = INC_SULFILSES_NAMES[0]; } else if (bProMapped) { // process mapped only strSulfideBaseName = strProMappingSulName; } else { // mapped nothing // force sulfide base name as "Sulfide" if sulfur 100% molar value over cutting off value if (dSulMolar100 > SULFIDE_MOLAR_CUTOFF) { strSulfideBaseName = SULFIDE_STR; } else { // no enough sulfur, consider that it is not a sulfide return TRUE; } } // check if the rest element chemistries map an oxide int nIncId = (int)OTS_PARTICLE_TYPE::INVALID; if (!OxideClassify(a_pPartSTDData, a_listChemistriesToAnalysis, a_dMolarSumNoFe, nIncId)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::SulClassify: failed to call OxideClassify method.")); return FALSE; } // mapped? if (nIncId >= (int)OTS_PARTICLE_TYPE::IDENTIFIED) { // this is an oxide sulfide // confirm the oxide sulfide id // get the STD item of the mapped oxide CSTDItemPtr pOxideSTDItem = a_pPartSTDData->GetSTDItemById(nIncId); if (pOxideSTDItem) { // get mapped oxide name CString strOxideName = pOxideSTDItem->GetName(); // oxide sulfide name: oxide + "-" + sulfide base string CString strOxide_SulName = strOxideName + STR_CONNECT + strSulfideBaseName; // try to find the STD item CSTDItemPtr pOxideSulSTDItem = GetSTDItemByName(listSulfideSTD, strOxide_SulName); if (pOxideSulSTDItem) { // found the STD item a_nIncId = pOxideSulSTDItem->GetSTDId(); return TRUE; } // rename the oxide sulfide name as "Oxide" + "-" + sulfide base string strOxide_SulName = OXIDE_STR + STR_CONNECT + strSulfideBaseName; // try to find the STD item pOxideSulSTDItem = GetSTDItemByName(listSulfideSTD, strOxide_SulName); if (pOxideSulSTDItem) { // found the STD item a_nIncId = pOxideSulSTDItem->GetSTDId(); return TRUE; } // rename the oxide sulfide name as strOxideName + "-" + "Sulfide" strOxide_SulName = strOxideName + STR_CONNECT + SULFIDE_STR; // try to find the STD item pOxideSulSTDItem = GetSTDItemByName(listSulfideSTD, strOxide_SulName); if (pOxideSulSTDItem) { // found the STD item a_nIncId = pOxideSulSTDItem->GetSTDId(); return TRUE; } // rename the oxide sulfide name as "Oxide" + "-" + "Sulfide" strOxide_SulName = OXIDE_STR + STR_CONNECT + SULFIDE_STR; // try to find the STD item pOxideSulSTDItem = GetSTDItemByName(listSulfideSTD, strOxide_SulName); if (pOxideSulSTDItem) { // found the STD item a_nIncId = pOxideSulSTDItem->GetSTDId(); return TRUE; } // rename the oxide sulfide name as "Sulfide" strOxide_SulName = SULFIDE_STR; // try to find the STD item pOxideSulSTDItem = GetSTDItemByName(listSulfideSTD, strOxide_SulName); if (pOxideSulSTDItem) { // found the STD item a_nIncId = pOxideSulSTDItem->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // this is a general sulfide // confirm the sulfide id // try to find the STD item CSTDItemPtr pSulSTDItem = GetSTDItemByName(listSulfideSTD, strSulfideBaseName); if (pSulSTDItem) { // found the STD item a_nIncId = pSulSTDItem->GetSTDId(); return TRUE; } // rename the sulfides name as "Sulfide" if it is not if (strSulfideBaseName.CompareNoCase(SULFIDE_STR) != 0) { strSulfideBaseName = SULFIDE_STR; pSulSTDItem = GetSTDItemByName(listSulfideSTD, strSulfideBaseName); if (pSulSTDItem) { // found the STD item a_nIncId = pSulSTDItem->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // oxides classification BOOL COTSClassifyEng::OxideClassify(CInclutionSTDDataPtr a_pPartSTDData, CElementChemistriesList& a_listElChemsIncNoFe, double a_dMolarSumNoFe, int& a_nIncId) { // safety check ASSERT(a_pPartSTDData); // get all oxides STD items CSTDItemsList listOxideSTD; if (!GetClassifySTDItem(a_pPartSTDData, INC_CLASSIFY_TYPE::OXIDE, listOxideSTD)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::OxideClassify: failed to call GetClassifySTDItem method.")); return FALSE; } // check if element chemistries list contain any oxygen CElementChemistryPtr pOElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, STR_OXIDE); if (!pOElChem) { // contains no oxygen, this is not an oxide return TRUE; } // check if oxygen amount enough double dOMolar100 = Cal100NorValue(pOElChem->GetMolarPercentage(), a_dMolarSumNoFe); if (dOMolar100 < MIN_OXIDE_MOLAR) { // no enough oxygen, this is not an oxide return TRUE; } // this is an oxide // any oxide STD items if (listOxideSTD.empty()) { // no oxide STD items, can't identify oxide // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // build oxide sub elements list // ========================================= // get all possible oxide sub element chemistries CElementChemistriesList listTempOxideSubElChems; double dTempOxideSubElMolarSum = 0; for (int i = 0; i < INC_OXIDE_SUB_ELEMENT_MAX; ++i) { // try to get the oxide sub element CElementChemistryPtr pSubElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_OXIDE_SUB_ELEMENT_NAMES[i]); // found it? if (pSubElChem) { // got one // get the %molar value of the sub element chemistries double dSubElMolar = pSubElChem->GetMolarPercentage(); // add the sub element chemistries into the oxides element chemistries list listTempOxideSubElChems.push_back(pSubElChem); dTempOxideSubElMolarSum += dSubElMolar; } } // clear oxide sub element chemistries list CElementChemistriesList listOxideSubElChems; double dOxideSubElMolarSum = 0; for (auto pElChem : listTempOxideSubElChems) { // get the %molar value of the sub element chemistries double dSubElMolar = pElChem->GetMolarPercentage(); // the %molar value of the sub element chemistries of the sub element chemistries list double dSubElMolarMolar100 = Cal100NorValue(dSubElMolar, dTempOxideSubElMolarSum); // remove the sub element chemistries less than the cut off (2%) if (dSubElMolarMolar100 > MIN_OXIDE_SUB_MOLAR_CUTOFF) { // keep it listOxideSubElChems.push_back(pElChem); dOxideSubElMolarSum += dSubElMolar; } } // ========================================= // check oxide elements list CString strOxideName = _T(""); CString aluminateStr=_T(""); if (listOxideSubElChems.empty()) { // no oxide sub elements // consider this is not a oxide (may be it just is a dust) return TRUE; } // is a simple oxide? else if(IsASimpleOxide(listOxideSubElChems, dOxideSubElMolarSum, strOxideName)) { // this is a simple oxide // named already during checking } // is it a REOxide (La-Ce-Oxide)? else if (IsAREOxide(listOxideSubElChems, dOxideSubElMolarSum)) { // REOxcide strOxideName = REOXIDE_STR; } // should be a complex oxide // is it a REAlOxide? else if (IsAnREAlOxide(listOxideSubElChems, dOxideSubElMolarSum)) { // REOxcide strOxideName = REALOXIDE_STR; } // is it a Spinel? else if (IsASpinel(listOxideSubElChems, dOxideSubElMolarSum)) { // Spinel strOxideName = SPINEL_STR; } // is it a Silicate? else if (IsASilicate(listOxideSubElChems, dOxideSubElMolarSum)) { // Silicate strOxideName = SILICATE_STR; } // is it an Aluminate? else if (IsAnCa_Aluminate(listOxideSubElChems, dOxideSubElMolarSum, aluminateStr)) { // Aluminate strOxideName = aluminateStr; } // fit none of them, simply name it as an oxide else { strOxideName = OXIDE_STR; } // confirm the oxide id // try to find the STD auto pSTDItem = GetSTDItemByName(listOxideSTD, strOxideName); if (pSTDItem) { // found the STD item a_nIncId = pSTDItem->GetSTDId(); return TRUE; } // rename the oxide as "Oxide" if it is not if (strOxideName.CompareNoCase(OXIDE_STR) != 0) { strOxideName = OXIDE_STR; auto pSTDItem = GetSTDItemByName(listOxideSTD, strOxideName); if (pSTDItem) { // found the STD item a_nIncId = pSTDItem->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // nitrides classification BOOL COTSClassifyEng::NitrideClassify(CInclutionSTDDataPtr a_pPartSTDData, CElementChemistriesList& a_listElChemsIncNoFe, double a_dMolarSumNoFe, int& a_nIncId) { // safety check ASSERT(a_pPartSTDData); // get all nitride STD items CSTDItemsList listNitrideSTD; if (!GetClassifySTDItem(a_pPartSTDData, INC_CLASSIFY_TYPE::NITR, listNitrideSTD)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::NitrideClassify: failed to call GetClassifySTDItem method.")); return FALSE; } // check if element chemistries list contain any nitrogen CElementChemistryPtr pNitrElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, STR_NITR); if (!pNitrElChem) { // contains no nitrogen, this is not a nitride return TRUE; } // check if nitrogen amount enough double dNitrMolar100 = Cal100NorValue(pNitrElChem->GetMolarPercentage(), a_dMolarSumNoFe); if (dNitrMolar100 < MIN_NITR_MOLAR) { // have no enough nitrogen, this is not a nitride return TRUE; } // this is a nitride // any nitride STD items if (listNitrideSTD.empty()) { // no nitrides std items. // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // mapping nitride sub elements CString strNitrideName = _T(""); BOOL bMapped = FALSE; for (int i = 0; i < INC_NITR_SUB_ELEMENT_MAX; ++i) { CElementChemistryPtr pNitrSubElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_NITR_SUB_ELEMENT_NAMES[i]); if (pNitrSubElChem) { // this is a nitride sub element chemistry // get %molar value of this sub element chemistry double dNitr_Sub_Molar = Cal100NorValue(pNitrSubElChem->GetMolarPercentage(), a_dMolarSumNoFe); // make sure the sub element molar value is over mapping min value if (dNitr_Sub_Molar > MIN_NITR_SUB_MOLAR) { // mapping this sub element chemistry double dMappingRadio = INC_NITR_MAPPING_RATIO[i]; if (!ElementsMapping(a_dMolarSumNoFe, dMappingRadio, pNitrSubElChem, pNitrElChem, bMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::NitrideClassify: failed to call ElementsMapping method.")); return FALSE; } // mapping succeed? if(bMapped) { // is mapped Ti? if (i == 0) { // the first mapped nitride is "TiN" // try to map Nb BOOL bNbMapped = FALSE; // get molar % of the rest nitrogen dNitrMolar100 = Cal100NorValue(pNitrElChem->GetMolarPercentage(), a_dMolarSumNoFe); // make sure nitrogen amount is enough if (dNitrMolar100 > MIN_NITR_MOLAR) { // get element "Nb" CElementChemistryPtr pElChemNb = GetNamedElementChemistry(a_listElChemsIncNoFe, STR_Nb); // is there Nb in the list if (pElChemNb) { // get %molar value of Nb double dNb_Molar = Cal100NorValue(pElChemNb->GetMolarPercentage(), a_dMolarSumNoFe); // make sure Nb molar value is over mapping min value double dNbMappingRadio = INC_NITR_MAPPING_RATIO[2]; if (dNb_Molar > MIN_NITR_SUB_MOLAR) { if (!ElementsMapping(a_dMolarSumNoFe, dNbMappingRadio, pElChemNb, pNitrElChem, bNbMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::NitrideClassify: failed to call ElementsMapping method.")); return FALSE; } } } } // mapped "Nb" if (bNbMapped) { // nitride name is "TiN-NbN" strNitrideName = INC_NITR_NAMES[0] + STR_CONNECT + INC_NITR_NAMES[2]; } else { // nitride name is "TiN" strNitrideName = INC_NITR_NAMES[0]; } } else { // get the nitride name strNitrideName = INC_NITR_NAMES[i]; } // completed mapping, get out the loop break; } } } } // not mapped? if (!bMapped) { // force to name it as "Nitride" if N 100% molar value over cutting off value if (dNitrMolar100 > NITRIDE_MOLAR_CUTOFF) { strNitrideName = NITRIDE_STR; } else { // no enough nitride, consider that it is not a nitride return TRUE; } } // check if the rest element chemistries map an oxide int nIncId = (int)OTS_PARTICLE_TYPE::INVALID; if (!OxideClassify(a_pPartSTDData, a_listElChemsIncNoFe, a_dMolarSumNoFe, nIncId)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::NitrideClassify: failed to call OxideClassify method.")); return FALSE; } // mapped? if (nIncId >= (int)OTS_PARTICLE_TYPE::IDENTIFIED) { // this is an oxide nitride // confirm the oxide nitride id // get the STD item of the mapped oxide CSTDItemPtr pOxideSTDItem = a_pPartSTDData->GetSTDItemById(nIncId); if (pOxideSTDItem) { // get mapped oxide name CString strOxideName = pOxideSTDItem->GetName(); // oxide nitride name: oxide + "-" + nitride base string CString strOxide_NitrName = strOxideName + STR_CONNECT + strNitrideName; // try to find the STD item CSTDItemPtr pOxideNitrSTDItem = GetSTDItemByName(listNitrideSTD, strOxide_NitrName); if (pOxideNitrSTDItem) { // found the STD item a_nIncId = pOxideNitrSTDItem->GetSTDId(); return TRUE; } // can't find the STD item // rename the oxide nitride name as "Oxide" + "-" + nitride base string strOxide_NitrName = OXIDE_STR + STR_CONNECT + strOxide_NitrName; // try to find the STD item pOxideNitrSTDItem = GetSTDItemByName(listNitrideSTD, strOxide_NitrName); if (pOxideNitrSTDItem) { // found the STD item a_nIncId = pOxideNitrSTDItem->GetSTDId(); return TRUE; } // rename the oxide nitride name as strOxideName + "-" + "Nitride" strOxide_NitrName = strOxideName + STR_CONNECT + NITRIDE_STR; // try to find the STD item pOxideNitrSTDItem = GetSTDItemByName(listNitrideSTD, strOxide_NitrName); if (strOxide_NitrName) { // found the STD item a_nIncId = pOxideNitrSTDItem->GetSTDId(); return TRUE; } // rename the oxide nitride name as "Oxide" + "-" + "Nitride" strOxide_NitrName = OXIDE_STR + STR_CONNECT + NITRIDE_STR; // try to find the STD item pOxideNitrSTDItem = GetSTDItemByName(listNitrideSTD, strOxide_NitrName); if (pOxideNitrSTDItem) { // found the STD item a_nIncId = pOxideNitrSTDItem->GetSTDId(); return TRUE; } // rename the oxide nitride name as "Nitride" strOxide_NitrName = NITRIDE_STR; // try to find the STD item pOxideNitrSTDItem = GetSTDItemByName(listNitrideSTD, strOxide_NitrName); if (pOxideNitrSTDItem) { // found the STD item a_nIncId = pOxideSTDItem->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // confirm the nitride id // try to find the STD auto pSTDItem = GetSTDItemByName(listNitrideSTD, strNitrideName); if (pSTDItem) { // found the STD item a_nIncId = pSTDItem->GetSTDId(); return TRUE; } // rename the nitride as "Nitride" if it is not if (strNitrideName.CompareNoCase(NITRIDE_STR) != 0) { strNitrideName = NITRIDE_STR; auto pSTDItem = GetSTDItemByName(listNitrideSTD, strNitrideName); if (pSTDItem) { // found the STD item a_nIncId = pSTDItem->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // nitrides classification BOOL COTSClassifyEng::CarbonClassify(CInclutionSTDDataPtr a_pPartSTDData, CElementChemistriesList& a_listElChemsIncNoFe, double a_dMolarSumNoFe, int& a_nIncId) { // safety check ASSERT(a_pPartSTDData); // get all nitride STD items CSTDItemsList listCarbonSTD; if (!GetClassifySTDItem(a_pPartSTDData, INC_CLASSIFY_TYPE::CARBON, listCarbonSTD)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("CClassifyEng::NitrideClassify: failed to call GetClassifySTDItem method.")); return FALSE; } // check if element chemistries list contain any nitrogen CElementChemistryPtr pCarElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, STR_CAR); if (!pCarElChem) { // contains no nitrogen, this is not a nitride return TRUE; } // check if nitrogen amount enough double dCarMolar100 = Cal100NorValue(pCarElChem->GetMolarPercentage(), a_dMolarSumNoFe); if (dCarMolar100 < MIN_CAR_MOLAR) { // have no enough nitrogen, this is not a nitride return TRUE; } // this is a carbon // any carbon STD items if (listCarbonSTD.empty()) { // no nitrides std items. // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // mapping carbon sub elements CString strCarbonName = _T(""); BOOL bMapped = FALSE; for (int i = 0; i < INC_CAR_SUB_ELEMENT_MAX; ++i) { CElementChemistryPtr pCarSubElChem = GetNamedElementChemistry(a_listElChemsIncNoFe, INC_CAR_SUB_ELEMENT_NAMES[i]); if (pCarSubElChem) { // this is a nitride sub element chemistry // get %molar value of this sub element chemistry double dCar_Sub_Molar = Cal100NorValue(pCarSubElChem->GetMolarPercentage(), a_dMolarSumNoFe); // make sure the sub element molar value is over mapping min value if (dCar_Sub_Molar > MIN_CAR_SUB_MOLAR) { // try to map Nb BOOL bNbMapped = FALSE; // get molar % of the rest nitrogen dCarMolar100 = Cal100NorValue(pCarElChem->GetMolarPercentage(), a_dMolarSumNoFe); // make sure nitrogen amount is enough if (dCarMolar100 > MIN_CAR_MOLAR) { // get element "Nb" CElementChemistryPtr pElChemNb = GetNamedElementChemistry(a_listElChemsIncNoFe, STR_Nb); // is there Nb in the list if (pElChemNb) { // get %molar value of Nb double dNb_Molar = Cal100NorValue(pElChemNb->GetMolarPercentage(), a_dMolarSumNoFe); // make sure Nb molar value is over mapping min value double dNbMappingRatio = INC_NITR_MAPPING_RATIO[2]; if (dNb_Molar > MIN_CAR_SUB_MOLAR) { if (!ElementsMapping(a_dMolarSumNoFe, dNbMappingRatio, pElChemNb, pCarElChem, bNbMapped)) { // something is wrong LogErrorTrace(__FILE__, __LINE__, _T("CClassifyEng::NitrideClassify: failed to call ElementsMapping method.")); return FALSE; } } } } // mapped "Nb" if (bNbMapped) { // carbon name is "NbC" strCarbonName = INC_CAR_NAMES[0]; } // completed mapping, get out the loop break; } } } // mapped? if (a_nIncId >= (int)OTS_PARTICLE_TYPE::IDENTIFIED) { // this is an oxide nitride // confirm the oxide nitride id CSTDItemPtr pCarSTDItem = GetSTDItemByName(listCarbonSTD, strCarbonName); // get the STD item of the mapped oxide if (pCarSTDItem) { // found the STD item a_nIncId = pCarSTDItem->GetSTDId(); return TRUE; } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // confirm the carben id // try to find the STD auto pSTDItem = GetSTDItemByName(listCarbonSTD, strCarbonName); if (pSTDItem) { // found the STD item a_nIncId = pSTDItem->GetSTDId(); return TRUE; } // rename the Carbon as "Carbon" if it is not if (strCarbonName.CompareNoCase(CARBON_STR) != 0) { strCarbonName = NITRIDE_STR; auto pSTDItem = GetSTDItemByName(listCarbonSTD, strCarbonName); if (pSTDItem) { // found the STD item a_nIncId = pSTDItem->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // user STD items classification BOOL COTSClassifyEng::UserClassify(CInclutionSTDDataPtr a_pPartSTDData, CElementChemistriesList& a_listEltChemsInc, double dSum, int& a_nIncId) { // safety check ASSERT(a_pPartSTDData); // get user STD items CSTDItemsList listUserSTD; if (!GetClassifySTDItem(a_pPartSTDData, INC_CLASSIFY_TYPE::USER, listUserSTD)) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::OxideClassify: failed to call GetClassifySTDItem method.")); return FALSE; } // try to identify the oxide for (auto pUserSTD : listUserSTD) { // matching STD? if (MatchingSTD(a_listEltChemsInc, pUserSTD, dSum)) { // found matching STD a_nIncId = pUserSTD->GetSTDId(); return TRUE; } } // can't identify this inclusion a_nIncId = (int)OTS_PARTICLE_TYPE::NOT_IDENTIFIED; return TRUE; } // nominate element chemistries list BOOL COTSClassifyEng::NomiNateElChemsList( CElementChemistriesList& a_listElChemsInc, CElementChemistriesList& a_listNomiElChemsInc) { // return FALSE if nothing in the input list if (a_listElChemsInc.empty()) { LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::NomiNateElChemsList: invalid inclusion x-ray data.")); return FALSE; } // initialize outputs a_listNomiElChemsInc.clear(); // go thought all element chemistry objects of the input lit double dWeightPerSum = 0; for (auto pElChem : a_listElChemsInc) { // create a new element chemistry CElementChemistryPtr pElChemNew = CElementChemistryPtr(new CElementChemistry(*pElChem.get())); // key element? if (IsKeyElement(pElChem)) { // this is a key element // cal weight percentage sum dWeightPerSum += pElChem->GetPercentage(); // add the element into the output list a_listNomiElChemsInc.push_back(pElChemNew); } // sub element? else if (IsSubElement(pElChem)) { // this is a sub element // Fe? if (pElChem->GetName().CompareNoCase("Fe") != 0) { // cal weight percentage sum dWeightPerSum += pElChem->GetPercentage(); // add the element into the output list a_listNomiElChemsInc.push_back(pElChemNew); } } } // return FALSE if nothing in the input list or sum less than cut off value if (a_listNomiElChemsInc.empty() || dWeightPerSum < INC_SUB_ELEMENT_CUT_OFF) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::NomiNateElChemsList: invalid inclusion x-ray data.")); return FALSE; } // go thought all element chemistry objects of the output lit for (auto pElChem : a_listNomiElChemsInc) { // reset weight % value // get weight % value double dWeightPer = pElChem->GetPercentage(); // calculate new weight % value double dWeightPerNew = Cal100NorValue(dWeightPer, dWeightPerSum); // reset pElChem->SetPercentage(dWeightPerNew); } // ok, return TRUE return TRUE; } // protected // check if this is a key element BOOL COTSClassifyEng::IsKeyElement(CElementChemistryPtr a_pElChem) { // safety check ASSERT(a_pElChem); if (!a_pElChem) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::IsKeyElement: invalid CElementChemistryPtr.")); return FALSE; } // go thought all key element for (long i = 0; i < INC_KEY_ELEMENT_MAX; ++i) { // compare if ((a_pElChem->GetName().CompareNoCase(INC_KEY_ELEMENT_NAMES[i]) == 0) && (a_pElChem->GetPercentage() >= INC_KEY_ELEMENT_CUT_OFF)) { // this is a key element, return TRUE return TRUE; } } // this is not a key element, return FALSE return FALSE; } // check if this is a sub element BOOL COTSClassifyEng::IsSubElement(CElementChemistryPtr a_pElChem) { // safety check ASSERT(a_pElChem); if (!a_pElChem) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::IsSubElement: invalid CElementChemistryPtr.")); return FALSE; } // go thought all sub element for (long i = 0; i < INC_SUB_ELEMENT_MAX; ++i) { // compare if ((a_pElChem->GetName().CompareNoCase(INC_SUB_ELEMENT_NAMES[i]) == 0) && (a_pElChem->GetPercentage() >= INC_SUB_ELEMENT_CUT_OFF)) { // this is a key element, return TRUE return TRUE; } } // this is not a key element, return FALSE return FALSE; } // get named element chemistry CElementChemistryPtr COTSClassifyEng::GetNamedElementChemistry(CElementChemistriesList & a_listChemistriesElements, const CString a_strElementName) { CElementChemistryPtr pElChem = nullptr; CString strName = a_strElementName; auto itr = std::find_if(a_listChemistriesElements.begin(), a_listChemistriesElements.end(), [strName](CElementChemistryPtr& poElementChemistry) { return poElementChemistry->GetName().CompareNoCase(strName) == 0; }); if (itr != a_listChemistriesElements.end()) { pElChem = *itr; } return pElChem; } bool SortBySTDID(const std::shared_ptr< CSTDItem> &v1, const std::shared_ptr< CSTDItem> &v2) { return v1->GetSTDId() < v2->GetSTDId(); } // get classify STD items BOOL COTSClassifyEng::GetClassifySTDItem(CInclutionSTDDataPtr a_pPartSTDDataPtr, INC_CLASSIFY_TYPE a_nClassifyType, CSTDItemsList& a_listSTDItems) { // safety check ASSERT(a_pPartSTDDataPtr); if (!a_pPartSTDDataPtr) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::GetClassifySTDItem: invalid CPartSTDDataPtr.")); return FALSE; } // cal STD item id value range OTS_STD_ITEM_VALUE nSTDIdRangeMin = OTS_STD_ITEM_VALUE::INVALID; OTS_STD_ITEM_VALUE nSTDIdRangeMax = OTS_STD_ITEM_VALUE::INVALID; switch (a_nClassifyType) { case INC_CLASSIFY_TYPE::SIMPLE_OXIDE: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::SYS_SIMPLE_OXIDE_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::SYS_SIMPLE_OXIDE_MAX; } break; case INC_CLASSIFY_TYPE::COMPLEX_OXIDE: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::SYS_COMPLEX_OXIDE_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::SYS_COMPLEX_OXIDE_MAX; } break; case INC_CLASSIFY_TYPE::OXIDE: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::SYS_OXIDE_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::SYS_OXIDE_MAX; } break; case INC_CLASSIFY_TYPE::SUL: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::SYS_SUL_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::SYS_SUL_MAX; } break; case INC_CLASSIFY_TYPE::NITR: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::SYS_NITRIDE_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::SYS_NITRIDE_MAX; } break; case INC_CLASSIFY_TYPE::CARBON: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::SYS_CARBON_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::SYS_CARBON_MAX; } break; case INC_CLASSIFY_TYPE::USER: { nSTDIdRangeMin = OTS_STD_ITEM_VALUE::USER_MIN; nSTDIdRangeMax = OTS_STD_ITEM_VALUE::USER_MAX; } break; default: { // wrong classify type value, return FALSE return FALSE; } break; } // go through all STD items a_listSTDItems.clear(); for (auto pSTDItem : a_pPartSTDDataPtr->GetSTDItemsList()) { // is matching STD item? if (pSTDItem->GetSTDId() >= (int)nSTDIdRangeMin && pSTDItem->GetSTDId() <= (int)nSTDIdRangeMax) { // get matching STD item a_listSTDItems.push_back(pSTDItem); } } // sort std item by std id sort(a_listSTDItems.begin(), a_listSTDItems.end(), SortBySTDID); // ok, return TRUE return TRUE; } // get STD item by name CSTDItemPtr COTSClassifyEng::GetSTDItemByName(CSTDItemsList& a_listSTDItems, CString a_strName) { CSTDItemPtr pSTDItem = nullptr; auto itr = std::find_if(a_listSTDItems.begin(), a_listSTDItems.end(), [a_strName](CSTDItemPtr& pSTD) { return pSTD->GetName().CompareNoCase(a_strName) == 0; }); if (itr != a_listSTDItems.end()) { // found the STD item pSTDItem = *itr; } return pSTDItem; } // get STD item name by id CString COTSClassifyEng::GetSTDItemNameById(CSTDItemsList& a_listSTDItems, int a_nItemId) { CString strName = _T(""); CSTDItemPtr pSTDItem = nullptr; auto itr = std::find_if(a_listSTDItems.begin(), a_listSTDItems.end(), [a_nItemId](CSTDItemPtr& pSTD) { return pSTD->GetSTDId() == a_nItemId; }); if (itr != a_listSTDItems.end()) { // found the STD item pSTDItem = *itr; strName = pSTDItem->GetName(); } return strName; } // elements mapping BOOL COTSClassifyEng::ElementsMapping(double a_dMolarSumNoFe, double a_dMappingRatio, CElementChemistryPtr a_pFirstElChem, CElementChemistryPtr a_pSecondElChem, BOOL& a_bMapped) { // safety check ASSERT(a_pFirstElChem); if (!a_pFirstElChem) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ElementsMapping: invalid a_pFirstElChem.")); return FALSE; } ASSERT(a_pSecondElChem); if (!a_pSecondElChem) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ElementsMapping: invalid a_pSecondElChem.")); return FALSE; } if (a_dMolarSumNoFe < MIN_ELEMENT_SUM) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ElementsMapping: invalid molar sum value.")); return FALSE; } if (a_dMappingRatio < MIN_DOUBLE_VALUE) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::ElementsMapping: invalid mapping radio value.")); return FALSE; } // set mapped flag to FALSE as default a_bMapped = FALSE; // get first mapping element molar value double dFirstMolar = a_pFirstElChem->GetMolarPercentage(); // make sure molar value of the first mapping element chemistry is enough double dFirstMolar100 = Cal100NorValue(dFirstMolar, a_dMolarSumNoFe); if (dFirstMolar100 > ELEMENT_MAPPING_100MOLAR) { // get second mapping element chemistry molar value double dSecondElMolar = a_pSecondElChem->GetMolarPercentage(); // make sure second mapping element chemistry value is enough double dSecondElMolar100 = Cal100NorValue(dSecondElMolar, a_dMolarSumNoFe); if (dSecondElMolar100 > ELEMENT_MAPPING_100MOLAR) { // set mapped flag to true a_bMapped = TRUE; // reset mapping element chemistry molar values // is there any first element left? if (dFirstMolar - dSecondElMolar * a_dMappingRatio > 0) { // no more second element chemistry left; a_pSecondElChem->SetPercentage(0.0); // still there are some first element left // calculate left first element molar value dFirstMolar = dFirstMolar - (dSecondElMolar * a_dMappingRatio); dFirstMolar100 = Cal100NorValue(dFirstMolar100, a_dMolarSumNoFe); // is there enough first element left? if (dFirstMolar100 > ELEMENT_MAPPING_100MOLAR) { // still have enough first element left a_pFirstElChem->SetMolarPercentage(dFirstMolar); } else { // no enough enough first element left, set to 0.0 a_pFirstElChem->SetPercentage(0.0); } } else { // no more first element chemistry left a_pFirstElChem->SetPercentage(0.0); // still there are some second element left // calculate left second element molar value dSecondElMolar = dSecondElMolar - dFirstMolar / a_dMappingRatio; dSecondElMolar100 = Cal100NorValue(dSecondElMolar, a_dMolarSumNoFe); // is there enough second element left? if (dSecondElMolar100 > ELEMENT_MAPPING_100MOLAR) { // still have enough second element left a_pSecondElChem->SetMolarPercentage(dSecondElMolar); } else { // no enough enough second element left, set to 0.0 a_pSecondElChem->SetPercentage(0.0); } } } } // ok, return TRUE return TRUE; } // check if is a REOxide BOOL COTSClassifyEng::IsASimpleOxide(CElementChemistriesList& a_listElChems, double a_dMolarSum, CString& a_strSimOxName) { // go through oxide sub element chemistries for (auto pOxideSubElChems : a_listElChems) { // calculate the %molar value of the sub element chemistry in the list double dSubElMolar100 = Cal100NorValue(pOxideSubElChems->GetMolarPercentage(), a_dMolarSum); // over simple oxide cut_off if (dSubElMolar100 > SIMPLE_OXIDE_MOLAR_CUTOFF) { // this is a simple oxide, name it for (int i = 0; i < INC_OXIDE_SUB_ELEMENT_MAX; ++i) { if (pOxideSubElChems->GetName().CompareNoCase(INC_OXIDE_SUB_ELEMENT_NAMES[i]) == 0) { // found it // assign simple oxide name a_strSimOxName = INC_OXIDE_NAMES[i]; // return TRUE return TRUE; } } } } // not a simple oxide, return FALSE return FALSE; } // check if is a REOxide (deal with La-Ce-Oxide) BOOL COTSClassifyEng::IsAREOxide(CElementChemistriesList& a_listElChems, double a_dMolarSum) { // calculate real element molar % double dREElementMolarSum = 0; for (int i = 0; i < REOXIDE_KEY_ELEMENT_MAX; ++i) { CElementChemistryPtr pREElement = GetNamedElementChemistry(a_listElChems, REOXIDE_KEY_ELEMENT_NAMES[i]); if (pREElement) { // got a real element // calculate the %molar value of the real element chemistry in the list double dSubElMolar100 = Cal100NorValue(pREElement->GetMolarPercentage(), a_dMolarSum); // calculate real element molar % dREElementMolarSum += dSubElMolar100; } } // is real element molar % over REAlOxide elements molar % cut_off if (dREElementMolarSum > REALOXIDE_ELEMELTS_MOLAR_CUTOFF) { // this is a REAlOxide, return TRUE return TRUE; } // not a REOxide, return FALSE return FALSE; } // check if is a REAlOxide BOOL COTSClassifyEng::IsAnREAlOxide(CElementChemistriesList& a_listElChems, double a_dMolarSum) { // calculate real element molar % double dREElementMolarSum = 0; for (int i = 0; i < REOXIDE_KEY_ELEMENT_MAX; ++i) { CElementChemistryPtr pREElement = GetNamedElementChemistry(a_listElChems, REOXIDE_KEY_ELEMENT_NAMES[i]); if (pREElement) { // got a real element // calculate the %molar value of the real element chemistry in the list double dSubElMolar100 = Cal100NorValue(pREElement->GetMolarPercentage(), a_dMolarSum); // calculate real element molar % dREElementMolarSum += dSubElMolar100; } } // is real element molar % over REAlOxide element molar % min cut_off if (dREElementMolarSum < REALOXIDE_ELEMENT_MOLAR_LOW_CUTOFF) { // no, this is not a REALOxide, return FALSE return FALSE; } // calculate Al/Si elements molar % double dAl_Si_ElementMolarSum = 0; for (int i = 0; i < REALOXIDE_SUB_ELEMENT_MAX; ++i) { CElementChemistryPtr pAlSiElement = GetNamedElementChemistry(a_listElChems, REALOXIDE_SUB_ELEMENT_NAMES[i]); if (pAlSiElement) { // got a Al or Si element // calculate the %molar value of the Al and Si element chemistry in the list double dAl_SiElMolar100 = Cal100NorValue(pAlSiElement->GetMolarPercentage(), a_dMolarSum); // calculate Al/Si elements molar % dAl_Si_ElementMolarSum += dAl_SiElMolar100; } } // is Al/Si elements molar % over REAlOxide element molar % min cut_off if (dAl_Si_ElementMolarSum < REALOXIDE_ELEMENT_MOLAR_LOW_CUTOFF) { // no, this is not a REALOxide, return FALSE return FALSE; } // is real element molar % + Al/Si elements molar % over the cut off if (dREElementMolarSum + dAl_Si_ElementMolarSum > SIMPLE_OXIDE_MOLAR_CUTOFF) { // this is a REAlOxide, return TRUE return TRUE; } // not a REAlOxide, return FALSE return FALSE; } // check if is a Spinel BOOL COTSClassifyEng::IsASpinel(CElementChemistriesList& a_listElChems, double a_dMolarSum) { // we deal with Mg, Al Spinel only // get the first key element of Spinel CElementChemistryPtr pFirstElChem = GetNamedElementChemistry(a_listElChems, SPINEL_KEY_ELEMENT_NAMES[0]); if (!pFirstElChem) { // not a Spinel, return FALSE return FALSE; } // get the second key element of Spinel CElementChemistryPtr pSecondElChem = GetNamedElementChemistry(a_listElChems, SPINEL_KEY_ELEMENT_NAMES[1]); if (!pSecondElChem) { // not a Spinel, return FALSE return FALSE; } // check ratio between the two elements double dFirstElMolar = pFirstElChem->GetMolarPercentage(); double dSecondElMolar = pSecondElChem->GetMolarPercentage(); if (dFirstElMolar < MIN_DOUBLE_VALUE) { // something wrong // not a Spinel, return FALSE return FALSE; } double dRatio = dSecondElMolar / dFirstElMolar; if (dRatio < SPINEL_ELEMENT_RATIO_MIN || dRatio > SPINEL_ELEMENT_RATIO_MAX) { // not a Spinel, return FALSE return FALSE; } // molar % amount check double dFirstElMolar100 = Cal100NorValue(dFirstElMolar, a_dMolarSum); double dSecondElMolar100 = Cal100NorValue(dSecondElMolar, a_dMolarSum); if (dFirstElMolar100 + dSecondElMolar100 > SPINEL_KEY_ELEMENT_MOLAR_TOTAL) { // this is a Spinel return TRUE; } // not a Spinel, return FALSE return FALSE; } // check if is a Silicate BOOL COTSClassifyEng::IsASilicate(CElementChemistriesList& a_listElChems, double a_dMolarSum) { // get key element of Silicate CElementChemistryPtr pKeyElChem = GetNamedElementChemistry(a_listElChems, SILICATE_KEY_ELEMENT_NAME); if (!pKeyElChem) { // not a Silicate, return FALSE return FALSE; } // molar % amount check double dKeyElMolar100 = Cal100NorValue(pKeyElChem->GetMolarPercentage(), a_dMolarSum); if (dKeyElMolar100 > SILICATE_KEY_ELEMENT_MOLAR_TOTAL_MIN && dKeyElMolar100 < SILICATE_KEY_ELEMENT_MOLAR_TOTAL_MAX) { // this is a Silicate, return TRUE return TRUE; } // not a Silicate, return FALSE return FALSE; } // check if is a Aluminate BOOL COTSClassifyEng::IsAnCa_Aluminate(CElementChemistriesList& a_listElChems, double a_dMolarSum,CString& strName) { // get key element of Aluminate CElementChemistryPtr pKeyElChem = GetNamedElementChemistry(a_listElChems, ALUMINATE_KEY_ELEMENT_NAME[0]); if (!pKeyElChem) { // not an Aluminate, return FALSE return FALSE; } // molar % amount check double dKeyElMolar100 = Cal100NorValue(pKeyElChem->GetMolarPercentage(), a_dMolarSum); if (dKeyElMolar100 > ALUMINAT_KEY_ELEMENT_MOLAR_TOTAL_MIN && dKeyElMolar100 < ALUMINAT_KEY_ELEMENT_MOLAR_TOTAL_MAX) { CElementChemistryPtr pKeyElChem2 = GetNamedElementChemistry(a_listElChems, ALUMINATE_KEY_ELEMENT_NAME[1]); double dFirstElMolar = pKeyElChem->GetMolarPercentage();//Al double dSecondElMolar = pKeyElChem2->GetMolarPercentage();//Ca double dRatio = dFirstElMolar/dSecondElMolar ;// Al/Ca if (dRatio < 1.3 || dRatio > 0.9)//12CaO-7Al2O3 14/12 { // not a Spinel, return FALSE strName = ALUMINATE12CaO_7Al2O3_STR; return TRUE; } if (dRatio < 0.9 || dRatio > 0.4)//3CaO-Al2O3 2/3 { // not a Spinel, return FALSE strName = ALUMINATE3CaO_Al2O3_STR; return TRUE; } else { strName = ALUMINATE_STR; } // this is a Aluminate, return TRUE return TRUE; } // not a Aluminate, return FALSE return FALSE; } // check if the element chemistries list matching the STD BOOL COTSClassifyEng::MatchingSTD(CElementChemistriesList& a_listElChems, CSTDItemPtr a_pSTDItem, double a_dMolarSum) { // safety check ASSERT(a_pSTDItem); if (!a_pSTDItem) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::MatchingSTD: invalid CSTDItemPtr.")); return FALSE; } if (a_dMolarSum < MIN_ELEMENT_SUM) { // something wrong LogErrorTrace(__FILE__, __LINE__, _T("COTSClassifyEng::MatchingSTD: invalid molar sum value.")); return FALSE; } // find out how many elements need for the STD CElementRangeList& listElementRange = a_pSTDItem->GetElementRangeList(); int nElNoMin = 0; for (auto pElmentRange : listElementRange) { int nStart = pElmentRange->GetRange()->GetStart(); if (nStart > 0) { // this element has to have ++nElNoMin; } } int nElNoMax = (int)listElementRange.size(); // a_listChemistriesElements size has to be between nElNoMin and nElNoMax int nElementSize = (int)a_listElChems.size(); //if (nElementSize < nElNoMin || nElementSize > nElNoMax) if (nElementSize < nElNoMin) { // a_listChemistriesElements size not match return FALSE; } // all element chemistries have to be in listElementRange and in the range for (auto pElChems : a_listElChems) { CString strElement = pElChems->GetName(); auto itr = std::find_if(listElementRange.begin(), listElementRange.end(), [strElement](CElementRangePtr& poElemenRange) { return poElemenRange->GetElement()->GetName().CompareNoCase(strElement) == 0; }); if (itr == listElementRange.end()) { // not in the element range list, not match then return FALSE; } // molar value has to be in the range double dMolar = pElChems->GetMolarPercentage(); dMolar = Cal100NorValue(dMolar, a_dMolarSum); if (dMolar < (double)(*itr)->GetRange()->GetStart() || dMolar >(double)(*itr)->GetRange()->GetEnd()) { // molar value has to be in the range, not match then return FALSE; } } // the two are matching each other, return TRUE return TRUE; } // calculate 100% normalize value (molar) double COTSClassifyEng::Cal100NorValue(double a_dValue, double a_dSumValue) { double d100NorCalue = MIN_DOUBLE_VALUE; // sum has a cut off if (a_dSumValue < MIN_ELEMENT_SUM) { return d100NorCalue; } d100NorCalue = a_dValue / a_dSumValue * 100; return d100NorCalue; } }