PLaSK library

Loading...
Searching...
No Matches
reader.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 "reader.hpp"
15
16#include "../manager.hpp"
17
18 namespace plask {
19
20 constexpr const char* const GeometryReader::XML_NAME_ATTR;
21 constexpr const char* const GeometryReader::XML_MATERIAL_ATTR;
22 constexpr const char* const GeometryReader::XML_MATERIAL_TOP_ATTR;
23 constexpr const char* const GeometryReader::XML_MATERIAL_BOTTOM_ATTR;
24 constexpr const char* const GeometryReader::XML_MATERIAL_GRADING_ATTR;
25
26 shared_ptr<Material> GeometryReader::getMaterial(const std::string& material_full_name) const {
27 try {
28 return materialsDB->get(material_full_name);
29 } catch (const NoSuchMaterial& err) {
30 manager.throwErrorIfNotDraft(err);
31 return plask::make_shared<DummyMaterial>(material_full_name);
32 } catch (const MaterialParseException& err) {
33 manager.throwErrorIfNotDraft(err);
34 return plask::make_shared<DummyMaterial>(material_full_name);
35 }
36}
37
38 shared_ptr<MaterialsDB::MixedCompositionFactory> GeometryReader::getMixedCompositionFactory(const std::string& material1_full_name,
39 const std::string& material2_full_name,
40 double shape) const {
41 try {
42 return materialsDB->getFactory(material1_full_name, material2_full_name, shape);
43 } catch (const NoSuchMaterial& err) {
44 manager.throwErrorIfNotDraft(err);
45 return plask::make_shared<MaterialsDB::DummyMixedCompositionFactory>(material1_full_name, material2_full_name);
46 } catch (const MaterialParseException& err) {
47 manager.throwErrorIfNotDraft(err);
48 return plask::make_shared<MaterialsDB::DummyMixedCompositionFactory>(material1_full_name, material2_full_name);
49 }
50}
51
52 SolidOrGradientMaterial GeometryReader::requireSolidOrGradientMaterial() const {
53 auto top_attr = source.getAttribute(GeometryReader::XML_MATERIAL_TOP_ATTR);
54 auto bottom_attr = source.getAttribute(GeometryReader::XML_MATERIAL_BOTTOM_ATTR);
55 if (!top_attr && !bottom_attr) {
56 if (source.hasAttribute(GeometryReader::XML_MATERIAL_GRADING_ATTR))
57 source.throwException(
58 format("'{}' attribute allowed only for layers with graded material", GeometryReader::XML_MATERIAL_GRADING_ATTR));
59 try {
60 return getMaterial(source.requireAttribute(XML_MATERIAL_ATTR));
61 } catch (const XMLNoAttrException err) {
62 manager.throwErrorIfNotDraft(err);
63 return plask::make_shared<DummyMaterial>("");
64 }
65 } else {
66 double shape = source.getAttribute<double>(GeometryReader::XML_MATERIAL_GRADING_ATTR, 1.);
67 if (!manager.draft) {
68 if (!top_attr || !bottom_attr)
69 source.throwException(format("If '{0}' or '{1}' attribute is given, the other one is also required",
70 GeometryReader::XML_MATERIAL_TOP_ATTR, GeometryReader::XML_MATERIAL_BOTTOM_ATTR));
71 return getMixedCompositionFactory(*top_attr, *bottom_attr, shape);
72 } else {
73 if (!top_attr || !bottom_attr)
74 manager.pushError(
75 XMLException(source, format("If '{0}' or '{1}' attribute is given, the other one is also required",
76 GeometryReader::XML_MATERIAL_TOP_ATTR, GeometryReader::XML_MATERIAL_BOTTOM_ATTR)));
77 return (*getMixedCompositionFactory(*top_attr, *bottom_attr, shape))(0.5);
78 }
79 }
80}
81
82std::map<std::string, GeometryReader::object_read_f*>& GeometryReader::objectReaders() {
83 static std::map<std::string, GeometryReader::object_read_f*> result;
84 return result;
85}
86
87 void GeometryReader::registerObjectReader(const std::string& tag_name, object_read_f* reader) {
88 objectReaders()[tag_name] = reader;
89}
90
91std::map<std::string, GeometryReader::changer_read_f>& GeometryReader::changerReaders() {
92 static std::map<std::string, GeometryReader::changer_read_f> result;
93 return result;
94}
95
96 void GeometryReader::registerChangerReader(const std::string& tag_name, changer_read_f reader) {
97 changerReaders()[tag_name] = reader;
98}
99
100 const AxisNames& GeometryReader::getAxisNames() const { return *manager.axisNames; }
101
102std::string GeometryReader::getAxisName(std::size_t axis_index) { return manager.getAxisName(axis_index); }
103
104 GeometryReader::SetExpectedSuffix::SetExpectedSuffix(GeometryReader& reader, const char* new_expected_suffix)
105 : reader(reader), old(reader.expectedSuffix) {
106 reader.expectedSuffix = new_expected_suffix;
107}
108
109 GeometryReader::SetExpectedSuffix::SetExpectedSuffix(GeometryReader& reader, int dim) : reader(reader), old(reader.expectedSuffix) {
110 if (dim == 2)
111 reader.expectedSuffix = PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D;
112 else if (dim == 3)
113 reader.expectedSuffix = PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D;
114 else
115 reader.expectedSuffix = "";
116}
117
118 GeometryReader::GeometryReader(plask::Manager& manager, plask::XMLReader& source, const MaterialsDB& materialsDB)
119 : materialsAreRequired(!manager.draft), expectedSuffix(0), manager(manager), source(source), materialsDB(&materialsDB) {}
120
121 inline bool isAutoName(const std::string& name) { return !name.empty() && name[0] == '#'; }
122
123#define XML_MAX_STEPS_ATTR "steps-num"
124#define XML_MIN_STEP_SIZE_ATTR "steps-dist"
125
126 shared_ptr<GeometryObject> GeometryReader::readObject() {
127 std::string nodeName = source.getNodeName();
128
129 if (nodeName == "again") {
130 shared_ptr<GeometryObject> result = requireObjectFromAttribute("ref");
131 source.requireTagEnd();
132 return result;
133 }
134
135 plask::optional<std::string> name = source.getAttribute(XML_NAME_ATTR); // read name
136 if (name && !isAutoName(*name)) {
137 std::replace(name->begin(), name->end(), '-', '_');
138 BadId::throwIfBad("geometry object", *name);
139 }
140
141 plask::optional<std::string> roles = source.getAttribute("role"); // read roles (tags)
142
143 auto max_steps = source.getAttribute<unsigned long>(XML_MAX_STEPS_ATTR);
144 auto min_step_size = source.getAttribute<double>(XML_MIN_STEP_SIZE_ATTR);
145
146 shared_ptr<GeometryObject> new_object; // new object that will be constructed
147
148 std::deque<std::pair<std::string, shared_ptr<GeometryObject>>> other_names;
149
150 if (nodeName == "copy") {
151 shared_ptr<GeometryObject> from = requireObjectFromAttribute("from");
152 try {
153 if (auto geometry = dynamic_pointer_cast<Geometry2DCartesian>(from))
154 from = geometry->getExtrusion();
155 else if (auto geometry = dynamic_pointer_cast<Geometry2DCylindrical>(from))
156 from = geometry->getRevolution();
157 else if (auto geometry = dynamic_pointer_cast<Geometry3D>(from))
158 from = geometry->getChild();
159 } catch (const NoChildException&) {
160 from.reset();
161 }
162 if (from) {
163 GeometryObject::CompositeChanger changers;
164 while (source.requireTagOrEnd()) {
165 const std::string operation_name = source.getNodeName();
166 if (operation_name == "replace") {
167 shared_ptr<GeometryObject> op_from = requireObjectFromAttribute("object");
168 plask::optional<std::string> to_name = source.getAttribute("with");
169 if (op_from) {
170 shared_ptr<GeometryObject> to;
171 if (to_name) {
172 to = requireObjectWithName(*to_name);
173 source.requireTagEnd();
174 } else {
175 if (source.requireTagOrEnd()) {
176 SetExpectedSuffix suffixSetter(*this, op_from->getDimensionsCount());
177 to = readObject();
178 source.requireTagEnd();
179 } else {
180 manager.throwErrorIfNotDraft(XMLUnexpectedElementException(source, "new tag"));
181 }
182 }
183 if (to)
184 changers.append(new GeometryObject::ReplaceChanger(op_from, to, vec(0.0, 0.0, 0.0)));
185 else
186 changers.append(new GeometryObject::DeleteChanger(op_from));
187 } else
188 source.gotoEndOfCurrentTag();
189 } else if (operation_name == "toblock") {
190 shared_ptr<GeometryObject> op_from = requireObjectFromAttribute("object");
191 SolidOrGradientMaterial blockMaterial = requireSolidOrGradientMaterial();
192 plask::optional<std::string> block_name = source.getAttribute(XML_NAME_ATTR); // read name
193 if (op_from) {
194 GeometryObject::ToBlockChanger* changer =
195 new GeometryObject::ToBlockChanger(op_from, blockMaterial, manager.draft);
196 changers.append(changer);
197 if (block_name && !isAutoName(*block_name)) {
198 std::replace(block_name->begin(), block_name->end(), '-', '_');
199 BadId::throwIfBad("block replacing object", *block_name);
200 other_names.push_back(std::make_pair(*block_name, changer->to));
201 }
202 if (plask::optional<std::string> block_roles = source.getAttribute("role")) { // if have some roles
203 for (const std::string& c : splitEscIterator(*block_roles, ',')) changer->to->addRole(c);
204 }
205 }
206 source.requireTagEnd();
207 } else if (operation_name == "delete") {
208 shared_ptr<GeometryObject> op_from = requireObjectFromAttribute("object");
209 if (op_from) changers.append(new GeometryObject::DeleteChanger(op_from));
210 source.requireTagEnd();
211 } else {
212 auto found = changerReaders().find(operation_name);
213 if (found == changerReaders().end())
214 throw Exception("\"{0}\" is not a proper name of copy operation and so it is not allowed in <copy> tag.",
215 operation_name);
216 changers.append(found->second(*this));
217 }
218 }
219 new_object = const_pointer_cast<GeometryObject>(from->changedVersion(changers));
220 } else {
221 source.gotoEndOfCurrentTag();
222 }
223 } else {
224 Manager::SetAxisNames axis_reader(*this); // try set up new axis names, store old, and restore old on end of block
225 auto reader_it = objectReaders().find(nodeName);
226 if (reader_it == objectReaders().end()) {
227 if (expectedSuffix == 0) throw NoSuchGeometryObjectType(nodeName);
228 reader_it = objectReaders().find(nodeName + expectedSuffix);
229 if (reader_it == objectReaders().end()) throw NoSuchGeometryObjectType(nodeName + "[" + expectedSuffix + "]");
230 }
231 new_object = reader_it->second(*this); // and rest (but while reading this subtree, name is not registered yet)
232 }
233
234 if (!new_object) return new_object;
235
236 registerObjectName(name, new_object);
237 for (const auto& other : other_names) {
238 registerObjectName(other.first, other.second);
239 }
240
241 if (roles) { // if have some roles
242 new_object->clearRoles(); // in case of copied object: overwrite
243 auto roles_it = splitEscIterator(*roles, ',');
244 for (const std::string& c : roles_it) {
245 new_object->addRole(c);
246 }
247 }
248
249 if (max_steps) new_object->max_steps = *max_steps;
250 if (min_step_size) new_object->min_step_size = *min_step_size;
251
252 return new_object;
253}
254
255 shared_ptr<GeometryObject> GeometryReader::readExactlyOneChild() {
256 shared_ptr<GeometryObject> result;
257 if (source.requireNext((!manager.draft) ? XMLReader::NODE_ELEMENT : (XMLReader::NODE_ELEMENT | XMLReader::NODE_ELEMENT_END)) ==
258 XMLReader::NODE_ELEMENT) {
259 result = readObject();
260 source.requireTagEnd();
261 } else {
262 manager.pushError(XMLUnexpectedElementException(source, "new tag"));
263 }
264 return result;
265}
266
267 shared_ptr<Geometry> GeometryReader::readGeometry() {
268 Manager::SetAxisNames axis_reader(*this); // try set up new axis names, store old, and restore old on end of block
269 std::string nodeName = source.getNodeName();
270 plask::optional<std::string> name = source.getAttribute(XML_NAME_ATTR);
271 if (name) {
272 std::replace(name->begin(), name->end(), '-', '_');
273 BadId::throwIfBad("geometry", *name);
274 if (manager.geometrics.find(*name) != manager.geometrics.end())
275 throw XMLDuplicatedElementException(source, "geometry '" + *name + "'");
276 }
277
278 // TODO read subspaces from XML
279 shared_ptr<Geometry> result;
280
281 if (nodeName == "cartesian2d") {
282 SetExpectedSuffix suffixSetter(*this, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D);
283 plask::optional<double> l = source.getAttribute<double>("length");
284 shared_ptr<Geometry2DCartesian> cartesian2d = plask::make_shared<Geometry2DCartesian>(); // result with original type
285 result = cartesian2d;
286 result->setEdges([&](const std::string& s) -> plask::optional<std::string> { return source.getAttribute(s); },
287 getAxisNames(), *materialsDB, manager.draft);
288 if (l) {
289 cartesian2d->setExtrusion(plask::make_shared<Extrusion>(readExactlyOneChild<GeometryObjectD<2>>(), *l));
290 } else {
291 shared_ptr<GeometryObject> child;
292 bool require_end = true;
293 if (source.requireNext(manager.draft ? (XMLReader::NODE_ELEMENT | XMLReader::NODE_ELEMENT_END)
294 : XMLReader::NODE_ELEMENT) == XMLReader::NODE_ELEMENT) {
295 child = readObject();
296 } else {
297 manager.pushError(XMLUnexpectedElementException(source, "new tag"));
298 require_end = false;
299 }
300 auto child_as_extrusion = dynamic_pointer_cast<Extrusion>(child);
301 if (child_as_extrusion) {
302 cartesian2d->setExtrusion(child_as_extrusion);
303 } else {
304 auto child_as_2d = dynamic_pointer_cast<GeometryObjectD<2>>(child);
305 if (!child_as_2d) manager.throwErrorIfNotDraft(UnexpectedGeometryObjectTypeException());
306 cartesian2d->setExtrusion(plask::make_shared<Extrusion>(child_as_2d, INFINITY));
307 }
308 if (require_end) source.requireTagEnd();
309 }
310
311 } else if (nodeName == "cylindrical" || nodeName == "cylindrical2d") {
312 SetExpectedSuffix suffixSetter(*this, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D);
313 result = plask::make_shared<Geometry2DCylindrical>();
314 result->setEdges([&](const std::string& s) -> plask::optional<std::string> { return source.getAttribute(s); },
315 getAxisNames(), *materialsDB, manager.draft);
316 shared_ptr<GeometryObject> child;
317 bool require_end = true;
318 if (source.requireNext(manager.draft ? (XMLReader::NODE_ELEMENT | XMLReader::NODE_ELEMENT_END) : XMLReader::NODE_ELEMENT) ==
319 XMLReader::NODE_ELEMENT) {
320 child = readObject();
321 } else {
322 manager.pushError(XMLUnexpectedElementException(source, "new tag"));
323 require_end = false;
324 }
325 auto child_as_revolution = dynamic_pointer_cast<Revolution>(child);
326 if (child_as_revolution) {
327 static_pointer_cast<Geometry2DCylindrical>(result)->setRevolution(child_as_revolution);
328 } else {
329 auto child_as_2d = dynamic_pointer_cast<GeometryObjectD<2>>(child);
330 if (!child_as_2d) manager.throwErrorIfNotDraft(UnexpectedGeometryObjectTypeException());
331 static_pointer_cast<Geometry2DCylindrical>(result)->setRevolution(plask::make_shared<Revolution>(child_as_2d));
332 }
333 if (require_end) source.requireTagEnd();
334
335 } else if (nodeName == "cartesian3d") {
336 SetExpectedSuffix suffixSetter(*this, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D);
337 result = plask::make_shared<Geometry3D>();
338 result->setEdges([&](const std::string& s) -> plask::optional<std::string> { return source.getAttribute(s); },
339 getAxisNames(), *materialsDB, manager.draft);
340 static_pointer_cast<Geometry3D>(result)->setChildUnsafe(readExactlyOneChild<GeometryObjectD<3>>());
341
342 } else
343 throw XMLUnexpectedElementException(source, "geometry tag (<cartesian2d>, <cartesian3d>, or <cylindrical>)");
344
345 result->axisNames = getAxisNames();
346
347 if (name) manager.geometrics[*name] = result;
348 return result;
349}
350
351 shared_ptr<GeometryObject> GeometryReader::requireObjectWithName(const std::string& name) const {
352 if (isAutoName(name)) {
353 auto it = autoNamedObjects.find(name);
354 if (it == autoNamedObjects.end()) {
355 manager.throwErrorIfNotDraft(NoSuchGeometryObject(name));
356 return shared_ptr<GeometryObject>();
357 }
358 return it->second;
359 } else {
360 try {
361 return manager.requireGeometryObject(name);
362 } catch (const NoSuchGeometryObject& err) {
363 manager.throwErrorIfNotDraft(err);
364 return shared_ptr<GeometryObject>();
365 }
366 }
367}
368
369 void GeometryReader::registerObjectName(const std::string& name, shared_ptr<GeometryObject> object) {
370 if (isAutoName(name)) {
371 if (!autoNamedObjects.insert(std::map<std::string, shared_ptr<GeometryObject>>::value_type(name, object)).second)
372 throw NamesConflictException("auto-named geometry object", name);
373 } else { // normal name
374 if (!manager.geometrics.insert(std::map<std::string, shared_ptr<GeometryObject>>::value_type(name, object)).second)
375 throw NamesConflictException("geometry object", name);
376 }
377}
378
379} // namespace plask