LCOV - code coverage report
Current view: top level - coupling/filtering - FilterPipeline.cpph (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 180 0
Test Date: 2026-08-21 15:14:06 Functions: 0.0 % 5 0

            Line data    Source code
       1              : // This file is part of the Mamico project. For conditions of distribution
       2              : // and use, please see the copyright notice in Mamico's main folder, or at
       3              : // www5.in.tum.de/mamico
       4              : 
       5              : // TODO: replace exit()s with throws
       6              : 
       7              : // constructors of coupling::filtering::FilterPipeline
       8              : template <class CellIndex_T, unsigned int dim>
       9            0 : coupling::filtering::FilterPipeline<CellIndex_T, dim>::FilterPipeline(coupling::datastructures::CellContainer<CellIndex_T, dim> inputCells,
      10              :                                                                       coupling::filtering::Scope scope, const tarch::utils::MultiMDService<dim>& multiMDService,
      11              :                                                                       const char* cfgpath)
      12              :     :
      13              : 
      14            0 :       _config(ParseConfiguration::XMLConfiguration::load(cfgpath)), _scope(scope)
      15              : #if (COUPLING_MD_PARALLEL == COUPLING_MD_YES)
      16              :       ,
      17            0 :       _comm(multiMDService.getLocalCommunicator())
      18              : #endif
      19              : {
      20              :   using namespace coupling::indexing;
      21              :   using namespace std::string_literals;
      22              : 
      23              :   // check if provided file is written in proper XML
      24            0 :   if (_config.error != tinyxml2::XML_SUCCESS) {
      25            0 :     std::cout << "Filter Pipeline config path: " << cfgpath << std::endl;
      26            0 :     throw std::runtime_error("Could not read config for Filter-Pipeline: XML syntax error.");
      27              :   }
      28              : 
      29              :   // check for structural errors in config file
      30            0 :   if (!configIsValid(_config))
      31            0 :     exit(EXIT_FAILURE);
      32              : 
      33              :   // differentiate between per-instance and post-multi-instance filtering
      34            0 :   I01 idx;
      35              :   coupling::datastructures::CouplingCell<3>* cell;
      36            0 :   switch (_scope) {
      37            0 :   case coupling::filtering::Scope::perInstance:
      38              :     // check for valid amount of input cells
      39              :     // case 1: the input vector contains the entire MD domain
      40            0 :     if (inputCells.size() == CellIndex<dim, IndexTrait::local>::linearNumberCellsInDomain) {
      41              :       using Md2MacroIndex = CellIndex<dim, IndexTrait::local, IndexTrait::md2macro, IndexTrait::noGhost>;
      42            0 :       for (auto pair : inputCells) {
      43            0 :         std::tie(cell, idx) = pair;
      44            0 :         if (Md2MacroIndex::contains(idx))
      45            0 :           _md2MacroCells.emplace_back(cell);
      46              :         else
      47            0 :           _outerCells.emplace_back(cell);
      48              :       }
      49              :     }
      50              :     // case 2: the input vector contains only the MD-To-Macro domain
      51            0 :     else if (inputCells.size() == CellIndex<dim, IndexTrait::local, IndexTrait::md2macro, IndexTrait::noGhost>::linearNumberCellsInDomain) {
      52            0 :       for (auto pair : inputCells) {
      53            0 :         std::tie(cell, idx) = pair;
      54            0 :         _md2MacroCells.emplace_back(cell);
      55              :       }
      56            0 :       _outerCells = {};
      57              :     }
      58              :     // case 3: the input vector contains a nonexpected amount of cells
      59              :     else {
      60            0 :       throw std::runtime_error("Per-Instance-Filtering: Unexpected cell input size: "s + std::to_string(inputCells.size()) + "\n" + "Expected either:\n"s +
      61            0 :                                std::to_string(CellIndex<dim, IndexTrait::local>::linearNumberCellsInDomain) + "(local MD domain size)" +
      62            0 :                                std::to_string(CellIndex<dim, IndexTrait::local, IndexTrait::md2macro, IndexTrait::noGhost>::linearNumberCellsInDomain) +
      63              :                                "(local MD-To-Macro domain size)");
      64              :     }
      65              : 
      66              :     // load sequences
      67            0 :     loadSequencesFromXML(_config.root->FirstChildElement("filter-pipeline")->FirstChildElement("per-instance"));
      68              :     break;
      69            0 :   case coupling::filtering::Scope::postMultiInstance:
      70              :     // check for valid amount of input cells
      71              :     // case 1: the input vector contains the entire global MD-To-Macro domain
      72              :     if (inputCells.size() == CellIndex<dim, IndexTrait::md2macro, IndexTrait::noGhost>::linearNumberCellsInDomain) {
      73            0 :       for (auto pair : inputCells) {
      74            0 :         std::tie(cell, idx) = pair;
      75            0 :         _md2MacroCells.emplace_back(cell);
      76              :       }
      77            0 :       _outerCells = {};
      78            0 :       loadSequencesFromXML(_config.root->FirstChildElement("filter-pipeline")->FirstChildElement("post-multi-instance"));
      79              :     }
      80              :     // case 2: the input vector is empty
      81            0 :     else if (inputCells.size() == 0) {
      82            0 :       _md2MacroCells = {};
      83            0 :       _outerCells = {};
      84              :     }
      85              :     // case 3: the input vector contains a nonexpected amount of cells
      86              :     else {
      87              :       // check if the post-multi-instance filter-pipeline actually contains XML sequences, otherwise ignore
      88            0 :       if (_config.root->FirstChildElement("filter-pipeline")->FirstChildElement("post-multi-instance")->FirstChildElement()) {
      89            0 :         throw std::runtime_error("postMultiInstance FilterPipeline has received an unexpected number of cells: "s + std::to_string(inputCells.size()) + "\n" +
      90            0 :                                  "Expected: "s +
      91            0 :                                  std::to_string(CellIndex<dim, IndexTrait::local, IndexTrait::md2macro, IndexTrait::noGhost>::linearNumberCellsInDomain) +
      92              :                                  " (global MD-To-Macro domain size) \n This probably means that you are trying to use post-multi-instance filtering together "
      93              :                                  "with a non-trivial parallelization of the macroscopic solver.\n This combination of features is currently not supported. You "
      94              :                                  "can either:\n => use per-instance filtering instead, or\n => use a sequential macroscopic solver, or\n => choose to "
      95              :                                  "parallelize the macroscopic solver so that it does not split the md2Macro-subdomain.");
      96              :       }
      97              :     }
      98              :     break;
      99              :   }
     100            0 : }
     101              : 
     102              : // member functions of coupling::filtering::FilterPipeline
     103            0 : template <class CellIndex_T, unsigned int dim> double coupling::filtering::FilterPipeline<CellIndex_T, dim>::operator()() {
     104              :   timeval start;
     105              :   timeval end;
     106            0 :   double runtime = 0;
     107            0 :   if (IDXS.getRank() == 0) {
     108            0 :     gettimeofday(&start, NULL);
     109              :   }
     110              : 
     111            0 :   for (coupling::filtering::FilterSequence<dim>* sequence : _sequences) {
     112              :     // finalize FS's filter vector
     113            0 :     if (sequence->isModifiable()) {
     114              : #ifdef DEBUG_FILTER_PIPELINE
     115              :       sequence->printFilters();
     116              : #endif
     117            0 :       sequence->makeUnmodifiable();
     118              :     }
     119              : 
     120              :     // update cell vector contents
     121            0 :     sequence->updateCellVectors();
     122              : 
     123              :     // apply all filters of sequence
     124            0 :     (*sequence)();
     125              : 
     126              : #ifdef DEBUG_FILTER_PIPELINE
     127              :     std::cout << "FP: Done applying sequence " << sequence->getName() << std::endl;
     128              : #endif
     129              : 
     130              :     // If !isOutputToMacro() for all sequences, "md" must be our output. This is
     131              :     // equivalent to a read-only filter pipeline.
     132            0 :     if (sequence->isOutputToMacro()) {
     133            0 :       auto sequenceOutputCells = sequence->getOutputCellVector();
     134            0 :       for (unsigned int i = 0; i < _md2MacroCells.size(); i++)
     135            0 :         *(_md2MacroCells[i]) = *(sequenceOutputCells[i]);
     136            0 :     }
     137              :   }
     138              : 
     139            0 :   if (IDXS.getRank() == 0) {
     140            0 :     gettimeofday(&end, NULL);
     141            0 :     runtime += (double)((end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec));
     142              :   }
     143            0 :   return runtime;
     144              : }
     145              : 
     146              : template <class CellIndex_T, unsigned int dim>
     147            0 : coupling::filtering::FilterSequence<dim>* coupling::filtering::FilterPipeline<CellIndex_T, dim>::getSequence(const char* sequenceIdentifier) const {
     148            0 :   for (auto sequence : _sequences) {
     149            0 :     if (std::strcmp(sequence->getName(), sequenceIdentifier) == 0) {
     150            0 :       return sequence;
     151              :     }
     152              :   }
     153            0 :   throw std::runtime_error(std::string("ERROR: Could not find Filter Sequence named ").append(sequenceIdentifier));
     154              : }
     155              : 
     156              : // Private functions of coupling::filtering::FilterPipeline:
     157              : template <class CellIndex_T, unsigned int dim>
     158            0 : bool coupling::filtering::FilterPipeline<CellIndex_T, dim>::configIsValid(ParseConfiguration::XMLConfiguration& xmlConfig) {
     159              :   // Check main node
     160            0 :   tinyxml2::XMLElement* node = xmlConfig.root->FirstChildElement("filter-pipeline");
     161            0 :   if (!node) {
     162            0 :     std::cout << "ERROR: Could not read config for Filter-Pipeline: missing "
     163              :                  "element <filter-pipeline>"
     164            0 :               << std::endl;
     165            0 :     return false;
     166              :   }
     167              : 
     168              :   // verfiy per-instance subtag
     169            0 :   tinyxml2::XMLElement* perInstanceTag = node->FirstChildElement("per-instance");
     170            0 :   if (!perInstanceTag) {
     171            0 :     std::cout << "ERROR: Could not read config for Filter-Pipeline: missing "
     172              :                  "element <per-instance>"
     173            0 :               << std::endl;
     174            0 :     return false;
     175              :   }
     176            0 :   if (!perInstanceTag->Attribute("output")) {
     177            0 :     std::cout << "ERROR: Could not read config for Filter-Pipeline: no output "
     178              :                  "specified for <per-instance>"
     179            0 :               << std::endl;
     180              :   }
     181              : 
     182              :   // verify multi-instance subtag
     183            0 :   tinyxml2::XMLElement* multiInstanceTag = node->FirstChildElement("post-multi-instance");
     184            0 :   if (!multiInstanceTag) {
     185            0 :     std::cout << "ERROR: Could not read config for Filter-Pipeline: missing "
     186              :                  "element <post-multi-instance>"
     187            0 :               << std::endl;
     188            0 :     return false;
     189              :   }
     190            0 :   if (!multiInstanceTag->Attribute("output")) {
     191            0 :     std::cout << "ERROR: Could not read config for Filter-Pipeline: no output "
     192              :                  "specified for <post-multi-instance>"
     193            0 :               << std::endl;
     194            0 :     return false;
     195              :   }
     196              : 
     197              :   // No checks are done to determine if any more subtags of main node exist as
     198              :   // they do not interfer with implementation of this class. Syntax checking for
     199              :   // sequence-subnodes can be found in their respective initializer methods.
     200              : 
     201              : #ifdef DEBUG_FILTER_PIPELINE
     202              :   std::cout << "FP: Config is valid!" << std::endl;
     203              : #endif
     204              : 
     205              :   return true;
     206              : }
     207              : 
     208            0 : template <class CellIndex_T, unsigned int dim> void coupling::filtering::FilterPipeline<CellIndex_T, dim>::loadSequencesFromXML(tinyxml2::XMLElement* node) {
     209              : 
     210            0 :   if (_md2MacroCells.empty()) {
     211            0 :     switch (_scope) { /*TODO: print rank as well */
     212              : 
     213            0 :     case coupling::filtering::Scope::perInstance:
     214            0 :       std::cout << "Warning: Empty md2Macro-Domain in per-instance Filter Pipeline." << std::endl;
     215              :     // TODO: only output the following in cases where this is unexpected
     216            0 :     case coupling::filtering::Scope::postMultiInstance: /*std::cout << "Warning: Empty md2Macro-Domain in
     217              :                                                            post-multi-instance Filter Pipeline." <<
     218              :                                                            std::endl*/
     219              :         ;
     220              :     }
     221            0 :     return;
     222              :   }
     223              : 
     224            0 :   tinyxml2::XMLElement* currSequenceNode = node->FirstChildElement();
     225              : 
     226              :   // true once specified output sequence has been found
     227            0 :   bool outputDefined = false;
     228              : 
     229              :   // Special case: MD values (i.e. FP input) is also used as output.
     230            0 :   if (std::strcmp(node->Attribute("output"), "md") == 0)
     231            0 :     outputDefined = true;
     232              : 
     233              :   // Check if all outputs are specified before being used by sequences
     234            0 :   while (currSequenceNode) {
     235              :     // NAME
     236              :     // Check for illegal sequence names
     237              :     //"md" is reserved
     238            0 :     if (std::strcmp(currSequenceNode->Value(), "md") == 0)
     239            0 :       throw std::runtime_error("ERROR: Filter-Pipeline: Sequence name may not be 'md'.");
     240              : 
     241              :     //"outer" is reserved as well
     242            0 :     if (std::strcmp(currSequenceNode->Value(), "outer") == 0)
     243            0 :       throw std::runtime_error("ERROR: Filter-Pipeline: Sequence name may not be 'outer'.");
     244              : 
     245              :     // Duplicate names are not allowed
     246            0 :     bool duplicate = true;
     247              :     try {
     248            0 :       getSequence(currSequenceNode->Value());
     249            0 :     } catch (...) {
     250            0 :       duplicate = false;
     251              :     }
     252              :     if (duplicate)
     253            0 :       throw std::runtime_error(std::string("ERROR: Filter-Pipeline: Two or "
     254              :                                            "more sequences of identical name ")
     255            0 :                                    .append(currSequenceNode->Value()));
     256              : 
     257              :     // INPUT
     258            0 :     std::stringstream inputStringStream;
     259            0 :     if (currSequenceNode->Attribute("input"))
     260            0 :       inputStringStream = std::stringstream(currSequenceNode->Attribute("input"));
     261              :     else
     262            0 :       inputStringStream = std::stringstream("");
     263              : 
     264              :     // Split into input names, seperated by " ".
     265            0 :     std::vector<std::string> inputNames;
     266            0 :     std::string namebuf;
     267            0 :     while (inputStringStream >> namebuf) {
     268            0 :       inputNames.push_back(namebuf);
     269              :     }
     270              : 
     271              :     // Default input if none is specified
     272            0 :     if (inputNames.empty()) {
     273            0 :       inputNames.push_back("md");
     274              : #ifdef DEBUG_FILTER_PIPELINE
     275              :       std::cout << "FP: Choosing default (md) input for sequence " << currSequenceNode->Value() << std::endl;
     276              : #endif
     277              :     }
     278              : 
     279              :     // For regular sequences, this holds just one element.
     280            0 :     std::vector<FilterSequence<dim>*> inputSeqs;
     281              : 
     282              :     // Holds all input cells for this sequence
     283            0 :     std::vector<coupling::datastructures::CouplingCell<dim>*> inputCellVector;
     284              : 
     285              :     // Iterate over all inputs specified in input string.
     286              :     // Note: If there are non-matching names in input String, they are ignored.
     287              : 
     288            0 :     bool asymmetrical = false; // true if we need to build an asymmetrical junction
     289            0 :     unsigned int inputCount = 0;
     290              : 
     291            0 :     for (auto inputName : inputNames) {
     292              :       // Special case: "md" is the default input and is not associated to any
     293              :       // sequence
     294            0 :       if (inputName == "md") {
     295            0 :         for (auto cell : _md2MacroCells)
     296            0 :           inputCellVector.push_back(cell);
     297            0 :         inputCount++;
     298              :       }
     299              : 
     300              :       // Special case: "outer" refers to all cells lying outside the md2macro
     301              :       // domain. only used by asymmetrical junctions
     302            0 :       else if (inputName == "outer") {
     303            0 :         asymmetrical = true;
     304            0 :         inputCount++;
     305              :       }
     306              : 
     307              :       // Default case: search for sequence
     308              :       else
     309              :         try {
     310            0 :           inputSeqs.push_back(getSequence(inputName.c_str())); // this may throw: if inputName is not recognized.
     311            0 :           inputCount++;                                        // if above didnt throw, we can safely increase the
     312              :                                                                // input count by one
     313            0 :         } catch (...) {
     314            0 :           std::cout << "Warning: Input identifier " << inputName << " for sequence " << currSequenceNode->Value() << " not recognized." << std::endl;
     315              :         }
     316              :     }
     317              : 
     318              : #ifdef DEBUG_FILTER_PIPELINE
     319              :     std::cout << "FP: Creating new sequence or junction: Number of inputs found: " << inputCount << std::endl;
     320              : #endif
     321              : 
     322            0 :     if (inputCount == 0)
     323            0 :       throw std::runtime_error(std::string("ERROR: Filter-Pipeline: Invalid input ")
     324            0 :                                    .append(inputStringStream.str())
     325            0 :                                    .append(" specified for sequence named ")
     326            0 :                                    .append(currSequenceNode->Value()));
     327              : 
     328              :     //"filtered-values" is not mandatory, default is false
     329            0 :     std::array<bool, 7> filteredValues = {false};
     330            0 :     const char* fvStr = currSequenceNode->Attribute("filtered-values");
     331            0 :     if (fvStr) {
     332            0 :       if (strstr(fvStr, "all"))
     333            0 :         for (unsigned int i = 0; i < 7; i++)
     334            0 :           filteredValues[i] = true;
     335              :       else {
     336            0 :         if (strstr(fvStr, "micro-mass"))
     337            0 :           filteredValues[0] = true;
     338            0 :         if (strstr(fvStr, "micro-momentum"))
     339            0 :           filteredValues[1] = true;
     340            0 :         if (strstr(fvStr, "macro-mass"))
     341            0 :           filteredValues[2] = true;
     342            0 :         if (strstr(fvStr, "macro-momentum"))
     343            0 :           filteredValues[3] = true;
     344            0 :         if (strstr(fvStr, "potential-energy"))
     345            0 :           filteredValues[4] = true;
     346            0 :         if (strstr(fvStr, "velocity"))
     347            0 :           filteredValues[5] = true;
     348            0 :         if (strstr(fvStr, "temperature"))
     349            0 :           filteredValues[6] = true;
     350              :       }
     351              :     }
     352              : 
     353              :     /*
     354              :      *  Concat output vectors of the new sequence's input(s) (singular for
     355              :      *non-Junction sequences) NOTE: If MD is (possibly among others) chosen as
     356              :      *input, it will always occupy the first cells of inputCellVector, i.e. be
     357              :      *in cell partition one in the context of FilterJunctions.
     358              :      */
     359              :     // TODO: If a junction has multiple partitions, this only gets the first
     360              :     // output partition.
     361            0 :     for (auto seq : inputSeqs)
     362            0 :       for (auto cell : seq->getOutputCellVector())
     363            0 :         inputCellVector.push_back(cell);
     364              :         // Can this be done more efficiently? I dont think so. std::move is not
     365              :         // an option, std::insert does the same...
     366              : 
     367              : #ifdef DEBUG_FILTER_PIPELINE
     368              :     std::cout << "FP: Creating new sequence or junction: Size of inputCellVector: " << inputCellVector.size() << std::endl;
     369              : #endif
     370              : 
     371              :     // CREATE SEQUENCE/JUNCTION
     372            0 :     if (inputCount == 1) {
     373            0 :       _sequences.push_back(new coupling::filtering::FilterSequence<dim>(currSequenceNode->Value(), inputCellVector,
     374              : #if (COUPLING_MD_PARALLEL == COUPLING_MD_YES)
     375              :                                                                         _comm,
     376              : #endif
     377              :                                                                         filteredValues));
     378              : 
     379              :       // register newborn sequence as child at its input sequence,
     380              :       // unless this parent sequence is md (in which case inputSeqs is empty)
     381            0 :       if (!inputSeqs.empty())
     382            0 :         inputSeqs[0]->addChildSequence(_sequences.back());
     383              : 
     384              : #ifdef DEBUG_FILTER_PIPELINE
     385              :       if (inputSeqs.size() == 1)
     386              :         std::cout << "FP: " << _sequences.back()->getName() << " will use " << inputSeqs[0]->getName() << " as input." << std::endl;
     387              :       else
     388              :         std::cout << "FP: " << _sequences.back()->getName() << " will use MD data as input." << std::endl;
     389              : #endif
     390            0 :     } else if (asymmetrical) {
     391            0 :       if (inputCount != 2)
     392            0 :         throw std::runtime_error("ERROR: Currently, asymmetrical junctions are "
     393              :                                  "only allowed for exactly two inputs!");
     394              :       // TODO: currently, the only support asymmetrical junction is one that
     395              :       // uses outer cells as secondary input
     396            0 :       _sequences.push_back(new coupling::filtering::AsymmetricalFilterJunction<dim>(currSequenceNode->Value(), inputCellVector, _outerCells,
     397              : #if (COUPLING_MD_PARALLEL == COUPLING_MD_YES)
     398              :                                                                                     _comm,
     399              : #endif
     400              :                                                                                     filteredValues));
     401              : #ifdef DEBUG_FILTER_PIPELINE
     402              :       std::cout << "FP: " << _sequences.back()->getName() << " will use " << currSequenceNode->Attribute("input") << " as input." << std::endl;
     403              : #endif
     404              : 
     405              :       // TODO handling of parent sequences once this becomes relevant for
     406              :       // junctions
     407              :     } else { //(symmetrical) Junction = Sequence with 2 or more inputs
     408              :       // TODO: move template inputc: currently has 2 hardcoded
     409            0 :       _sequences.push_back(new coupling::filtering::FilterJunction<dim, 2>(currSequenceNode->Value(), inputCellVector,
     410              : #if (COUPLING_MD_PARALLEL == COUPLING_MD_YES)
     411              :                                                                            _comm,
     412              : #endif
     413              :                                                                            filteredValues));
     414              : #ifdef DEBUG_FILTER_PIPELINE
     415              :       std::cout << "FP: " << _sequences.back()->getName() << " will use " << currSequenceNode->Attribute("input") << " as input." << std::endl;
     416              : #endif
     417              : 
     418              :       // TODO handling of parent sequences once this becomes relevant for
     419              :       // junctions
     420              :     }
     421              : 
     422              :     // Add sequence to temp vector
     423              :     // Check if it's the output sequence.
     424            0 :     if (std::strcmp(_sequences.back()->getName(), node->Attribute("output")) == 0) {
     425            0 :       outputDefined = true;
     426            0 :       _sequences.back()->setAsOutputToMacro();
     427              :     }
     428              : 
     429              :     // Load filters of the newly created sequence
     430            0 :     if (_sequences.back()->loadFiltersFromXML(currSequenceNode))
     431            0 :       exit(EXIT_FAILURE); /*TODO: make loadFilters throw exception, catch it
     432              :                              here*/
     433              :     ;
     434              : 
     435              :     // Proceed to iterate over next element
     436            0 :     currSequenceNode = currSequenceNode->NextSiblingElement();
     437              :   }
     438              : 
     439            0 :   if (!outputDefined) {
     440            0 :     std::cout << "ERROR: Output sequence not specified or unknown." << std::endl;
     441            0 :     exit(EXIT_FAILURE);
     442              :   }
     443              : }
        

Generated by: LCOV version 2.0-1