PLaSK library

Loading...
Searching...
No Matches
manager.cpp
Go to the documentation of this file.
1/*
2 * This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3 * Copyright (c) 2022 Lodz University of Technology
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#include <boost/algorithm/string.hpp>
15#include <fstream>
16
17#include "manager.hpp"
18
19#include "filters/factory.hpp"
20#include "geometry/reader.hpp"
21#include "utils/stl.hpp"
22
23#include "utils/dynlib/manager.hpp"
24
25#include "utils/system.hpp"
26
27 namespace plask {
28
29 Manager::SetAxisNames::SetAxisNames(GeometryReader& reader) : SetAxisNames(reader.manager, reader.source) {}
30
31 void Manager::ExternalSourcesFromFile::operator()(Manager& manager, const std::string& url, const std::string& section) {
32 boost::filesystem::path url_path(url);
33 if (url_path.is_relative()) {
34 if (originalFileName.empty())
35 throw Exception("error while reading section \"{0}\": relative path name \"{1}\" is not supported.", section, url);
36 url_path = originalFileName;
37 url_path.remove_filename();
38 url_path /= url;
39 }
40 if (hasCircularRef(url_path, section))
41 throw Exception("error while reading section \"{0}\": circular reference was detected.", section);
42 XMLReader reader(url_path.string().c_str());
43 manager.loadSection(reader, section, ExternalSourcesFromFile(url_path, section, this));
44}
45
46 Manager::SetAxisNames::SetAxisNames(Manager& manager, const AxisNames* names) : manager(manager), old(manager.axisNames) {
47 manager.axisNames = names;
48}
49
50 Manager::SetAxisNames::SetAxisNames(Manager& manager, XMLReader& source) : manager(manager), old(manager.axisNames) {
51 plask::optional<std::string> axis = source.getAttribute(XML_AXES_ATTR);
52 if (axis) manager.axisNames = &AxisNames::axisNamesRegister.get(*axis);
53}
54
55 bool Manager::tryLoadFromExternal(XMLReader& reader, const Manager::LoadFunCallbackT& load_from) {
56 plask::optional<std::string> from_attr = reader.getAttribute("external");
57 if (!from_attr) return false;
58 load_from(*this, *from_attr, reader.getNodeName());
59 return true;
60
61 /*std::string section_to_load = reader.getNodeName();
62 std::pair< XMLReader, std::unique_ptr<LoadFunCallbackT> > new_loader = load_from.get(*from_attr);
63 load(new_loader.first, materialsDB, *new_loader.second, [&](const std::string& section_name) -> bool { return section_name ==
64 section_to_load; }); return true;*/
65}
66
67 PathHints* Manager::getPathHints(const std::string& path_hints_name) {
68 auto result_it = pathHints.find(path_hints_name);
69 if (result_it == pathHints.end()) return nullptr;
70 return &result_it->second;
71}
72
73 const PathHints* Manager::getPathHints(const std::string& path_hints_name) const {
74 auto result_it = pathHints.find(path_hints_name);
75 if (result_it == pathHints.end()) return nullptr;
76 return &result_it->second;
77}
78
79 PathHints& Manager::requirePathHints(const std::string& path_hints_name) {
80 auto result_it = pathHints.find(path_hints_name);
81 if (result_it == pathHints.end()) throw NoSuchPath(path_hints_name);
82 return result_it->second;
83}
84
85 const PathHints& Manager::requirePathHints(const std::string& path_hints_name) const {
86 auto result_it = pathHints.find(path_hints_name);
87 if (result_it == pathHints.end()) throw NoSuchPath(path_hints_name);
88 return result_it->second;
89}
90
91 shared_ptr<GeometryObject> Manager::getGeometryObject(const std::string& name) const {
92 auto result_it = geometrics.find(name);
93 if (result_it == geometrics.end()) return shared_ptr<GeometryObject>();
94 // auto result = result_it->second.lock();
95 // if (!result) const_cast<Manager*>(this)->geometrics.erase(name);
96 // return result;
97 return result_it->second;
98 /*auto result_it = geometrics.find(name);
99 return result_it != geometrics.end() ? result_it->second.lock() : shared_ptr<GeometryObject>();*/
100}
101
102 shared_ptr<GeometryObject> Manager::requireGeometryObject(const std::string& name) {
103 shared_ptr<GeometryObject> result = getGeometryObject(name);
104 if (!result) throwErrorIfNotDraft(NoSuchGeometryObject(name));
105 return result;
106}
107
108 shared_ptr<Geometry> Manager::getGeometry(const std::string& name) const {
109 auto result_it = geometrics.find(name);
110 return result_it == geometrics.end() ? shared_ptr<Geometry>() : dynamic_pointer_cast<Geometry>(result_it->second);
111}
112
113 shared_ptr<MeshBase> Manager::getMesh(const std::string& name) const {
114 auto result_it = meshes.find(name);
115 return result_it == meshes.end() ? shared_ptr<Mesh>() : result_it->second;
116}
117
118 void Manager::loadDefines(XMLReader& reader) {
119 writelog(LOG_ERROR, "Loading defines from C++ not implemented. Ignoring XML section <defines>.");
120 reader.gotoEndOfCurrentTag();
121}
122
123 void Manager::loadFromReader(XMLReader& reader, const LoadFunCallbackT& load_from_cb) { load(reader, load_from_cb); }
124
125 void Manager::loadFromStream(std::unique_ptr<std::istream>&& input, const LoadFunCallbackT& load_from_cb) {
126 XMLReader reader(std::move(input));
127 loadFromReader(reader, load_from_cb);
128}
129
130 void Manager::loadFromXMLString(const std::string& input_XML_str, const LoadFunCallbackT& load_from_cb) {
131 loadFromStream(std::unique_ptr<std::istream>(new std::istringstream(input_XML_str)), load_from_cb);
132}
133
134 void Manager::loadFromFile(const std::string& fileName) {
135 XMLReader reader(fileName.c_str());
136 loadFromReader(reader, ExternalSourcesFromFile(fileName));
137}
138
139 void Manager::loadFromFILE(FILE* file, const LoadFunCallbackT& load_from_cb) {
140 XMLReader reader(file);
141 loadFromReader(reader, load_from_cb);
142}
143
144 void Manager::loadGeometry(GeometryReader& greader) {
145 if (greader.source.getNodeType() != XMLReader::NODE_ELEMENT || greader.source.getNodeName() != std::string("geometry"))
146 throw XMLUnexpectedElementException(greader.source, "<geometry>");
147 SetAxisNames read_axis_tag(greader);
148 while (greader.source.requireTagOrEnd()) roots.push_back(greader.readGeometry());
149}
150
151 void Manager::loadMaterialLib(XMLReader& reader) {
152 std::string name = reader.requireAttribute("name");
153 try {
154 if (name != "") MaterialsDB::loadToDefault(name);
155 } catch (Exception& err) {
156 throwErrorIfNotDraft(XMLException(reader, err.what()));
157 }
158 reader.requireTagEnd();
159}
160
161 void Manager::loadMaterial(XMLReader& reader) {
162 writelog(LOG_ERROR, "Loading XML material from C++ not implemented (ignoring material {})",
163 reader.getAttribute<std::string>("name", "unknown"));
164 reader.gotoEndOfCurrentTag();
165}
166
167 void Manager::loadMaterials(XMLReader& reader) {
168 while (reader.requireTagOrEnd()) {
169 if (reader.getNodeName() == "material")
170 loadMaterial(reader);
171 else if (reader.getNodeName() == "library")
172 loadMaterialLib(reader);
173 else
174 throw XMLUnexpectedElementException(reader, "<material>");
175 }
176}
177
178 void Manager::loadGrids(XMLReader& reader) {
179 if (reader.getNodeType() != XMLReader::NODE_ELEMENT || reader.getNodeName() != std::string("grids"))
180 throw XMLUnexpectedElementException(reader, "<grids>");
181 while (reader.requireTagOrEnd()) {
182 if (reader.getNodeName() == "mesh") {
183 std::string type = reader.requireAttribute("type");
184 std::string name = reader.requireAttribute("name");
185 std::replace(name.begin(), name.end(), '-', '_');
186 BadId::throwIfBad("mesh", name);
187 if (meshes.find(name) != meshes.end()) throw NamesConflictException("mesh or mesh generator", name);
188 shared_ptr<Mesh> mesh = RegisterMeshReader::getReader(type)(reader);
189 if (reader.getNodeType() != XMLReader::NODE_ELEMENT_END || reader.getNodeName() != "mesh")
190 throw Exception("internal error in {0} mesh reader, after return reader does not point to end of mesh tag.", type);
191 meshes[name] = mesh;
192 } else if (reader.getNodeName() == "generator") {
193 std::string type = reader.requireAttribute("type");
194 std::string method = reader.requireAttribute("method");
195 std::string name = reader.requireAttribute("name");
196 std::replace(name.begin(), name.end(), '-', '_');
197 BadId::throwIfBad("generator", name);
198 std::string key = type + "." + method;
199 if (meshes.find(name) != meshes.end()) throw NamesConflictException("mesh or mesh generator", name);
200 shared_ptr<MeshGenerator> generator = RegisterMeshGeneratorReader::getReader(key)(reader, *this);
201 if (reader.getNodeType() != XMLReader::NODE_ELEMENT_END || reader.getNodeName() != "generator")
202 throw Exception(
203 "internal error in {0} (method: {1}) mesh generator reader, after return reader does not point to end of "
204 "generator tag.",
205 type, method);
206 meshes[name] = generator;
207 } else
208 throw XMLUnexpectedElementException(reader, "<mesh...>, <generator...>, or </grids>");
209 }
210}
211
212 shared_ptr<Solver> Manager::loadSolver(const std::string&, const std::string&, const std::string&, const std::string& name) {
213 auto found = solvers.find(name);
214 if (found == solvers.end())
215 throw Exception(
216 "in C++ solvers ('{0}' in this case) must be created and added to Manager::solvers manually before reading XML.", name);
217 solvers.erase(
218 found); // this is necessary so we don't have name conflicts — the solver will be added back to the map by loadSolvers
219 return found->second;
220}
221
222 void Manager::loadSolvers(XMLReader& reader) {
223 if (reader.getNodeType() != XMLReader::NODE_ELEMENT || reader.getNodeName() != std::string("solvers"))
224 throw XMLUnexpectedElementException(reader, "<solvers>");
225 while (reader.requireTagOrEnd()) {
226 std::string name = reader.requireAttribute("name");
227 std::replace(name.begin(), name.end(), '-', '_');
228 BadId::throwIfBad("solver", name);
229 if (shared_ptr<Solver> filter = FiltersFactory::getDefault().get(reader, *this)) {
230 if (!this->solvers.insert(std::make_pair(name, filter)).second) throw NamesConflictException("solver", name);
231 continue;
232 }
233 plask::optional<std::string> lib = reader.getAttribute("lib");
234 const std::string solver_name = reader.requireAttribute("solver");
235 std::string category = reader.getNodeName();
236 if (!lib) {
237 if (category != "local") {
238 auto libs = global_solver_names[category];
239 if (libs.empty()) { // read lib index from file
240 boost::filesystem::directory_iterator iter(plaskSolversPath(category));
241 boost::filesystem::directory_iterator end;
242 while (iter != end) {
243 boost::filesystem::path p = iter->path();
244 std::string current_solver;
245 if (boost::filesystem::is_regular_file(p) && p.extension().string() == ".yml") {
246 // Look for lib in yaml file
247 std::ifstream yaml(p.string());
248 std::string line;
249 while (std::getline(yaml, line)) {
250 if (line.substr(0, 9) == "- solver:") {
251 current_solver = line.substr(9);
252 boost::trim(current_solver);
253 } else if (current_solver != "" && line.substr(0, 6) == " lib:") {
254 std::string lib = line.substr(6);
255 boost::trim(lib);
256 libs[current_solver] = lib;
257 }
258 }
259 }
260 ++iter;
261 }
262 }
263 lib.reset(libs[solver_name]);
264 }
265 }
266 if (!lib || lib->empty())
267 throw XMLException(reader, format("cannot determine library for {0}.{1} solver", category, solver_name));
268 shared_ptr<Solver> solver = loadSolver(category, *lib, solver_name, name);
269 solver->loadConfiguration(reader, *this);
270 if (!this->solvers.insert(std::make_pair(name, solver)).second) throw NamesConflictException("solver", name);
271 }
272 assert(reader.getNodeName() == "solvers");
273}
274
275 void Manager::loadConnects(XMLReader& reader) {
276 writelog(LOG_ERROR, "Loading interconnects only possible from Python interface. Ignoring XML section <connects>.");
277 reader.gotoEndOfCurrentTag();
278}
279
280 void Manager::loadScript(XMLReader& reader) {
281 if (reader.getNodeType() != XMLReader::NODE_ELEMENT || reader.getNodeName() != "script")
282 throw XMLUnexpectedElementException(reader, "<script>");
283 scriptline = reader.getLineNr();
284 std::string scr = reader.requireTextInCurrentTag();
285 size_t start;
286 for (start = 0; scr[start] != '\n' && start < scr.length(); ++start) {
287 if (!std::isspace(scr[start]))
288 throw XMLException(format("XML line {}", scriptline), "script must begin from new line after <script>", scriptline);
289 }
290 if (start != scr.length()) script = scr.substr(start + 1);
291}
292
293/*static inline MaterialsDB& getMaterialsDBfromSource(const Manager::MaterialsDB& materialsDB) {
294 const GeometryReader::MaterialsDBSource* src = materialsDB.target<const GeometryReader::MaterialsDBSource>();
295 return src ? const_cast<MaterialsDB&>(src->materialsDB) : MaterialsDB::getDefault();
296}*/
297
298 void Manager::load(XMLReader& reader,
299 const LoadFunCallbackT& load_from,
300 const std::function<bool(const std::string& section_name)>& section_filter) {
301 errors.clear();
302 try {
303 reader.requireTag(TAG_NAME_ROOT);
304 reader.removeAlienNamespaceAttr(); // remove possible schema declaration
305 auto logattr = reader.getAttribute("loglevel");
306 if (logattr && !forcedLoglevel) {
307 try {
308 maxLoglevel = LogLevel(boost::lexical_cast<unsigned>(*logattr));
309 } catch (boost::bad_lexical_cast&) {
310 maxLoglevel = reader.enumAttribute<LogLevel>("loglevel")
311 .value("critical-error", LOG_CRITICAL_ERROR)
312 .value("critical", LOG_CRITICAL_ERROR)
313 .value("error", LOG_ERROR)
314 .value("error-detail", LOG_ERROR_DETAIL)
315 .value("warning", LOG_WARNING)
316 .value("important", LOG_IMPORTANT)
317 .value("info", LOG_INFO)
318 .value("result", LOG_RESULT)
319 .value("data", LOG_DATA)
320 .value("detail", LOG_DETAIL)
321 .value("debug", LOG_DEBUG)
322 .get(maxLoglevel);
323 }
324 }
325
326 size_t next = 0;
327
328 if (!reader.requireTagOrEnd()) return;
329
330 if (reader.getNodeName() == TAG_NAME_DEFINES) {
331 next = 1;
332 if (section_filter(TAG_NAME_DEFINES)) {
333 if (!tryLoadFromExternal(reader, load_from)) loadDefines(reader);
334 } else
335 reader.gotoEndOfCurrentTag();
336 if (!reader.requireTagOrEnd()) return;
337 }
338
339 if (reader.getNodeName() == TAG_NAME_MATERIALS) {
340 next = 2;
341 if (section_filter(TAG_NAME_MATERIALS)) {
342 if (!tryLoadFromExternal(reader, load_from)) loadMaterials(reader);
343 } else
344 reader.gotoEndOfCurrentTag();
345 if (!reader.requireTagOrEnd()) return;
346 }
347
348 if (reader.getNodeName() == TAG_NAME_GEOMETRY) {
349 next = 3;
350 if (section_filter(TAG_NAME_GEOMETRY)) {
351 if (!tryLoadFromExternal(reader, load_from)) {
352 GeometryReader greader(*this, reader);
353 loadGeometry(greader);
354 }
355 } else
356 reader.gotoEndOfCurrentTag();
357 if (!reader.requireTagOrEnd()) return;
358 }
359
360 if (reader.getNodeName() == TAG_NAME_GRIDS) {
361 next = 4;
362 if (section_filter(TAG_NAME_GRIDS)) {
363 if (!tryLoadFromExternal(reader, load_from)) loadGrids(reader);
364 } else
365 reader.gotoEndOfCurrentTag();
366 if (!reader.requireTagOrEnd()) return;
367 }
368
369 if (reader.getNodeName() == TAG_NAME_SOLVERS) {
370 next = 5;
371 if (section_filter(TAG_NAME_SOLVERS)) {
372 if (!tryLoadFromExternal(reader, load_from)) loadSolvers(reader);
373 } else
374 reader.gotoEndOfCurrentTag();
375 if (!reader.requireTagOrEnd()) return;
376 }
377
378 if (reader.getNodeName() == TAG_NAME_CONNECTS) {
379 next = 6;
380 if (section_filter(TAG_NAME_CONNECTS)) {
381 if (!tryLoadFromExternal(reader, load_from)) loadConnects(reader);
382 } else
383 reader.gotoEndOfCurrentTag();
384 if (!reader.requireTagOrEnd()) return;
385 }
386
387 if (reader.getNodeName() == TAG_NAME_SCRIPT) {
388 next = 7;
389 if (section_filter(TAG_NAME_SCRIPT)) {
390 if (!tryLoadFromExternal(reader, load_from)) loadScript(reader);
391 } else
392 reader.gotoEndOfCurrentTag();
393 if (!reader.requireTagOrEnd()) return;
394 }
395
396 const char* tags[] = {TAG_NAME_DEFINES, TAG_NAME_MATERIALS, TAG_NAME_GEOMETRY, TAG_NAME_GRIDS,
397 TAG_NAME_SOLVERS, TAG_NAME_CONNECTS, TAG_NAME_SCRIPT};
398 std::string msg;
399 for (; next != 7; ++next) {
400 msg += "<";
401 msg += tags[next];
402 msg += ">, ";
403 }
404 if (msg != "") msg += "or ";
405 msg += "</plask>";
406
407 throw XMLUnexpectedElementException(reader, msg);
408
409 } catch (const XMLException&) {
410 throw;
411 } catch (const std::exception& err) {
412 throw XMLException(reader, err.what());
413 // } catch (...) {
414 // throw XMLException(reader, "unrecognized exception");
415 }
416}
417
418/*
419 Validate positions:
420
421 Notatki:
422 1. obiekt istotny w geometrii to obiekt mający nazwę, taki że na ścieżce od korzenia (geometrii) do tego obiektu nie ma innego
423 obiektu mającego nazwe mozna je znaleźć przechodząc drzew w głąb aż do napotkania obiektu mającego nazwę (wtedy przestajemy schodzić
424 w głąb)
425 2. geometrie są porównywane parami, każda para zwiera:
426 geometrie tych samych typów, takie że jedna nie jest zawarta w drugiej
427 3. sprawdzane są tylko obiekty należące do sumy zbiorów obiektów istotnych obu geometrii
428 (uwaga: obiekt który nie jest istotny w geometrii nadal może być w niej obiektem nazwanym)
429 4. ustalane i porównywane są pozycje każdego takiego obiektu w obu geometriach i:
430 - jeśli obiekt nie występuje w jednej z geometrii ostrzeżenie NIE jest drukowane
431 - jeśli zbiory pozycji w geometriach są równej wielkości i nie są takie same ostrzeżenie JEST drukowane
432 - jeśli zbiory pozycji w geometriach mają elementy wspólne ostrzeżenie NIE jest drukowane
433 - w pozostałych przypadkach ostrzeżenie JEST drukowane
434*/
435
436 struct PositionValidator {
437 typedef std::map<const GeometryObject*, const char*> GeomToName;
438
439 // always sorted
440 typedef std::vector<const GeometryObject*> Set;
441
442 GeomToName& geom_to_name;
443
444 std::map<const Geometry*, Set> cache;
445
446 PositionValidator(GeomToName& geom_to_name) : geom_to_name(geom_to_name) {}
447
448 Set& get(const Geometry* geometry) {
449 auto it = cache.find(geometry);
450 if (it != cache.end()) return it->second;
451 Set& res = cache[geometry];
452 fill(geometry->getObject3D().get(), res);
453 std::sort(res.begin(), res.end());
454 return res;
455 }
456
457 void fill(const GeometryObject* obj, Set& s) {
458 if (!obj) return;
459 if (geom_to_name.count(obj))
460 s.push_back(obj);
461 else {
462 std::size_t c = obj->getRealChildrenCount();
463 for (std::size_t i = 0; i < c; ++i) fill(obj->getRealChildNo(i).get(), s);
464 }
465 }
466
472 // TODO what with NaNs in vectors?
473 template <typename VectorType> bool compare_vec(std::vector<VectorType> v1, std::vector<VectorType> v2) {
474 if (v1.empty() || v2.empty()) return true;
475 std::sort(v1.begin(), v1.end());
476 std::sort(v2.begin(), v2.end());
477 if (v1.size() == v2.size()) {
478 return std::equal(v1.begin(), v1.end(), v2.begin(), [](const VectorType& x1, const VectorType& x2) {
479 return isnan(x1) || isnan(x2) || x1.equals(x2, 1.1e-4);
480 });
481 } // different sizes:
482 auto v2_last_match_it = v2.begin(); // != v2.end() since v2 is not empty
483 for (VectorType point : v1) {
484 while (true) {
485 if (point.equals(*v2_last_match_it, 1.1e-4)) return true; // common point
486 if (point < *v2_last_match_it) break; // point is not included in v2, we are going to check next point from v1
487 ++v2_last_match_it;
488 if (v2_last_match_it == v2.end()) return false; // no common points found so far
489 }
490 }
491 return false;
492 }
493
499 template <typename GeomType> Set compare_d(const GeomType* geom1, const GeomType* geom2) {
500 auto c1 = geom1->getChildUnsafe();
501 auto c2 = geom2->getChildUnsafe();
502 if (!c1 || !c2) return {}; // one geom. is empty
503 if (geom1->hasInSubtree(*c2) || geom2->hasInSubtree(*c1)) return {};
504 Set obj_to_check;
505 {
506 Set og1 = get(geom1);
507 Set og2 = get(geom2);
508 std::set_union(og1.begin(), og1.end(), og2.begin(), og2.end(), std::back_inserter(obj_to_check));
509 }
510 Set result;
511 for (auto o : obj_to_check)
512 if (!compare_vec(geom1->getObjectPositions(*o), geom2->getObjectPositions(*o))) {
513 // found problem, for obj geom_to_name[o]
514 result.push_back(o);
515 }
516 return result;
517 }
518
519 Set compare(const Geometry* g1, const Geometry* g2) {
520 if (const GeometryD<2>* g1_2d = dynamic_cast<const GeometryD<2>*>(g1))
521 return compare_d(g1_2d, static_cast<const GeometryD<2>*>(g2));
522 if (const GeometryD<3>* g1_3d = dynamic_cast<const GeometryD<3>*>(g1))
523 return compare_d(g1_3d, static_cast<const GeometryD<3>*>(g2));
524 return {};
525 }
526};
527
528 void Manager::validatePositions(const std::function<void(const Geometry*,
529 const Geometry*,
530 std::vector<const GeometryObject*>&&,
531 const std::map<const GeometryObject*, const char*>&)>& callback) const {
532 // split geometries by types, we will compare each pairs of geometries of the same type:
533 typedef std::map<std::type_index, std::set<const Geometry*>> GeomToType;
534 GeomToType geometries_by_type;
535 for (auto& geom : roots) {
536 auto ptr = geom.get();
537 geometries_by_type[std::type_index(typeid(*ptr))].insert(ptr);
538 }
539 if (std::find_if(geometries_by_type.begin(), geometries_by_type.end(),
540 [](GeomToType::value_type& v) { return v.second.size() > 1; }) == geometries_by_type.end())
541 return; // no 2 geometries of the same type
542
543 PositionValidator::GeomToName geom_to_name;
544 for (auto& i : geometrics) geom_to_name[i.second.get()] = i.first.c_str();
545 PositionValidator important_obj(geom_to_name);
546
547 for (auto& geom_set : geometries_by_type)
548 for (auto it = geom_set.second.begin(); it != geom_set.second.end(); ++it) {
549 auto it2 = it;
550 ++it2;
551 for (; it2 != geom_set.second.end(); ++it2) {
552 PositionValidator::Set objs = important_obj.compare(*it, *it2);
553 if (!objs.empty()) callback(*it, *it2, std::move(objs), geom_to_name);
554 }
555 }
556}
557
558 static std::string geomName(const Manager& m, const Geometry* g, const std::map<const GeometryObject*, const char*>& names) {
559 auto it = names.find(g);
560 if (it != names.end()) {
561 std::string r = "'";
562 r += it->second;
563 r += '\'';
564 return r;
565 } else {
566 std::string r = "[";
567 r += boost::lexical_cast<std::string>(m.getRootIndex(g));
568 r += ']';
569 return r;
570 }
571}
572
573 void Manager::validatePositions() const {
574 if (this->draft) return;
575 validatePositions([this](const Geometry* g1, const Geometry* g2, std::vector<const GeometryObject*>&& objs,
576 const std::map<const GeometryObject*, const char*>& names) {
577 bool single = objs.size() < 2;
578 std::string ons;
579 for (auto o : objs) {
580 ons += " '";
581 ons += names.find(o)->second;
582 ons += '\'';
583 }
584 writelog(plask::LOG_WARNING, "Object{}{} ha{} different position in geometry {} and {}", single ? "" : "s", ons,
585 single ? "s" : "ve", geomName(*this, g1, names), geomName(*this, g2, names));
586 });
587}
588
589std::size_t Manager::getRootIndex(const Geometry* geom) const {
590 for (std::size_t result = 0; result < roots.size(); ++result)
591 if (roots[result].get() == geom) return result;
592 return roots.size();
593}
594
595} // namespace plask