LCOV - code coverage report
Current view: top level - coupling/filtering - FilterPipeline.cpph (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 177 0.0 %
Date: 2025-06-25 11:26:37 Functions: 0 5 0.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             :                                std::to_string(CellIndex<dim, IndexTrait::local>::linearNumberCellsInDomain) + "(local MD domain size)" +
      62             :                                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           0 :     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             :                                  "Expected: "s +
      91             :                                  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 1.14