PLaSK library

Loading...
Searching...
No Matches
lattice.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 "lattice.hpp"
15
16#include "../manager.hpp"
17#include "reader.hpp"
18
19// used by lattice
20#include <map>
21#include <set>
22#include <utility>
23
24#define PLASK_ARRANGE2D_NAME ("arrange" PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D)
25#define PLASK_ARRANGE3D_NAME ("arrange" PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D)
26#define PLASK_LATTICE_NAME "lattice"
27
28 namespace plask {
29
30 template <int dim> const char* ArrangeContainer<dim>::NAME = dim == 2 ? PLASK_ARRANGE2D_NAME : PLASK_ARRANGE3D_NAME;
31
32 template <int dim>
33std::pair<int, int> ArrangeContainer<dim>::bounds(const typename ArrangeContainer<dim>::DVec& vec) const {
34 if (!this->hasChild() || repeat_count == 0) return std::make_pair(1, 0);
35 auto box = _child->getBoundingBox();
36 int hi = repeat_count - 1, lo = 0;
37 for (int i = 0; i != dim; ++i) {
38 if (translation[i] > 0.) {
39 lo = max(1 + int(std::floor((vec[i] - box.upper[i]) / translation[i])), lo);
40 hi = min(int(std::floor((vec[i] - box.lower[i]) / translation[i])), hi);
41 } else if (translation[i] < 0.) {
42 lo = max(1 + int(std::floor((vec[i] - box.lower[i]) / translation[i])), lo);
43 hi = min(int(std::floor((vec[i] - box.upper[i]) / translation[i])), hi);
44 } else if (vec[i] < box.lower[i] || box.upper[i] < vec[i]) {
45 return std::make_pair(1, 0);
46 }
47 }
48 return std::make_pair(lo, hi);
49}
50
51 template <int dim> void ArrangeContainer<dim>::warmOverlaping() const {
52 if (warn_overlapping && this->hasChild()) {
53 Box box = this->_child->getBoundingBox();
54 box -= box.lower;
55 if (box.intersects(box + translation)) writelog(LOG_WARNING, "Arrange: item bboxes overlap");
56 }
57}
58
59 template <int dim> typename ArrangeContainer<dim>::Box ArrangeContainer<dim>::getBoundingBox() const {
60 Box bbox;
61 if (!this->hasChild()) {
62 bbox.makeInvalid();
63 } else {
64 Box box = _child->getBoundingBox();
65 for (int i = 0; i != dim; ++i) {
66 if (translation[i] >= 0.) {
67 bbox.lower[i] = box.lower[i];
68 bbox.upper[i] = box.upper[i] + (int(repeat_count) - 1) * translation[i];
69 } else {
70 bbox.lower[i] = box.lower[i] + (int(repeat_count) - 1) * translation[i];
71 bbox.upper[i] = box.upper[i];
72 }
73 }
74 }
75 return bbox;
76}
77
78 template <int dim> typename ArrangeContainer<dim>::Box ArrangeContainer<dim>::getRealBoundingBox() const {
79 if (!getChild()) return ArrangeContainer<dim>::Box(Primitive<dim>::ZERO_VEC, Primitive<dim>::ZERO_VEC);
80 return getChild()->getBoundingBox();
81}
82
83 template <int dim>
84 void ArrangeContainer<dim>::getBoundingBoxesToVec(const GeometryObject::Predicate& predicate,
85 std::vector<typename ArrangeContainer<dim>::Box>& dest,
86 const PathHints* path) const {
87 if (predicate(*this)) {
88 dest.push_back(getBoundingBox());
89 return;
90 }
91 if (repeat_count == 0 || !this->hasChild()) return;
92 std::size_t old_size = dest.size();
93 _child->getBoundingBoxesToVec(predicate, dest, path);
94 std::size_t new_size = dest.size();
95 for (unsigned r = 1; r < repeat_count; ++r)
96 for (std::size_t i = old_size; i < new_size; ++i) dest.push_back(dest[i] + translation * r);
97}
98
99 template <int dim>
100 void ArrangeContainer<dim>::getObjectsToVec(const GeometryObject::Predicate& predicate,
101 std::vector<shared_ptr<const GeometryObject>>& dest,
102 const PathHints* path) const {
103 if (predicate(*this)) {
104 dest.push_back(this->shared_from_this());
105 return;
106 }
107 if (repeat_count == 0 || !this->hasChild()) return;
108 std::size_t old_size = dest.size();
109 _child->getObjectsToVec(predicate, dest, path);
110 std::size_t new_size = dest.size();
111 for (unsigned r = 1; r < repeat_count; ++r)
112 for (std::size_t i = old_size; i < new_size; ++i) dest.push_back(dest[i]);
113}
114
115 template <int dim>
116 void ArrangeContainer<dim>::getPositionsToVec(const GeometryObject::Predicate& predicate,
117 std::vector<typename ArrangeContainer<dim>::DVec>& dest,
118 const PathHints* path) const {
119 if (predicate(*this)) {
120 dest.push_back(Primitive<dim>::ZERO_VEC);
121 return;
122 }
123 if (repeat_count == 0 || !this->hasChild()) return;
124 std::size_t old_size = dest.size();
125 _child->getPositionsToVec(predicate, dest, path);
126 std::size_t new_size = dest.size();
127 for (unsigned r = 1; r < repeat_count; ++r)
128 for (std::size_t i = old_size; i < new_size; ++i) dest.push_back(dest[i] + translation * r);
129}
130
131 template <int dim> bool ArrangeContainer<dim>::contains(const typename ArrangeContainer<dim>::DVec& p) const {
132 if (!this->hasChild()) return false;
133 auto lohi = bounds(p);
134 for (int i = lohi.second; i >= lohi.first; --i)
135 if (_child->contains(p - i * translation)) return true;
136 return false;
137}
138
139 template <int dim>
140 shared_ptr<Material> ArrangeContainer<dim>::getMaterial(const typename ArrangeContainer<dim>::DVec& p) const {
141 if (!this->hasChild()) return shared_ptr<Material>();
142 auto lohi = bounds(p);
143 for (int i = lohi.second; i >= lohi.first; --i)
144 if (auto material = _child->getMaterial(p - i * translation)) return material;
145 return shared_ptr<Material>();
146}
147
148 template <int dim> std::size_t ArrangeContainer<dim>::getChildrenCount() const {
149 return this->hasChild() ? repeat_count : 0;
150}
151
152 template <int dim> shared_ptr<GeometryObject> ArrangeContainer<dim>::getChildNo(std::size_t child_no) const {
153 if (child_no >= getChildrenCount())
154 throw OutOfBoundsException("getChildNo", "child_no", child_no, 0, getChildrenCount() - 1);
155 return plask::make_shared<Translation<dim>>(_child, child_no * translation);
156}
157
158 template <int dim> std::size_t ArrangeContainer<dim>::getRealChildrenCount() const {
159 return GeometryObjectTransform<dim>::getChildrenCount();
160}
161
162 template <int dim> shared_ptr<GeometryObject> ArrangeContainer<dim>::getRealChildNo(std::size_t child_no) const {
163 return GeometryObjectTransform<dim>::getRealChildNo(child_no);
164}
165
166 template <int dim>
167 GeometryObject::Subtree ArrangeContainer<dim>::getPathsAt(const typename ArrangeContainer<dim>::DVec& point,
168 bool all) const {
169 if (!this->hasChild()) return GeometryObject::Subtree();
170 GeometryObject::Subtree result;
171 auto lohi = bounds(point);
172 if (all) {
173 for (int i = lohi.first; i <= lohi.second; --i) {
174 GeometryObject::Subtree child_path = _child->getPathsAt(point - i * translation, true);
175 if (!child_path.empty()) result.children.push_back(std::move(child_path));
176 }
177 } else {
178 for (int i = lohi.second; i >= lohi.first; --i) {
179 GeometryObject::Subtree child_path = _child->getPathsAt(point - i * translation, true);
180 if (!child_path.empty()) {
181 result.children.push_back(std::move(child_path));
182 break;
183 }
184 }
185 }
186 if (!result.children.empty()) result.object = this->shared_from_this();
187 return result;
188}
189
190 template <int dim> shared_ptr<GeometryObject> ArrangeContainer<dim>::shallowCopy() const {
191 return plask::make_shared<ArrangeContainer<dim>>(_child, translation, repeat_count);
192}
193
194 template <int dim>
195 typename ArrangeContainer<dim>::Box ArrangeContainer<dim>::fromChildCoords(
196 const typename ArrangeContainer<dim>::ChildType::Box& child_bbox) const {
197 return child_bbox;
198}
199
200 template <int dim>
201 void ArrangeContainer<dim>::addPointsAlongToSet(std::set<double>& points,
202 Primitive<3>::Direction direction,
203 unsigned max_steps,
204 double min_step_size) const {
205 if (!this->hasChild()) return;
206 std::set<double> child_points;
207 _child->addPointsAlongToSet(child_points, direction, this->max_steps ? this->max_steps : max_steps,
208 this->min_step_size ? this->min_step_size : min_step_size);
209 for (int i = 0; i < repeat_count; ++i) {
210 double trans = i * translation[int(direction) - (3 - dim)];
211 for (double p : child_points) points.insert(p + trans);
212 }
213}
214
215 template <int dim>
216 void ArrangeContainer<dim>::addLineSegmentsToSet(std::set<typename GeometryObjectD<dim>::LineSegment>& segments,
217 unsigned max_steps,
218 double min_step_size) const {
219 if (!this->hasChild()) return;
220 std::set<typename GeometryObjectD<dim>::LineSegment> child_segments;
221 _child->addLineSegmentsToSet(child_segments, this->max_steps ? this->max_steps : max_steps,
222 this->min_step_size ? this->min_step_size : min_step_size);
223 for (int i = 0; i < repeat_count; ++i) {
224 DVec trans = i * translation;
225 for (const auto& p : child_segments)
226 segments.insert(typename GeometryObjectD<dim>::LineSegment(p[0] + trans, p[1] + trans));
227 }
228}
229
230 template <> void ArrangeContainer<2>::writeXMLAttr(XMLWriter::Element& dest_xml_object, const AxisNames& axes) const {
231 BaseClass::writeXMLAttr(dest_xml_object, axes);
232 if (translation.tran() != 0.) dest_xml_object.attr("d" + axes.getNameForTran(), translation.tran());
233 if (translation.vert() != 0.) dest_xml_object.attr("d" + axes.getNameForVert(), translation.vert());
234 dest_xml_object.attr("count", repeat_count);
235 if (warn_overlapping) dest_xml_object.attr("warning", "false");
236}
237
238 template <> void ArrangeContainer<3>::writeXMLAttr(XMLWriter::Element& dest_xml_object, const AxisNames& axes) const {
239 BaseClass::writeXMLAttr(dest_xml_object, axes);
240 if (translation.lon() != 0.) dest_xml_object.attr("d" + axes.getNameForLong(), translation.lon());
241 if (translation.tran() != 0.) dest_xml_object.attr("d" + axes.getNameForTran(), translation.tran());
242 if (translation.vert() != 0.) dest_xml_object.attr("d" + axes.getNameForVert(), translation.vert());
243 dest_xml_object.attr("count", repeat_count);
244 if (warn_overlapping) dest_xml_object.attr("warning", "false");
245}
246
247 shared_ptr<GeometryObject> read_arrange2d(GeometryReader& reader) {
248 GeometryReader::SetExpectedSuffix suffixSetter(reader, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D);
249 typename ArrangeContainer<2>::DVec vec;
250 vec.tran() = reader.source.getAttribute("d" + reader.getAxisTranName(), 0.);
251 vec.vert() = reader.source.getAttribute("d" + reader.getAxisVertName(), 0.);
252 unsigned repeat = reader.source.requireAttribute<unsigned>("count");
253 bool warn = reader.source.getAttribute("warning", true);
254 auto child = reader.readExactlyOneChild<typename ArrangeContainer<2>::ChildType>();
255 return plask::make_shared<ArrangeContainer<2>>(child, vec, repeat, warn);
256}
257
258 shared_ptr<GeometryObject> read_arrange3d(GeometryReader& reader) {
259 GeometryReader::SetExpectedSuffix suffixSetter(reader, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D);
260 typename ArrangeContainer<3>::DVec vec;
261 vec.lon() = reader.source.getAttribute("d" + reader.getAxisLongName(), 0.);
262 vec.tran() = reader.source.getAttribute("d" + reader.getAxisTranName(), 0.);
263 vec.vert() = reader.source.getAttribute("d" + reader.getAxisVertName(), 0.);
264 unsigned repeat = reader.source.requireAttribute<unsigned>("count");
265 bool warn = reader.source.getAttribute("warning", true);
266 auto child = reader.readExactlyOneChild<typename ArrangeContainer<3>::ChildType>();
267 return plask::make_shared<ArrangeContainer<3>>(child, vec, repeat, warn);
268}
269
270 static GeometryReader::RegisterObjectReader arrange2d_reader(PLASK_ARRANGE2D_NAME, read_arrange2d);
271 static GeometryReader::RegisterObjectReader arrange3d_reader(PLASK_ARRANGE3D_NAME, read_arrange3d);
272
273 template struct PLASK_API ArrangeContainer<2>;
274 template struct PLASK_API ArrangeContainer<3>;
275
276 struct YEnds {
277 // map: y -> 2*x, each (x, y) is point where shape begins or ends
278 // odd 2*x are for each points between integer coordinates
279 std::map<int, std::set<int>> coords;
280
281 void add_d(int dbl_x, int y) {
282 std::set<int>& dst = coords[y];
283 auto ins_ans = dst.insert(dbl_x);
284 if (!ins_ans.second) { // element was already included
285 dst.erase(ins_ans.first); // we remove it
286 if (dst.empty()) coords.erase(y);
287 }
288 }
289
290 // void add(int x, int y) { add_d(2*x, y); }
291 void add(LateralVec<int> p) { add_d(2 * p.c0, p.c1); }
292};
293
301 struct SegmentsIterator {
302 const std::vector<std::vector<LateralVec<int>>>& segments;
303
304 LateralVec<int> first, second;
305
306 std::size_t seg_nr;
307 std::ptrdiff_t point_nr;
308
314 SegmentsIterator(const std::vector<std::vector<LateralVec<int>>>& segments)
315 : segments(segments), seg_nr(0), point_nr(-1) {}
316
321 bool next() {
322 if (seg_nr == segments.size()) return false;
323 ++point_nr;
324 if (point_nr == std::ptrdiff_t(segments[seg_nr].size())) { // end of segment?
325 point_nr = 0; // go to next segment
326 do {
327 ++seg_nr;
328 if (seg_nr == segments.size()) return false;
329 } while (segments[seg_nr].empty()); // loop skips empty segments
330 }
331 first = segments[seg_nr][point_nr];
332 second = segments[seg_nr][(point_nr + 1) % segments[seg_nr].size()];
333 return true;
334 }
335};
336
337 const char* Lattice::NAME = PLASK_LATTICE_NAME;
338
339 void Lattice::getBoundingBoxesToVec(const GeometryObject::Predicate& predicate,
340 std::vector<Box>& dest,
341 const PathHints* path) const {
342 if (predicate(*this)) {
343 dest.push_back(getBoundingBox());
344 return;
345 }
346 // hide this->child from predicate, delegate calling to its child
347 container->forEachChild([&](const Translation<3>& child) { child.getBoundingBoxesToVec(predicate, dest, path); },
348 path);
349}
350
351 void Lattice::getObjectsToVec(const GeometryObject::Predicate& predicate,
352 std::vector<shared_ptr<const GeometryObject>>& dest,
353 const PathHints* path) const {
354 if (predicate(*this)) {
355 dest.push_back(this->shared_from_this());
356 return;
357 }
358 // hide this->child from predicate, delegate calling to its child
359 container->forEachChild([&](const Translation<3>& child) { child.getObjectsToVec(predicate, dest, path); }, path);
360}
361
362 void Lattice::getPositionsToVec(const GeometryObject::Predicate& predicate,
363 std::vector<DVec>& dest,
364 const PathHints* path) const {
365 if (predicate(*this)) {
366 dest.push_back(Primitive<3>::ZERO_VEC);
367 return;
368 }
369 // hide this->child from predicate, delegate calling to its child
370 container->forEachChild([&](const Translation<3>& child) { child.getPositionsToVec(predicate, dest, path); }, path);
371}
372
373 GeometryObject::Subtree Lattice::getPathsTo(const GeometryObject& el, const PathHints* path) const {
374 if (this == &el) return this->shared_from_this();
375 GeometryObject::Subtree result = container->getPathsTo(el, path);
376 if (result.object) result.object = this->shared_from_this();
377 return result;
378}
379
380 void Lattice::refillContainer() {
381 container->clear();
382
383 std::map<int, std::set<int>>
384 result; // coordinates of all (x, y) points inside the polygon in the map: y -> set of x
385 YEnds ends;
386 SegmentsIterator segment(this->segments);
387 while (segment.next()) {
388 if (segment.first.c1 == segment.second.c1) {
389 std::set<int>& dst = result[segment.first.c1];
390 if (segment.first.c0 > segment.second.c0) std::swap(segment.first.c0, segment.second.c0);
391 for (int x = segment.first.c0; x <= segment.second.c0; ++x)
392 dst.insert(x); // we immediately add all points which lie on side
393 } else {
394 result[segment.first.c1].insert(segment.first.c0); // we immediately add all vertices
395 result[segment.second.c1].insert(segment.second.c0); // we immediately add all vertices
396
397 LateralVec<int> low_y, hi_y;
398 if (segment.first.c1 > segment.second.c1) {
399 low_y = segment.second;
400 hi_y = segment.first;
401 } else {
402 low_y = segment.first;
403 hi_y = segment.second;
404 }
405 int dx = hi_y.c0 - low_y.c0;
406 int dy = hi_y.c1 - low_y.c1; // dy > 0
407 for (int y = low_y.c1 + 1; y < hi_y.c1; ++y) {
408 // x = l/m + low_y.c0
409 int l = dx * (y - low_y.c1);
410 int x = l / dy + low_y.c0;
411 int rem = l % dy;
412 if (rem == 0) { // x,y is exactly on side
413 result[y].insert(x); // so we immediately add it
414 ends.add_d(2 * x, y);
415 } else {
416 // here: real x = x + rem / dy and dy>0
417 ends.add_d(rem > 0 ? 2 * x + 1 : 2 * x - 1, y);
418 }
419 }
420 ends.add(hi_y); // add point with higher y to ends
421 }
422 }
423 for (std::pair<const int, std::set<int>>& line : ends.coords) {
424 assert(line.second.size() % 2 == 0);
425 std::set<int>& dst = result[line.first];
426 for (std::set<int>::iterator dblx_it = line.second.begin(); dblx_it != line.second.end(); ++dblx_it) {
427 // we can exclude ends of the segments as they are already included
428 int beg = (*dblx_it + 1) / 2;
429 ++dblx_it; // this is ok because of line.second has even length
430 int end = (*dblx_it + 1) / 2;
431 // add all points from range [beg, end)
432 while (beg != end) {
433 dst.insert(beg);
434 ++beg;
435 }
436 }
437 }
438
439 for (auto& p : result) { // p is pair: set of x's, and one y
440 for (auto x : p.second) {
441 container->addUnsafe(this->_child, x * vec0 + p.first * vec1);
442 }
443 }
444}
445
446 void Lattice::addPointsAlongToSet(std::set<double>& points,
447 Primitive<3>::Direction direction,
448 unsigned max_steps,
449 double min_step_size) const {
450 if (!this->hasChild()) return;
451 container->addPointsAlongToSet(points, direction, this->max_steps ? this->max_steps : max_steps,
452 this->min_step_size ? this->min_step_size : min_step_size);
453}
454
455 void Lattice::addLineSegmentsToSet(std::set<typename GeometryObjectD<3>::LineSegment>& segments,
456 unsigned max_steps,
457 double min_step_size) const {
458 if (!this->hasChild()) return;
459 container->addLineSegmentsToSet(segments, this->max_steps ? this->max_steps : max_steps,
460 this->min_step_size ? this->min_step_size : min_step_size);
461}
462
463 void Lattice::writeXMLAttr(XMLWriter::Element& dest_xml_object, const AxisNames& axes) const {
464 GeometryObjectTransform<3>::writeXMLAttr(dest_xml_object, axes);
465 if (vec0.lon() != 0.) dest_xml_object.attr("a" + axes.getNameForLong(), vec0.lon());
466 if (vec0.tran() != 0.) dest_xml_object.attr("a" + axes.getNameForTran(), vec0.tran());
467 if (vec0.vert() != 0.) dest_xml_object.attr("a" + axes.getNameForVert(), vec0.vert());
468 if (vec1.lon() != 0.) dest_xml_object.attr("b" + axes.getNameForLong(), vec1.lon());
469 if (vec1.tran() != 0.) dest_xml_object.attr("b" + axes.getNameForTran(), vec1.tran());
470 if (vec1.vert() != 0.) dest_xml_object.attr("b" + axes.getNameForVert(), vec1.vert());
471}
472
473#define LATTICE_XML_SEGMENTS_TAG_NAME "segments"
474
475 void Lattice::writeXMLChildren(XMLWriter::Element& dest_xml_object,
476 GeometryObject::WriteXMLCallback& write_cb,
477 const AxisNames& axes) const {
478 { // write <segments>
479 XMLElement segments_tag(dest_xml_object, LATTICE_XML_SEGMENTS_TAG_NAME);
480 bool first = true;
481 for (const std::vector<LateralVec<int>>& s : segments) {
482 if (!first) segments_tag.writeText(" ^\n");
483 first = false;
484 bool first_point = true;
485 for (LateralVec<int> p : s) {
486 if (!first_point) {
487 segments_tag.writeText("; ");
488 }
489 first_point = false;
490 segments_tag.writeText(p[0]).writeText(' ').writeText(p[1]);
491 }
492 }
493 } // </segments>
494 // write child:
495 GeometryObjectTransform<3>::writeXML(dest_xml_object, write_cb, axes);
496}
497
498 void Lattice::setSegments(std::vector<std::vector<LateralVec<int>>> new_segments) {
499 this->segments = std::move(new_segments);
500 refillContainer();
501}
502
503 shared_ptr<GeometryObject> read_lattice(GeometryReader& reader) {
504 GeometryReader::SetExpectedSuffix suffixSetter(reader, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D);
505 auto result = plask::make_shared<Lattice>();
506 result->vec0.lon() = reader.source.getAttribute("a" + reader.getAxisLongName(), 0.);
507 result->vec0.tran() = reader.source.getAttribute("a" + reader.getAxisTranName(), 0.);
508 result->vec0.vert() = reader.source.getAttribute("a" + reader.getAxisVertName(), 0.);
509 result->vec1.lon() = reader.source.getAttribute("b" + reader.getAxisLongName(), 0.);
510 result->vec1.tran() = reader.source.getAttribute("b" + reader.getAxisTranName(), 0.);
511 result->vec1.vert() = reader.source.getAttribute("b" + reader.getAxisVertName(), 0.);
512 reader.source.requireTag(LATTICE_XML_SEGMENTS_TAG_NAME);
513 std::string segments = reader.source.requireTextInCurrentTag();
514 boost::tokenizer<boost::char_separator<char>> tokens(segments, boost::char_separator<char>(" \t\n\r", ";^"));
515 result->segments.emplace_back();
516 int cords_in_current_point = 0;
517 for (const std::string& t : tokens) {
518 if (t == ";" || t == "^") { // end of point or segment
519 if (cords_in_current_point != 2) throw Exception("each point must have two coordinates");
520 cords_in_current_point = 0;
521 if (t == "^") // end of segment, add new one
522 result->segments.emplace_back();
523 } else { // end of point coordinate
524 if (cords_in_current_point == 2)
525 throw Exception("end of point (\";\") or segment (\"^\") was expected, but got \"{0}\"", t);
526 if (cords_in_current_point == 0) result->segments.back().emplace_back();
527 result->segments.back().back()[cords_in_current_point++] = boost::lexical_cast<int>(t);
528 }
529 }
530 result->setChild(reader.readExactlyOneChild<typename Lattice::ChildType>());
531 result->refillContainer();
532 return result;
533}
534
535 static GeometryReader::RegisterObjectReader lattice_reader(PLASK_LATTICE_NAME, read_lattice);
536
537} // namespace plask