IgANet
IGAnets - Isogeometric Analysis Networks
Loading...
Searching...
No Matches
multipatch.hpp
Go to the documentation of this file.
1
15#pragma once
16
18
19#include <string_view>
20
21namespace iganet {
22
24template <typename Patch> class PatchInterface {
25public:
32 PatchInterface(std::shared_ptr<Patch> firstPatch, enum side firstSide,
33 std::shared_ptr<Patch> secondPatch, enum side secondSide)
34 : patches_{std::move(firstPatch), std::move(secondPatch)},
36 if (!patches_[0] || !patches_[1])
37 throw std::invalid_argument("An interface requires two valid patches");
38 if (sides_[0] == none || sides_[1] == none)
39 throw std::invalid_argument("An interface requires two valid sides");
40 }
41
45 Patch &patch(std::size_t endpoint) {
46 assert(endpoint < patches_.size());
47 return *patches_[endpoint];
48 }
49
53 const Patch &patch(std::size_t endpoint) const {
54 assert(endpoint < patches_.size());
55 return *patches_[endpoint];
56 }
57
61 const std::shared_ptr<Patch> &patchPtr(std::size_t endpoint) const {
62 assert(endpoint < patches_.size());
63 return patches_[endpoint];
64 }
65
69 enum side side(std::size_t endpoint) const {
70 assert(endpoint < sides_.size());
71 return sides_[endpoint];
72 }
73
77 Patch &firstPatch() { return patch(0); }
80 const Patch &firstPatch() const { return patch(0); }
83 Patch &secondPatch() { return patch(1); }
86 const Patch &secondPatch() const { return patch(1); }
89 enum side firstSide() const { return side(0); }
92 enum side secondSide() const { return side(1); }
94
95private:
96 std::array<std::shared_ptr<Patch>, 2> patches_;
97 std::array<enum side, 2> sides_;
98};
99
105template <typename Patch> class MultiPatch {
106
107public:
110
112 MultiPatch() = default;
113
116 MultiPatch(const MultiPatch &other)
117 : patches_(other.patches_), interfaces_(other.interfaces_) {}
118
121 MultiPatch(MultiPatch &&other) noexcept {
122 patches_.swap(other.patches_);
123 interfaces_.swap(other.interfaces_);
124 }
125
126public:
129 auto begin() { return patches_.begin(); }
130
134 auto begin() const { return patches_.begin(); }
137 auto cbegin() const noexcept { return patches_.cbegin(); }
139
142 auto end() { return patches_.end(); }
143
147 auto end() const { return patches_.end(); }
150 auto cend() const noexcept { return patches_.cend(); }
152
155 auto rbegin() { return patches_.rbegin(); }
156
160 auto rbegin() const { return patches_.rbegin(); }
163 auto crbegin() const noexcept { return patches_.crbegin(); }
165
168 auto rend() { return patches_.rend(); }
169
173 auto rend() const { return patches_.rend(); }
176 auto crend() const noexcept { return patches_.crend(); }
178
179public:
182 [[nodiscard]] std::size_t npatches() const { return patches_.size(); }
183
186 [[nodiscard]] std::size_t ninterfaces() const { return interfaces_.size(); }
187
190 [[nodiscard]] std::size_t nboundaries() const { return patches_.size(); }
191
192public:
197 std::size_t addPatch(std::shared_ptr<Patch> patch) {
198 std::size_t index = patches_.size();
199 patches_.push_back(patch);
200 return index;
201 }
202
206 std::size_t addPatch(std::unique_ptr<Patch> patch) {
207 std::size_t index = patches_.size();
208 patches_.push_back(std::shared_ptr<Patch>(std::move(patch)));
209 return index;
210 }
212
219 std::size_t addInterface(std::size_t firstPatch, enum side firstSide,
220 std::size_t secondPatch, enum side secondSide) {
221 assert(firstPatch < patches_.size());
222 assert(secondPatch < patches_.size());
223 return addInterface(patches_[firstPatch], firstSide, patches_[secondPatch],
224 secondSide);
225 }
226
233 std::size_t addInterface(std::shared_ptr<Patch> firstPatch,
234 enum side firstSide,
235 std::shared_ptr<Patch> secondPatch,
236 enum side secondSide) {
237 if (std::find(patches_.begin(), patches_.end(), firstPatch) ==
238 patches_.end() ||
239 std::find(patches_.begin(), patches_.end(), secondPatch) ==
240 patches_.end())
241 throw std::invalid_argument(
242 "Interface patches must belong to the MultiPatch");
243
244 const std::size_t index = interfaces_.size();
245 interfaces_.emplace_back(std::move(firstPatch), firstSide,
246 std::move(secondPatch), secondSide);
247 return index;
248 }
249
253 std::size_t addInterface(interface_type patchInterface) {
254 return addInterface(patchInterface.patchPtr(0), patchInterface.side(0),
255 patchInterface.patchPtr(1), patchInterface.side(1));
256 }
257
260 void removeInterface(std::size_t index) {
261 assert(index < interfaces_.size());
262 interfaces_.erase(interfaces_.begin() + index);
263 }
264
266 void clear() {
267 interfaces_.clear();
268 patches_.clear();
269 }
270
274 Patch &patch(std::size_t index) {
275 assert(index < patches_.size());
276 return *patches_[index];
277 }
278
282 const Patch &patch(std::size_t index) const {
283 assert(index < patches_.size());
284 return *patches_[index];
285 }
286
290 std::vector<std::shared_ptr<Patch>> &patches() { return patches_; }
293 const std::vector<std::shared_ptr<Patch>> &patches() const {
294 return patches_;
295 }
297
301 interface_type &interface(std::size_t index) {
302 assert(index < interfaces_.size());
303 return interfaces_[index];
304 }
305
309 const interface_type &interface(std::size_t index) const {
310 assert(index < interfaces_.size());
311 return interfaces_[index];
312 }
313
317 std::vector<interface_type> &interfaces() { return interfaces_; }
320 const std::vector<interface_type> &interfaces() const { return interfaces_; }
322
327 std::size_t findPatchIndex(const Patch &patch) const {
328 return findPatchIndex(&patch);
329 }
330
334 std::size_t findPatchIndex(const Patch *patch) const {
335 auto it = std::find_if(
336 patches_.begin(), patches_.end(),
337 [patch](const auto &candidate) { return candidate.get() == patch; });
338 if (it == patches_.end())
339 throw std::runtime_error("Did not find the patch index");
340
341 return it - patches_.begin();
342 }
344
349 std::size_t findInterfaceIndex(const interface_type &patchInterface) const {
350 return findInterfaceIndex(&patchInterface);
351 }
352
356 std::size_t findInterfaceIndex(const interface_type *patchInterface) const {
357 auto it = std::find_if(interfaces_.begin(), interfaces_.end(),
358 [patchInterface](const auto &candidate) {
359 return &candidate == patchInterface;
360 });
361 if (it == interfaces_.end())
362 throw std::runtime_error("Did not find the patch interface index");
363
364 return it - interfaces_.begin();
365 }
367
373 template <typename P = Patch>
374 requires requires { typename P::value_type; }
375 void load(const std::string &filename, const std::string &key = "multipatch",
378 torch::serialize::InputArchive archive;
379 archive.load_from(filename);
380 read(archive, key, options);
381 }
382
389 template <typename P = Patch>
390 requires requires { typename P::value_type; }
391 torch::serialize::InputArchive &read(torch::serialize::InputArchive &archive,
392 const std::string &key = "multipatch",
395 torch::Tensor data;
396 archive.read(key + ".json", data);
397 data = data.to(torch::kCPU, torch::kUInt8).contiguous();
398 const auto *begin =
399 reinterpret_cast<const char *>(data.data_ptr<uint8_t>());
400 from_json(nlohmann::json::parse(std::string(begin, begin + data.numel())),
401 options);
402 return archive;
403 }
404
408 void save(const std::string &filename,
409 const std::string &key = "multipatch") const {
410 torch::serialize::OutputArchive archive;
411 write(archive, key).save_to(filename);
412 }
413
418 torch::serialize::OutputArchive &
419 write(torch::serialize::OutputArchive &archive,
420 const std::string &key = "multipatch") const {
421 const std::string serialized = to_json().dump();
422 const auto data =
423 torch::from_blob(const_cast<char *>(serialized.data()),
424 {static_cast<int64_t>(serialized.size())},
425 torch::TensorOptions{}.dtype(torch::kUInt8))
426 .clone();
427 archive.write(key + ".json", data);
428 return archive;
429 }
430
437 template <typename P = Patch>
438 requires requires { typename P::value_type; }
439 bool
440 isclose(const MultiPatch &other,
441 typename P::value_type rtol = typename P::value_type{1e-5},
442 typename P::value_type atol = typename P::value_type{1e-8}) const {
443 return jsonIsClose(to_json(), other.to_json(), rtol, atol);
444 }
445
449 bool operator==(const MultiPatch &other) const {
450 return to_json() == other.to_json();
451 }
452
456 bool operator!=(const MultiPatch &other) const { return !(*this == other); }
457
460 void pretty_print(std::ostream &os) const noexcept {
461 os << "MultiPatch(\nparDim = " << Patch::parDim()
462 << ", npatches = " << npatches() << ", ninterfaces = " << ninterfaces()
463 << "\n";
464 for (std::size_t patchIndex = 0; patchIndex < patches_.size();
465 ++patchIndex) {
466 const auto json = patches_[patchIndex]->to_json();
467 os << "patch[" << patchIndex << "] = {geoDim = " << json["geoDim"]
468 << ", degrees = " << json["degrees"] << "}\n";
469 }
470 for (std::size_t interfaceIndex = 0; interfaceIndex < interfaces_.size();
471 ++interfaceIndex) {
472 const auto &patchInterface = interfaces_[interfaceIndex];
473 os << "interface[" << interfaceIndex << "] = {patch "
474 << findPatchIndex(patchInterface.patchPtr(0).get()) << ", side "
475 << static_cast<short_t>(patchInterface.side(0)) << " <-> patch "
476 << findPatchIndex(patchInterface.patchPtr(1).get()) << ", side "
477 << static_cast<short_t>(patchInterface.side(1)) << "}\n";
478 }
479 os << ")";
480 }
481
484 [[nodiscard]] nlohmann::json to_json() const {
485 nlohmann::json json;
486 json["parDim"] = Patch::parDim();
487 json["patches"] = nlohmann::json::array();
488 json["interfaces"] = nlohmann::json::array();
489 json["boundaries"] = nlohmann::json::array();
490
491 for (const auto &patch : patches_)
492 json["patches"].push_back(patch->to_json());
493
494 for (const auto &patchInterface : interfaces_) {
495 nlohmann::json interfaceJson;
496 interfaceJson["patches"] = {
497 findPatchIndex(patchInterface.patchPtr(0).get()),
498 findPatchIndex(patchInterface.patchPtr(1).get())};
499 interfaceJson["sides"] = {static_cast<short_t>(patchInterface.side(0)),
500 static_cast<short_t>(patchInterface.side(1))};
501
502 const short_t firstAxis =
503 (static_cast<short_t>(patchInterface.side(0)) - 1) / 2;
504 const short_t secondAxis =
505 (static_cast<short_t>(patchInterface.side(1)) - 1) / 2;
506 interfaceJson["direction"] = nlohmann::json::array();
507 interfaceJson["orientation"] = nlohmann::json::array();
508 for (short_t axis = 0; axis < Patch::parDim(); ++axis) {
509 short_t mappedAxis = axis;
510 if (axis == firstAxis)
511 mappedAxis = secondAxis;
512 else if (axis == secondAxis)
513 mappedAxis = firstAxis;
514 interfaceJson["direction"].push_back(mappedAxis);
515 interfaceJson["orientation"].push_back(1);
516 }
517 json["interfaces"].push_back(std::move(interfaceJson));
518 }
519
520 for (std::size_t patchIndex = 0; patchIndex < patches_.size();
521 ++patchIndex) {
522 for (short_t patchSide = 1; patchSide <= 2 * Patch::parDim();
523 ++patchSide) {
524 const bool isInterface =
525 std::any_of(interfaces_.begin(), interfaces_.end(),
526 [&](const auto &patchInterface) {
527 return (patchInterface.patchPtr(0).get() ==
528 patches_[patchIndex].get() &&
529 patchInterface.side(0) == patchSide) ||
530 (patchInterface.patchPtr(1).get() ==
531 patches_[patchIndex].get() &&
532 patchInterface.side(1) == patchSide);
533 });
534 if (!isInterface)
535 json["boundaries"].push_back(
536 {{"patch", patchIndex}, {"side", patchSide}});
537 }
538 }
539 return json;
540 }
541
547 template <typename P = Patch>
548 requires requires { typename P::value_type; }
549 MultiPatch &from_json(const nlohmann::json &json,
552 if (json.at("parDim").get<short_t>() != Patch::parDim())
553 throw std::runtime_error(
554 "MultiPatch JSON provides an incompatible parametric dimension");
555
556 const auto &patchJson = json.at("patches");
557 if (!patchJson.is_array() ||
558 (!patches_.empty() && patchJson.size() != patches_.size()))
559 throw std::runtime_error(
560 "MultiPatch JSON patch count does not match the patch container");
561
562 auto parsedPatches = patches_;
563 const bool createPatches = parsedPatches.empty();
564 if (createPatches) {
565 parsedPatches.reserve(patchJson.size());
566 for (const auto &item : patchJson) {
567 try {
568 parsedPatches.push_back(
569 createUniformBSpline<typename Patch::value_type, Patch::geoDim(),
570 Patch::parDim()>(item, options));
571 } catch (const std::runtime_error &) {
572 parsedPatches.push_back(
573 createNonUniformBSpline<typename Patch::value_type,
574 Patch::geoDim(), Patch::parDim()>(
575 item, options));
576 }
577 }
578 }
579
580 const auto &interfaceJson = json.at("interfaces");
581 if (!interfaceJson.is_array())
582 throw std::runtime_error("MultiPatch JSON interfaces must be an array");
583
584 std::vector<interface_type> parsedInterfaces;
585 parsedInterfaces.reserve(interfaceJson.size());
586 for (const auto &item : interfaceJson) {
587 const auto patchIndices = item.at("patches").get<std::array<size_t, 2>>();
588 const auto sides = item.at("sides").get<std::array<short_t, 2>>();
589 if (patchIndices[0] >= parsedPatches.size() ||
590 patchIndices[1] >= parsedPatches.size() || sides[0] <= none ||
591 sides[0] > 2 * Patch::parDim() || sides[1] <= none ||
592 sides[1] > 2 * Patch::parDim())
593 throw std::runtime_error("MultiPatch JSON has an invalid interface");
594
595 if (!item.at("direction").is_array() ||
596 item.at("direction").size() != Patch::parDim() ||
597 !item.at("orientation").is_array() ||
598 item.at("orientation").size() != Patch::parDim())
599 throw std::runtime_error(
600 "MultiPatch JSON has invalid interface orientation data");
601
602 parsedInterfaces.emplace_back(
603 parsedPatches[patchIndices[0]], static_cast<enum side>(sides[0]),
604 parsedPatches[patchIndices[1]], static_cast<enum side>(sides[1]));
605 }
606
607 if (!createPatches)
608 for (std::size_t patchIndex = 0; patchIndex < parsedPatches.size();
609 ++patchIndex)
610 parsedPatches[patchIndex]->from_json(patchJson[patchIndex]);
611
612 patches_ = std::move(parsedPatches);
613 interfaces_ = std::move(parsedInterfaces);
614 return *this;
615 }
616
622 [[nodiscard]] pugi::xml_document
623 to_xml(int id = 0, const std::string &label = "", int index = -1) const {
624 pugi::xml_document doc;
625 pugi::xml_node root = doc.append_child("xml");
626 to_xml(root, id, label, index);
627 return doc;
628 }
629
636 pugi::xml_node &to_xml(pugi::xml_node &root, int id = 0,
637 const std::string &label = "", int index = -1) const {
638 for (std::size_t patchIndex = 0; patchIndex < patches_.size(); ++patchIndex)
639 patches_[patchIndex]->to_xml(root, static_cast<int>(patchIndex));
640
641 pugi::xml_node multiPatch = root.append_child("MultiPatch");
642 multiPatch.append_attribute("parDim") = Patch::parDim();
643 multiPatch.append_attribute("id") = id;
644 if (!label.empty())
645 multiPatch.append_attribute("label") = label.c_str();
646 if (index >= 0)
647 multiPatch.append_attribute("index") = index;
648
649 pugi::xml_node patches = multiPatch.append_child("patches");
650 patches.append_attribute("type") = "id_range";
651 if (!patches_.empty()) {
652 const std::string range =
653 "0 " + std::to_string(static_cast<int64_t>(patches_.size()) - 1);
654 patches.append_child(pugi::node_pcdata).set_value(range.c_str());
655 }
656
657 std::stringstream interfaceData;
658 for (const auto &patchInterface : interfaces_) {
659 interfaceData << "\n ";
660 const auto firstPatch = findPatchIndex(patchInterface.patchPtr(0).get());
661 const auto secondPatch = findPatchIndex(patchInterface.patchPtr(1).get());
662 const short_t firstAxis =
663 (static_cast<short_t>(patchInterface.side(0)) - 1) / 2;
664 const short_t secondAxis =
665 (static_cast<short_t>(patchInterface.side(1)) - 1) / 2;
666
667 interfaceData << firstPatch << ' '
668 << static_cast<short_t>(patchInterface.side(0)) << ' '
669 << secondPatch << ' '
670 << static_cast<short_t>(patchInterface.side(1));
671
672 for (short_t axis = 0; axis < Patch::parDim(); ++axis) {
673 short_t mappedAxis = axis;
674 if (axis == firstAxis)
675 mappedAxis = secondAxis;
676 else if (axis == secondAxis)
677 mappedAxis = firstAxis;
678 interfaceData << ' ' << mappedAxis;
679 }
680 for (short_t axis = 0; axis < Patch::parDim(); ++axis)
681 interfaceData << " 1";
682 }
683 if (!interfaces_.empty())
684 interfaceData << "\n ";
685 multiPatch.append_child("interfaces")
686 .append_child(pugi::node_pcdata)
687 .set_value(interfaceData.str().c_str());
688
689 std::stringstream boundaryData;
690 for (std::size_t patchIndex = 0; patchIndex < patches_.size();
691 ++patchIndex) {
692 for (short_t patchSide = 1; patchSide <= 2 * Patch::parDim();
693 ++patchSide) {
694 const bool isInterface =
695 std::any_of(interfaces_.begin(), interfaces_.end(),
696 [&](const auto &patchInterface) {
697 return (patchInterface.patchPtr(0).get() ==
698 patches_[patchIndex].get() &&
699 patchInterface.side(0) == patchSide) ||
700 (patchInterface.patchPtr(1).get() ==
701 patches_[patchIndex].get() &&
702 patchInterface.side(1) == patchSide);
703 });
704 if (!isInterface)
705 boundaryData << "\n " << patchIndex << ' ' << patchSide;
706 }
707 }
708 if (!boundaryData.str().empty())
709 boundaryData << "\n ";
710 multiPatch.append_child("boundary")
711 .append_child(pugi::node_pcdata)
712 .set_value(boundaryData.str().c_str());
713
714 return root;
715 }
716
725 template <typename P = Patch>
726 requires requires { typename P::value_type; }
727 MultiPatch &from_xml(const pugi::xml_document &doc, int id = 0,
728 const std::string &label = "", int index = -1,
731 return from_xml(doc.child("xml"), id, label, index, options);
732 }
733
742 template <typename P = Patch>
743 requires requires { typename P::value_type; }
744 MultiPatch &from_xml(const pugi::xml_node &root, int id = 0,
745 const std::string &label = "", int index = -1,
748 pugi::xml_node multiPatch;
749 for (const auto &candidate : root.children("MultiPatch")) {
750 if ((id >= 0 ? candidate.attribute("id").as_int() == id : true) &&
751 (index >= 0 ? candidate.attribute("index").as_int() == index
752 : true) &&
753 (!label.empty() ? candidate.attribute("label").value() == label
754 : true)) {
755 multiPatch = candidate;
756 break;
757 }
758 }
759 if (!multiPatch)
760 throw std::runtime_error("Did not find the MultiPatch XML node");
761
762 if (multiPatch.attribute("parDim").as_int() != Patch::parDim())
763 throw std::runtime_error(
764 "MultiPatch XML provides an incompatible parametric dimension");
765
766 const pugi::xml_node patchRange = multiPatch.child("patches");
767 if (!patchRange ||
768 std::string_view{patchRange.attribute("type").value()} != "id_range")
769 throw std::runtime_error("MultiPatch XML has no valid patch ID range");
770
771 std::stringstream rangeData(patchRange.child_value());
772 int64_t firstPatch = 0;
773 int64_t lastPatch = -1;
774 if (!(rangeData >> firstPatch >> lastPatch) || firstPatch != 0 ||
775 (!patches_.empty() &&
776 lastPatch + 1 != static_cast<int64_t>(patches_.size())))
777 throw std::runtime_error(
778 "MultiPatch XML patch range does not match the patch container");
779
780 auto parsedPatches = patches_;
781 const bool createPatches = parsedPatches.empty();
782 if (createPatches) {
783 parsedPatches.reserve(static_cast<std::size_t>(lastPatch + 1));
784 for (int64_t patchIndex = 0; patchIndex <= lastPatch; ++patchIndex) {
785 try {
786 parsedPatches.push_back(
787 createUniformBSpline<typename Patch::value_type, Patch::geoDim(),
788 Patch::parDim()>(root, patchIndex, "", -1,
789 options));
790 } catch (const std::runtime_error &) {
791 parsedPatches.push_back(
792 createNonUniformBSpline<typename Patch::value_type,
793 Patch::geoDim(), Patch::parDim()>(
794 root, patchIndex, "", -1, options));
795 }
796 }
797 }
798
799 std::vector<interface_type> parsedInterfaces;
800 const pugi::xml_node interfaceNode = multiPatch.child("interfaces");
801 if (!interfaceNode)
802 throw std::runtime_error("MultiPatch XML has no interfaces node");
803
804 std::stringstream interfaceData(interfaceNode.child_value());
805 std::string line;
806 while (std::getline(interfaceData, line)) {
807 std::stringstream item(line);
808 std::size_t firstPatchIndex;
809 std::size_t secondPatchIndex;
810 short_t firstSide;
811 short_t secondSide;
812 if (!(item >> firstPatchIndex >> firstSide >> secondPatchIndex >>
813 secondSide))
814 continue;
815
816 if (firstPatchIndex >= parsedPatches.size() ||
817 secondPatchIndex >= parsedPatches.size() || firstSide <= none ||
818 firstSide > 2 * Patch::parDim() || secondSide <= none ||
819 secondSide > 2 * Patch::parDim())
820 throw std::runtime_error("MultiPatch XML has an invalid interface");
821
822 short_t topologyEntry;
823 for (short_t entry = 0; entry < 2 * Patch::parDim(); ++entry) {
824 if (!(item >> topologyEntry))
825 throw std::runtime_error(
826 "MultiPatch XML has incomplete interface orientation data");
827 }
828 if (item >> topologyEntry)
829 throw std::runtime_error(
830 "MultiPatch XML has excess interface orientation data");
831
832 parsedInterfaces.emplace_back(
833 parsedPatches[firstPatchIndex], static_cast<enum side>(firstSide),
834 parsedPatches[secondPatchIndex], static_cast<enum side>(secondSide));
835 }
836
837 if (!createPatches)
838 for (std::size_t patchIndex = 0; patchIndex < parsedPatches.size();
839 ++patchIndex)
840 parsedPatches[patchIndex]->from_xml(root, static_cast<int>(patchIndex));
841
842 patches_ = std::move(parsedPatches);
843 interfaces_ = std::move(parsedInterfaces);
844 return *this;
845 }
846
847private:
848 template <typename real_t>
849 static bool jsonIsClose(const nlohmann::json &first,
850 const nlohmann::json &second, real_t rtol,
851 real_t atol) {
852 if (first.type() != second.type())
853 return first.is_number() && second.is_number() &&
854 std::abs(first.template get<real_t>() -
855 second.template get<real_t>()) <=
856 atol + rtol * std::abs(second.template get<real_t>());
857 if (first.is_number())
858 return std::abs(first.template get<real_t>() -
859 second.template get<real_t>()) <=
860 atol + rtol * std::abs(second.template get<real_t>());
861 if (first.is_array()) {
862 if (first.size() != second.size())
863 return false;
864 for (std::size_t i = 0; i < first.size(); ++i)
865 if (!jsonIsClose(first[i], second[i], rtol, atol))
866 return false;
867 return true;
868 }
869 if (first.is_object()) {
870 if (first.size() != second.size())
871 return false;
872 for (const auto &[key, value] : first.items()) {
873 const auto it = second.find(key);
874 if (it == second.end() || !jsonIsClose(value, *it, rtol, atol))
875 return false;
876 }
877 return true;
878 }
879 return first == second;
880 }
881
883 std::vector<std::shared_ptr<Patch>> patches_;
884
886 std::vector<interface_type> interfaces_;
887};
888
894template <typename Patch>
895std::ostream &operator<<(std::ostream &os, const MultiPatch<Patch> &obj) {
896 obj.pretty_print(os);
897 return os;
898}
899
905template <typename Patch>
906torch::serialize::OutputArchive &
907operator<<(torch::serialize::OutputArchive &archive,
908 const MultiPatch<Patch> &obj) {
909 return obj.write(archive);
910}
911
917template <typename Patch>
918torch::serialize::InputArchive &
919operator>>(torch::serialize::InputArchive &archive, MultiPatch<Patch> &obj) {
920 return obj.read(archive);
921}
922
923} // namespace iganet
Boundary treatment.
Multi-patch container class.
Definition multipatch.hpp:105
MultiPatch & from_xml(const pugi::xml_node &root, int id=0, const std::string &label="", int index=-1, Options< typename P::value_type > options=Options< typename P::value_type >{})
Updates the multi-patch object from an XML node.
Definition multipatch.hpp:744
void pretty_print(std::ostream &os) const noexcept
Prints a human-readable representation of the multi-patch object.
Definition multipatch.hpp:460
void removeInterface(std::size_t index)
Removes a single interface.
Definition multipatch.hpp:260
MultiPatch(const MultiPatch &other)
Copy constructor.
Definition multipatch.hpp:116
auto rbegin() const
Returns a reverse const-iterator to the patches.
Definition multipatch.hpp:160
std::size_t findPatchIndex(const Patch *patch) const
Provides the findPatchIndex operation.
Definition multipatch.hpp:334
auto rend()
Returns a reverse iterator to the end of the patches.
Definition multipatch.hpp:168
interface_type & interface(std::size_t index)
Returns a non-constant reference to a single interface.
Definition multipatch.hpp:301
void clear()
Removes all patches.
Definition multipatch.hpp:266
std::size_t npatches() const
Returns the number of patches.
Definition multipatch.hpp:182
bool operator!=(const MultiPatch &other) const
Returns true if patches or topology differ.
Definition multipatch.hpp:456
bool operator==(const MultiPatch &other) const
Returns true if patches and topology are exactly equal.
Definition multipatch.hpp:449
auto crbegin() const noexcept
Provides the crbegin operation.
Definition multipatch.hpp:163
pugi::xml_node & to_xml(pugi::xml_node &root, int id=0, const std::string &label="", int index=-1) const
Appends the multi-patch object to an XML node.
Definition multipatch.hpp:636
auto end() const
Returns a const-iterator to the end of the patches.
Definition multipatch.hpp:147
std::vector< interface_type > interfaces_
Vector of patch-interface objects.
Definition multipatch.hpp:886
MultiPatch(MultiPatch &&other) noexcept
Move constructor.
Definition multipatch.hpp:121
std::size_t findPatchIndex(const Patch &patch) const
Returns the index of a given single patch.
Definition multipatch.hpp:327
pugi::xml_document to_xml(int id=0, const std::string &label="", int index=-1) const
Returns the multi-patch object as an XML document.
Definition multipatch.hpp:623
std::size_t addInterface(interface_type patchInterface)
Adds an interface object.
Definition multipatch.hpp:253
MultiPatch & from_xml(const pugi::xml_document &doc, int id=0, const std::string &label="", int index=-1, Options< typename P::value_type > options=Options< typename P::value_type >{})
Updates the multi-patch object from an XML document.
Definition multipatch.hpp:727
const std::vector< std::shared_ptr< Patch > > & patches() const
Provides the patches operation.
Definition multipatch.hpp:293
auto begin()
Returns an iterator to the patches.
Definition multipatch.hpp:129
std::size_t addPatch(std::shared_ptr< Patch > patch)
Adds a single patch.
Definition multipatch.hpp:197
bool isclose(const MultiPatch &other, typename P::value_type rtol=typename P::value_type{1e-5}, typename P::value_type atol=typename P::value_type{1e-8}) const
Returns true if patches and topology are close up to tolerances.
Definition multipatch.hpp:440
void load(const std::string &filename, const std::string &key="multipatch", Options< typename P::value_type > options=Options< typename P::value_type >{})
Loads the multi-patch object from a Torch archive file.
Definition multipatch.hpp:375
const Patch & patch(std::size_t index) const
Returns a constant reference to a single patch.
Definition multipatch.hpp:282
const std::vector< interface_type > & interfaces() const
Provides the interfaces operation.
Definition multipatch.hpp:320
std::size_t ninterfaces() const
Returns the number of interfaces.
Definition multipatch.hpp:186
auto rend() const
Returns a reverse const-iterator to the end of the patches.
Definition multipatch.hpp:173
std::vector< interface_type > & interfaces()
Returns the interfaces for range-based iteration.
Definition multipatch.hpp:317
std::size_t addInterface(std::size_t firstPatch, enum side firstSide, std::size_t secondPatch, enum side secondSide)
Adds an interface between two patches identified by index.
Definition multipatch.hpp:219
std::vector< std::shared_ptr< Patch > > patches_
Vector of single-patch objects.
Definition multipatch.hpp:883
MultiPatch()=default
Default constructor.
void save(const std::string &filename, const std::string &key="multipatch") const
Saves the multi-patch object to a Torch archive file.
Definition multipatch.hpp:408
nlohmann::json to_json() const
Returns the multi-patch object as a JSON object.
Definition multipatch.hpp:484
torch::serialize::OutputArchive & write(torch::serialize::OutputArchive &archive, const std::string &key="multipatch") const
Writes the multi-patch object into a Torch output archive.
Definition multipatch.hpp:419
std::size_t addPatch(std::unique_ptr< Patch > patch)
Provides the addPatch operation.
Definition multipatch.hpp:206
std::size_t findInterfaceIndex(const interface_type &patchInterface) const
Returns the index of a given patch interface.
Definition multipatch.hpp:349
auto rbegin()
Returns a reverse iterator to the patches.
Definition multipatch.hpp:155
std::size_t findInterfaceIndex(const interface_type *patchInterface) const
Provides the findInterfaceIndex operation.
Definition multipatch.hpp:356
std::vector< std::shared_ptr< Patch > > & patches()
Returns a reference to the vector of patches.
Definition multipatch.hpp:290
auto begin() const
Returns a const-iterator to the patches.
Definition multipatch.hpp:134
std::size_t nboundaries() const
Returns the number of outer boundaries.
Definition multipatch.hpp:190
static bool jsonIsClose(const nlohmann::json &first, const nlohmann::json &second, real_t rtol, real_t atol)
Definition multipatch.hpp:849
torch::serialize::InputArchive & read(torch::serialize::InputArchive &archive, const std::string &key="multipatch", Options< typename P::value_type > options=Options< typename P::value_type >{})
Reads the multi-patch object from a Torch input archive.
Definition multipatch.hpp:391
MultiPatch & from_json(const nlohmann::json &json, Options< typename P::value_type > options=Options< typename P::value_type >{})
Updates the multi-patch object from a JSON object.
Definition multipatch.hpp:549
auto cbegin() const noexcept
Provides the cbegin operation.
Definition multipatch.hpp:137
Patch & patch(std::size_t index)
Returns a non-constant reference to a single patch.
Definition multipatch.hpp:274
auto crend() const noexcept
Provides the crend operation.
Definition multipatch.hpp:176
auto cend() const noexcept
Provides the cend operation.
Definition multipatch.hpp:150
const interface_type & interface(std::size_t index) const
Returns a constant reference to a single interface.
Definition multipatch.hpp:309
auto end()
Returns an iterator to the end of the patches.
Definition multipatch.hpp:142
std::size_t addInterface(std::shared_ptr< Patch > firstPatch, enum side firstSide, std::shared_ptr< Patch > secondPatch, enum side secondSide)
Adds an interface between two patches.
Definition multipatch.hpp:233
The Options class handles the automated determination of dtype from the template argument and the sel...
Definition options.hpp:47
Connection between two patch sides.
Definition multipatch.hpp:24
std::array< enum side, 2 > sides_
Definition multipatch.hpp:97
const Patch & firstPatch() const
Provides the firstPatch operation.
Definition multipatch.hpp:80
const Patch & patch(std::size_t endpoint) const
Returns one of the two patches.
Definition multipatch.hpp:53
std::array< std::shared_ptr< Patch >, 2 > patches_
Definition multipatch.hpp:96
Patch & secondPatch()
Provides the secondPatch operation.
Definition multipatch.hpp:83
const std::shared_ptr< Patch > & patchPtr(std::size_t endpoint) const
Returns the shared pointer to one of the two patches.
Definition multipatch.hpp:61
Patch & patch(std::size_t endpoint)
Returns one of the two patches.
Definition multipatch.hpp:45
const Patch & secondPatch() const
Provides the secondPatch operation.
Definition multipatch.hpp:86
PatchInterface(std::shared_ptr< Patch > firstPatch, enum side firstSide, std::shared_ptr< Patch > secondPatch, enum side secondSide)
Constructor.
Definition multipatch.hpp:32
Patch & firstPatch()
Named endpoint accessors.
Definition multipatch.hpp:77
enum side firstSide() const
Provides the firstSide operation.
Definition multipatch.hpp:89
enum side secondSide() const
Provides the secondSide operation.
Definition multipatch.hpp:92
enum side side(std::size_t endpoint) const
Returns the side of one of the two patches.
Definition multipatch.hpp:69
Definition core.hpp:73
std::shared_ptr< iganet::BSplinePatch< real_t, GeoDim, ParDim > > createUniformBSpline(const std::array< iganet::short_t, ParDim > &degrees, const std::array< int64_t, ParDim > &ncoeffs, enum iganet::init init=iganet::init::greville, iganet::Options< real_t > options=iganet::Options< real_t >{})
Create tensor-product uniform B-spline.
Definition bspline.hpp:7729
std::shared_ptr< iganet::BSplinePatch< real_t, GeoDim, ParDim > > createNonUniformBSpline(const std::array< iganet::short_t, ParDim > &degrees, const std::array< int64_t, ParDim > &ncoeffs, enum iganet::init init=iganet::init::greville, iganet::Options< real_t > options=iganet::Options< real_t >{})
Create tensor-product non-uniform B-spline.
Definition bspline.hpp:7823
std::ostream & operator<<(std::ostream &os, const MemoryDebugger< id > &obj)
Prints a memory debugger object.
Definition memory.hpp:145
torch::serialize::InputArchive & operator>>(torch::serialize::InputArchive &archive, UniformBSplineCore< real_t, GeoDim, Degrees... > &obj)
Deserializes a B-spline object.
Definition bspline.hpp:3274
side
Identifiers for topological sides.
Definition boundary.hpp:25
@ none
Definition boundary.hpp:38
short int short_t
Signed short integer type used by IgANet's compact enumerations.
Definition core.hpp:76
STL namespace.