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 : }
|