33#include <iganet/utils/jit.hpp>
43#define GENERATE_EXPR_SEQ (curl)(div)(grad)(hess)(jac)(lapl)
49#define GENERATE_IEXPR_SEQ (icurl)(idiv)(igrad)(ihess)(ijac)(ilapl)
53using namespace literals;
54using utils::operator+;
117 { t.find_knot_indices(x) };
124 { t.find_coeff_indices(x) };
229 public BSplinePatch<real_t, GeoDim, sizeof...(Degrees)> {
231 template <
typename BSplineCore>
246 static constexpr const std::array<short_t, parDim_>
degrees_ = {Degrees...};
283 std::make_signed_t<short_t> degree_elevate = 0>
284 using derived_type = BSpline<real_t, GeoDim, (Degrees + degree_elevate)...>;
288 template <std::make_
signed_t<
short_t> degree_elevate = 0>
299 template <
typename other_t>
306 [[nodiscard]]
inline torch::Device
device() const noexcept
override {
318 [[nodiscard]]
inline torch::Dtype
dtype() const noexcept
override {
324 [[nodiscard]]
inline torch::Layout
layout() const noexcept
override {
342 [[nodiscard]]
inline bool is_sparse() const noexcept
override {
348 inline static constexpr bool is_uniform() noexcept {
return true; }
443 throw std::runtime_error(
"Invalid number of coefficients");
450 .to(
options.requires_grad(
false))
451 .requires_grad_(
options.requires_grad());
484 template <
typename other_t>
497 .to(
options.requires_grad(
false))
498 .requires_grad_(
options.requires_grad());
504 .to(
options.requires_grad(
false))
505 .requires_grad_(
options.requires_grad());
538 inline static constexpr const std::array<short_t, parDim_> &
568 [[nodiscard]]
inline const torch::Tensor &
knots(
short_t i)
const noexcept {
594 inline const std::array<int64_t, parDim_> &
nknots() const noexcept {
623 [[nodiscard]]
inline const torch::Tensor &
coeffs(
short_t i)
const noexcept {
688 inline const std::array<int64_t, parDim_> &
ncoeffs() const noexcept {
707 template <std::size_t... Is>
708 inline torch::Tensor
as_tensor_(std::index_sequence<Is...>)
const noexcept {
709 return torch::cat({
coeffs_[Is]...});
716 [[nodiscard]]
inline torch::Tensor
as_tensor() const noexcept
override {
717 return as_tensor_(std::make_index_sequence<geoDim_>{});
724 template <std::size_t... Is>
727 const torch::Tensor &tensor)
noexcept {
743 return from_tensor_(std::make_index_sequence<geoDim_>{}, tensor);
789 options_.requires_grad(
false).template dtype<int64_t>())
796 options_.requires_grad(
false).template dtype<int64_t>())
800 auto indices = idx_base + offset + offsets;
804 .index_select(0, indices.flatten())
808 auto greville_ = gathered.mean(1);
849 const torch::Tensor &coeff_indices, int64_t numeval,
850 torch::IntArrayRef sizes)
const override {
858 coeffs(i).index_select(0, coeff_indices).view({-1, numeval}))
871 const torch::Tensor &coeff_indices, int64_t numeval,
872 torch::IntArrayRef sizes)
const override {
887 return torch::matmul(
coeffs(i)
888 .index_select(0, coeff_indices)
889 .view({numeval, -1,
degrees_[0] + 1}),
890 basfunc[0].view({numeval, -1, 1}));
892 return torch::matmul(
893 (eval_(i, dim - 1)).view({numeval, -1,
degrees_[dim] + 1}),
894 basfunc[dim].view({numeval, -1, 1}));
899 result.set(i, (eval_(i,
parDim_ - 1)).view(sizes));
955 template <deriv deriv = deriv::func,
bool memory_optimized = false>
956 inline auto eval(
const torch::Tensor &xi)
const {
960 throw std::runtime_error(
"Invalid parametric dimension");
968 template <deriv deriv = deriv::func,
bool memory_optimized = false>
980 template <deriv deriv = deriv::func,
bool memory_optimized = false>
981 inline auto eval_tr(
const torch::Tensor &xi)
const {
985 throw std::runtime_error(
"Invalid parametric dimension");
993 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1014 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1017 return eval<deriv, memory_optimized>(
1018 xi, knot_indices, find_coeff_indices<memory_optimized>(knot_indices));
1027 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1030 return eval_tr<deriv, memory_optimized>(
1031 xi, knot_indices, find_coeff_indices<memory_optimized>(knot_indices));
1052 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1055 const torch::Tensor &coeff_indices)
const {
1064 result.set(i, torch::zeros_like(
coeffs_[i]));
1072 assert(xi[i].sizes() == knot_indices[i].sizes());
1074 assert(xi[0].sizes() == xi[i].sizes());
1076 if constexpr (memory_optimized) {
1080 throw std::runtime_error(
1081 "Memory-optimized evaluation requires single-valued coefficient");
1085 eval_basfunc<deriv, memory_optimized>(xi, knot_indices);
1092 return torch::matmul(
1094 .index_select(0, coeff_indices)
1095 .view({xi[0].numel(), -1,
degrees_[0] + 1}),
1096 basfunc[0].view({xi[0].numel(), -1, 1}));
1098 return torch::matmul(
1100 .view({xi[0].numel(), -1,
degrees_[dim] + 1}),
1101 basfunc[dim].view({xi[0].numel(), -1, 1}));
1106 result.set(i, (eval_(i,
parDim_ - 1)).view(xi[0].sizes()));
1115 auto basfunc = eval_basfunc<deriv, memory_optimized>(xi, knot_indices);
1117 if (
coeffs(0).dim() > 1) {
1119 auto sizes = xi[0].sizes() + (-1_i64);
1123 .index_select(0, coeff_indices)
1124 .view({-1, xi[0].numel(),
1132 .index_select(0, coeff_indices)
1133 .view({-1, xi[0].numel()}))
1134 .view(xi[0].sizes()));
1148 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1151 const torch::Tensor &coeff_indices)
const {
1160 result.set(i, torch::zeros_like(
coeffs_[i]));
1168 assert(xi[i].sizes() == knot_indices[i].sizes());
1170 assert(xi[0].sizes() == xi[i].sizes());
1172 if constexpr (memory_optimized) {
1176 throw std::runtime_error(
1177 "Memory-optimized evaluation requires single-valued coefficient");
1181 eval_basfunc_tr<deriv, memory_optimized>(xi, knot_indices);
1188 return torch::matmul(
1190 .index_select(0, coeff_indices)
1191 .view({xi[0].numel(), -1,
degrees_[0] + 1}),
1192 basfunc[0].view({xi[0].numel(), -1, 1}));
1194 return torch::matmul(
1196 .view({xi[0].numel(), -1,
degrees_[dim] + 1}),
1197 basfunc[dim].view({xi[0].numel(), -1, 1}));
1202 result.set(i, (eval_(i,
parDim_ - 1)).view(xi[0].sizes()));
1212 eval_basfunc_tr<deriv, memory_optimized>(xi, knot_indices);
1214 if (
coeffs(0).dim() > 1) {
1216 auto sizes = xi[0].sizes() + (-1_i64);
1220 .index_select(0, coeff_indices)
1221 .view({-1, xi[0].numel(),
1229 .index_select(0, coeff_indices)
1230 .view({-1, xi[0].numel()}))
1231 .view(xi[0].sizes()));
1258 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1293 template <
bool memory_optimized = false>
1296 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1298 return find_coeff_indices<memory_optimized>(
1306 template <
bool memory_optimized = false>
1309 using utils::operator-;
1312 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1313 else if constexpr (
parDim_ == 1)
1314 return utils::VSlice<memory_optimized>(indices[0].flatten(), -
degrees_[0],
1317 return utils::VSlice<memory_optimized>(
1319 utils::make_array<int64_t>(-
degrees_),
1320 utils::make_array<int64_t, parDim_>(1),
1335 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1339 return torch::ones_like(
coeffs_[0]);
1341 return torch::zeros_like(
coeffs_[0]);
1351 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1355 return torch::ones_like(
coeffs_[0]);
1357 return torch::zeros_like(
coeffs_[0]);
1367 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1371 return torch::ones_like(
coeffs_[0]);
1373 return torch::zeros_like(
coeffs_[0]);
1375 return eval_basfunc_tr<deriv, memory_optimized>(
1384 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1388 return torch::ones_like(
coeffs_[0]);
1390 return torch::zeros_like(
coeffs_[0]);
1392 return eval_basfunc_tr<deriv, memory_optimized>(xi,
1409 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1411 const torch::Tensor &knot_indices)
const {
1414 return torch::ones_like(
coeffs_[0]);
1416 return torch::zeros_like(
coeffs_[0]);
1418 return eval_basfunc<deriv, memory_optimized>(
1428 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1430 const torch::Tensor &knot_indices)
const {
1433 return torch::ones_like(
coeffs_[0]);
1435 return torch::zeros_like(
coeffs_[0]);
1437 return eval_basfunc_tr<deriv, memory_optimized>(
1447 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1454 return torch::ones_like(
coeffs_[0]);
1456 return torch::zeros_like(
coeffs_[0]);
1462 assert(xi[i].sizes() == knot_indices[i].sizes());
1464 assert(xi[0].sizes() == xi[i].sizes());
1466 if constexpr (memory_optimized) {
1470 this]<std::size_t... Is>(std::index_sequence<Is...>) {
1478 10>(xi[Is].flatten(),
1479 knot_indices[Is].flatten())
1480 .transpose(0, 1))...};
1483 return basfunc_(std::make_index_sequence<parDim_>{});
1494 xi[0].flatten(), knot_indices[0].flatten());
1499 auto basfunc_ = [&,
this]<std::size_t... Is>(
1500 std::index_sequence<Is...>) {
1511 xi[Is].flatten(), knot_indices[Is].flatten())...);
1527 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1534 return torch::ones_like(
coeffs_[0]);
1536 return torch::zeros_like(
coeffs_[0]);
1542 assert(xi[i].sizes() == knot_indices[i].sizes());
1544 assert(xi[0].sizes() == xi[i].sizes());
1546 if constexpr (memory_optimized) {
1550 this]<std::size_t... Is>(std::index_sequence<Is...>) {
1558 10>(xi[Is].flatten(),
1559 knot_indices[Is].flatten())
1560 .transpose(0, 1))...};
1563 return basfunc_(std::make_index_sequence<parDim_>{});
1574 xi[0].flatten(), knot_indices[0].flatten());
1579 auto basfunc_ = [&,
this]<std::size_t... Is>(
1580 std::index_sequence<Is...>) {
1586 utils::kronproduct<-1>(
1591 xi[Is].flatten(), knot_indices[Is].flatten())...);
1607 std::array<real_t, geoDim_>(
const std::array<real_t, parDim_> &)>
1610 static_assert(
parDim_ <= 4,
"Unsupported parametric dimension");
1614 auto c = mapping(std::array<real_t, parDim_>{});
1616 coeffs_[d].detach()[0] = c[d];
1620 else if constexpr (
parDim_ == 1) {
1621#pragma omp parallel for
1622 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1624 mapping(std::array<real_t, parDim_>{i / real_t(
ncoeffs_[0] - 1)});
1626 coeffs_[d].detach()[i] = c[d];
1631 else if constexpr (
parDim_ == 2) {
1632#pragma omp parallel for collapse(2)
1633 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1634 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1635 auto c = mapping(std::array<real_t, parDim_>{
1644 else if constexpr (
parDim_ == 3) {
1645#pragma omp parallel for collapse(3)
1646 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1647 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1648 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1649 auto c = mapping(std::array<real_t, parDim_>{
1661 else if constexpr (
parDim_ == 4) {
1662#pragma omp parallel for collapse(4)
1663 for (int64_t l = 0; l <
ncoeffs_[3]; ++l) {
1664 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1665 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1666 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1667 auto c = mapping(std::array<real_t, parDim_>{
1680 throw std::runtime_error(
"Unsupported parametric dimension");
1690 template <std::
size_t N>
1693 std::array<real_t, N>(
const std::array<real_t, parDim_> &)>
1695 std::array<short_t, N> dims) {
1697 static_assert(
parDim_ <= 4,
"Unsupported parametric dimension");
1701 auto c = mapping(std::array<real_t, parDim_>{});
1702 for (std::size_t d = 0; d < N; ++d)
1703 coeffs_[dims[d]].detach()[0] = c[d];
1707 else if constexpr (
parDim_ == 1) {
1708#pragma omp parallel for
1709 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1711 mapping(std::array<real_t, parDim_>{i / real_t(
ncoeffs_[0] - 1)});
1712 for (std::size_t d = 0; d < N; ++d)
1713 coeffs_[dims[d]].detach()[i] = c[d];
1718 else if constexpr (
parDim_ == 2) {
1719#pragma omp parallel for collapse(2)
1720 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1721 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1722 auto c = mapping(std::array<real_t, parDim_>{
1724 for (std::size_t d = 0; d < N; ++d)
1731 else if constexpr (
parDim_ == 3) {
1732#pragma omp parallel for collapse(3)
1733 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1734 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1735 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1736 auto c = mapping(std::array<real_t, parDim_>{
1739 for (std::size_t d = 0; d < N; ++d)
1748 else if constexpr (
parDim_ == 4) {
1749#pragma omp parallel for collapse(4)
1750 for (int64_t l = 0; l <
ncoeffs_[3]; ++l) {
1751 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1752 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1753 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1754 auto c = mapping(std::array<real_t, parDim_>{
1757 for (std::size_t d = 0; d < N; ++d)
1767 throw std::runtime_error(
"Unsupported parametric dimension");
1774 [[nodiscard]]
inline nlohmann::json
to_json()
const override {
1775 nlohmann::json json;
1790 return ::iganet::utils::to_json<real_t, 1>(
knots_);
1796 auto coeffs_json = nlohmann::json::array();
1798 auto [coeffs_cpu, coeffs_accessor] =
1799 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
1801 auto json = nlohmann::json::array();
1804 json.push_back(coeffs_accessor[0]);
1809 json.push_back(coeffs_accessor[i]);
1812 coeffs_json.push_back(json);
1822 if (json[
"geoDim"].get<short_t>() !=
geoDim_)
1823 throw std::runtime_error(
1824 "JSON object provides incompatible geometric dimensions");
1826 if (json[
"parDim"].get<short_t>() !=
parDim_)
1827 throw std::runtime_error(
1828 "JSON object provides incompatible parametric dimensions");
1830 if (json[
"degrees"].get<std::array<short_t, parDim_>>() !=
degrees_)
1831 throw std::runtime_error(
"JSON object provides incompatible degrees");
1833 nknots_ = json[
"nknots"].get<std::array<int64_t, parDim_>>();
1834 ncoeffs_ = json[
"ncoeffs"].get<std::array<int64_t, parDim_>>();
1840 auto kv = json[
"knots"].get<std::array<std::vector<real_t>,
parDim_>>();
1845 auto c = json[
"coeffs"].get<std::array<std::vector<real_t>,
geoDim_>>();
1858 [[nodiscard]]
inline pugi::xml_document
1859 to_xml(
int id = 0,
const std::string &label =
"",
1860 int index = -1)
const override {
1861 pugi::xml_document doc;
1862 pugi::xml_node root = doc.append_child(
"xml");
1863 to_xml(root,
id, label, index);
1874 inline pugi::xml_node &
to_xml(pugi::xml_node &root,
int id = 0,
1875 const std::string &label =
"",
1876 int index = -1)
const override {
1878 pugi::xml_node geo = root.append_child(
"Geometry");
1882 geo.append_attribute(
"type") =
"Point";
1885 geo.append_attribute(
"id") = id;
1888 geo.append_attribute(
"index") = index;
1891 geo.append_attribute(
"label") = label.c_str();
1895 else if constexpr (
parDim_ == 1) {
1896 geo.append_attribute(
"type") =
"BSpline";
1899 geo.append_attribute(
"id") = id;
1902 geo.append_attribute(
"index") = index;
1905 geo.append_attribute(
"label") = label.c_str();
1908 pugi::xml_node basis = geo.append_child(
"Basis");
1909 basis.append_attribute(
"type") =
"BSplineBasis";
1912 pugi::xml_node
knots = basis.append_child(
"KnotVector");
1915 std::stringstream ss;
1916 auto [knots_cpu, knots_accessor] =
1917 utils::to_tensorAccessor<real_t, 1>(
knots_[0], torch::kCPU);
1918 for (int64_t i = 0; i <
nknots_[0]; ++i)
1919 ss << std::to_string(knots_accessor[i])
1920 << (i <
nknots_[0] - 1 ?
" " :
"");
1921 knots.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1926 geo.append_attribute(
"type") =
1927 std::string(
"TensorBSpline").append(std::to_string(
parDim_)).c_str();
1930 geo.append_attribute(
"id") = id;
1933 geo.append_attribute(
"index") = index;
1936 geo.append_attribute(
"label") = label.c_str();
1939 pugi::xml_node bases = geo.append_child(
"Basis");
1940 bases.append_attribute(
"type") = std::string(
"TensorBSplineBasis")
1941 .append(std::to_string(
parDim_))
1945 pugi::xml_node basis = bases.append_child(
"Basis");
1946 basis.append_attribute(
"type") =
"BSplineBasis";
1947 basis.append_attribute(
"index") = index;
1950 pugi::xml_node
knots = basis.append_child(
"KnotVector");
1953 std::stringstream ss;
1954 auto [knots_cpu, knots_accessor] =
1955 utils::to_tensorAccessor<real_t, 1>(
knots_[index], torch::kCPU);
1956 for (int64_t i = 0; i <
nknots_[index]; ++i)
1957 ss << std::to_string(knots_accessor[i])
1958 << (i <
nknots_[index] - 1 ?
" " :
"");
1959 knots.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1965 pugi::xml_node coefs = geo.append_child(
"coefs");
1966 coefs.append_attribute(
"geoDim") =
geoDim_;
1968 auto [coeffs_cpu, coeffs_accessors] =
1969 utils::to_tensorAccessor<real_t, 1>(
coeffs_, torch::kCPU);
1970 std::stringstream ss;
1974 ss << std::to_string(coeffs_accessors[g][0]) <<
" ";
1979 ss << std::to_string(coeffs_accessors[g][i]) <<
" ";
1982 coefs.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1994 const std::string &label =
"",
1995 int index = -1)
override {
1996 return from_xml(doc.child(
"xml"),
id, label, index);
2006 const std::string &label =
"",
2007 int index = -1)
override {
2009 std::array<bool, std::max(
parDim_,
short_t{1})> nknots_found{
false},
2010 ncoeffs_found{
false};
2013 for (pugi::xml_node geo : root.children(
"Geometry")) {
2019 if (std::string_view{geo.attribute(
"type").value()} ==
"Point" &&
2020 (
id >= 0 ? geo.attribute(
"id").as_int() == id :
true) &&
2021 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
2022 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
2024 nknots_found[0] =
true;
2025 ncoeffs_found[0] =
true;
2032 else if constexpr (
parDim_ == 1) {
2035 if (std::string_view{geo.attribute(
"type").value()} ==
"BSpline" &&
2036 (
id >= 0 ? geo.attribute(
"id").as_int() == id :
true) &&
2037 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
2038 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
2041 if (pugi::xml_node basis = geo.child(
"Basis");
2042 std::string_view{basis.attribute(
"type").value()} ==
2046 if (pugi::xml_node
knots = basis.child(
"KnotVector");
2049 std::vector<real_t> kv;
2050 std::string values = std::regex_replace(
2051 knots.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
2052 for (
auto value = strtok(&values[0],
" "); value !=
nullptr;
2053 value = strtok(
nullptr,
" "))
2054 kv.push_back(
static_cast<real_t
>(std::stod(value)));
2060 nknots_found[0] =
true;
2061 ncoeffs_found[0] =
true;
2076 if (geo.attribute(
"type").value() ==
2077 std::string(
"TensorBSpline").append(std::to_string(
parDim_)) &&
2078 (
id >= 0 ? geo.attribute(
"id").as_int() ==
id :
true) &&
2079 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
2080 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
2083 if (pugi::xml_node bases = geo.child(
"Basis");
2084 bases.attribute(
"type").value() ==
2085 std::string(
"TensorBSplineBasis")
2086 .append(std::to_string(
parDim_))) {
2089 for (pugi::xml_node basis : bases.children(
"Basis")) {
2092 if (std::string_view{basis.attribute(
"type").value()} ==
2095 int index = basis.attribute(
"index").as_int();
2098 if (pugi::xml_node
knots = basis.child(
"KnotVector");
2101 std::vector<real_t> kv;
2102 std::string values = std::regex_replace(
2103 knots.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
2105 for (
auto value = strtok(&values[0],
" "); value !=
nullptr;
2106 value = strtok(
nullptr,
" "))
2107 kv.push_back(
static_cast<real_t
>(std::stod(value)));
2113 nknots_found[index] =
true;
2114 ncoeffs_found[index] =
true;
2130 if (std::any_of(std::begin(nknots_found), std::end(nknots_found),
2131 [](
bool i) {
return !i; }))
2132 throw std::runtime_error(
2133 "XML object is not compatible with B-spline object");
2145 if (pugi::xml_node coefs = geo.child(
"coefs")) {
2147 std::string values = std::regex_replace(
2148 coefs.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
2149 auto coeffs_accessors = utils::to_tensorAccessor<real_t, 1>(
coeffs_);
2152 auto value = strtok(&values[0],
" ");
2155 if (value ==
nullptr)
2156 throw std::runtime_error(
2157 "XML object does not provide enough coefficients");
2159 coeffs_accessors[g][0] =
static_cast<real_t
>(std::stod(value));
2160 value = strtok(
nullptr,
" ");
2163 if (value !=
nullptr)
2164 throw std::runtime_error(
2165 "XML object provides too many coefficients");
2168 auto value = strtok(&values[0],
" ");
2172 if (value ==
nullptr)
2173 throw std::runtime_error(
2174 "XML object does not provide enough coefficients");
2176 coeffs_accessors[g][i] =
static_cast<real_t
>(std::stod(value));
2177 value = strtok(
nullptr,
" ");
2180 if (value !=
nullptr)
2181 throw std::runtime_error(
2182 "XML object provides too many coefficients");
2190 if (nknots_found[0] && ncoeffs_found[0])
2192 }
else if (std::all_of(std::begin(nknots_found), std::end(nknots_found),
2193 [](
bool i) {
return i; }) &&
2194 std::all_of(std::begin(ncoeffs_found),
2195 std::end(ncoeffs_found),
2196 [](
bool i) {
return i; }))
2200 throw std::runtime_error(
2201 "XML object is not compatible with B-spline object");
2205 throw std::runtime_error(
"XML object does not provide coefficients");
2209 throw std::runtime_error(
"XML object does not provide geometry with given "
2210 "id, index, and/or label");
2217 inline void load(
const std::string &filename,
2218 const std::string &key =
"bspline") {
2219 torch::serialize::InputArchive archive;
2220 archive.load_from(filename);
2228 inline torch::serialize::InputArchive &
2229 read(torch::serialize::InputArchive &archive,
2230 const std::string &key =
"bspline") {
2231 torch::Tensor tensor;
2233 archive.read(key +
".parDim", tensor);
2234 if (tensor.item<int64_t>() !=
parDim_)
2235 throw std::runtime_error(
"parDim mismatch");
2237 archive.read(key +
".geoDim", tensor);
2238 if (tensor.item<int64_t>() !=
geoDim_)
2239 throw std::runtime_error(
"geoDim mismatch");
2242 archive.read(key +
".degree[" + std::to_string(i) +
"]", tensor);
2243 if (tensor.item<int64_t>() !=
degrees_[i])
2244 throw std::runtime_error(
"degrees mismatch");
2248 archive.read(key +
".nknots[" + std::to_string(i) +
"]", tensor);
2249 nknots_[i] = tensor.item<int64_t>();
2253 archive.read(key +
".knots[" + std::to_string(i) +
"]",
knots_[i]);
2256 archive.read(key +
".ncoeffs[" + std::to_string(i) +
"]", tensor);
2257 ncoeffs_[i] = tensor.item<int64_t>();
2264 archive.read(key +
".coeffs[" + std::to_string(i) +
"]",
coeffs_[i]);
2272 inline void save(
const std::string &filename,
2273 const std::string &key =
"bspline")
const {
2274 torch::serialize::OutputArchive archive;
2275 write(archive, key).save_to(filename);
2282 inline torch::serialize::OutputArchive &
2283 write(torch::serialize::OutputArchive &archive,
2284 const std::string &key =
"bspline")
const {
2285 archive.write(key +
".parDim", torch::full({1},
parDim_));
2286 archive.write(key +
".geoDim", torch::full({1},
geoDim_));
2289 archive.write(key +
".degree[" + std::to_string(i) +
"]",
2293 archive.write(key +
".nknots[" + std::to_string(i) +
"]",
2294 torch::full({1},
nknots_[i]));
2297 archive.write(key +
".knots[" + std::to_string(i) +
"]",
knots_[i]);
2300 archive.write(key +
".ncoeffs[" + std::to_string(i) +
"]",
2304 archive.write(key +
".coeffs[" + std::to_string(i) +
"]",
coeffs_[i]);
2318 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2320 real_t rtol = real_t{1e-5}, real_t atol = real_t{1e-8})
const {
2321 if constexpr (!std::is_same_v<real_t, other_t>)
2341 result *= torch::allclose(
knots(i), other.
knots(i), rtol, atol);
2344 result *= torch::allclose(
coeffs(i), other.
coeffs(i), rtol, atol);
2355 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2358 if constexpr (!std::is_same_v<real_t, other_t>)
2378 result *= torch::equal(
knots(i), other.
knots(i));
2392 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2410 assert(numRefine > 0);
2411 assert(dim == -1 || (dim >= 0 && dim <
parDim_));
2417 for (
int refine = 0; refine < numRefine; ++refine) {
2434 std::vector<real_t> kv;
2437 for (int64_t j = 0; j <
degrees_[i]; ++j)
2438 kv.push_back(
static_cast<real_t
>(0));
2441 kv.push_back(
static_cast<real_t
>(j) /
2444 for (int64_t j = 0; j <
degrees_[i]; ++j)
2445 kv.push_back(
static_cast<real_t
>(1));
2456 knots_indices[i] =
knots[i].index(
2457 {torch::indexing::Slice(0,
knots[i].numel() -
degrees_[i] - 1)});
2482 if constexpr (
degree > terminal)
2483 return degree * eval_prefactor<degree - 1, deriv, terminal>();
2496 throw std::runtime_error(
2497 "Not enough coefficients to create open knot vector");
2504 auto inner = torch::empty({0},
options_);
2506 if (num_inner > 0) {
2507 inner = torch::arange(0, num_inner + 1,
options_);
2508 inner = inner /
static_cast<real_t
>(num_inner);
2511 knots_[i] = torch::cat({start, inner, end});
2563 coeffs_[i] = torch::kron(torch::linspace(
static_cast<real_t
>(0),
2564 static_cast<real_t
>(1),
2595 options_.requires_grad(
false).template dtype<int64_t>())
2602 options_.requires_grad(
false).template dtype<int64_t>())
2606 auto indices = idx_base + offsets;
2609 auto gathered =
knots_[j]
2610 .index_select(0, indices.flatten())
2614 auto greville_ = gathered.mean(1);
2635 std::pow(10, i) * 0, std::pow(10, i) * (size - 1), size,
options_);
2641 throw std::runtime_error(
"Unsupported init option");
2654 assert(
knots[i].numel() == knot_indices[i].numel() +
degrees_[i] + 1);
2658 auto basfunc = update_coeffs_univariate<degrees_[0], 0>(
2659 knots[0].flatten(), knot_indices[0].flatten());
2666 .index_select(0, coeff_indices)
2667 .view({-1, knot_indices[0].numel()}))
2668 .view(knot_indices[0].sizes());
2673 auto basfunc_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
2674 if constexpr (
sizeof...(Is) == 1)
2676 knots[Is].flatten(), knot_indices[Is].flatten()),
2680 knots[Is].flatten(), knot_indices[Is].flatten())...);
2690 for (
short_t i = start_index; i <= stop_index; ++i)
2691 result *= array[i].numel();
2700 .repeat_interleave(prod_(knot_indices, 0, i - 1), 0)
2701 .repeat(prod_(knot_indices, i + 1,
parDim_ - 1));
2708 .index_select(0, coeff_indices)
2709 .view({-1, knot_indices_[0].numel()}))
2710 .view(knot_indices_[0].sizes());
2822 template <
short_t degree,
short_t dim,
short_t deriv>
2824 const torch::Tensor &knot_indices)
const {
2825 assert(xi.sizes() == knot_indices.sizes());
2831 torch::Tensor b = torch::ones({xi.numel()},
options_);
2846 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2847 .to(::iganet::dtype_v<real_t>);
2853 auto w = torch::div(xi.repeat(k) - t1 - mask, t21 - mask);
2856 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2859 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2873 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2874 .to(::iganet::dtype_v<real_t>);
2880 auto w = torch::div(torch::ones_like(t21,
options_) - mask, t21 - mask);
2883 b = torch::cat({torch::mul(-w, b), torch::zeros_like(xi,
options_)},
2885 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2888 return b.view({
degree + 1, xi.numel()});
2899 template <
short_t degree,
short_t dim,
short_t deriv>
2902 const torch::Tensor &knot_indices)
const {
2903 assert(xi.sizes() == knot_indices.sizes());
2908 torch::Tensor b = torch::ones({xi.numel()},
options_);
2918 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2919 .to(::iganet::dtype_v<real_t>);
2921 auto w = torch::div(xi.repeat(k) - t1 - mask, t21 - mask);
2923 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2926 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2935 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2936 .to(::iganet::dtype_v<real_t>);
2938 auto w = torch::div(torch::ones_like(t21,
options_) - mask, t21 - mask);
2940 b = torch::cat({torch::mul(-w, b), torch::zeros_like(xi,
options_)},
2942 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2946 return b.view({
degree + 1, xi.numel()}).transpose(0, 1);
2961 template <
short_t degree,
short_t dim>
2964 const torch::Tensor &knot_indices)
const {
2967 torch::Tensor b = torch::ones({knot_indices.numel()},
options_);
2982 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2983 .to(::iganet::dtype_v<real_t>);
2989 auto w = torch::div(
2990 knots.index({torch::indexing::Slice(k, knot_indices.numel() + k)})
2996 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2997 torch::zeros_like(knot_indices,
options_)},
3000 {torch::zeros_like(knot_indices,
options_), torch::mul(w, b)}, 0);
3003 return b.view({
degree + 1, knot_indices.numel()});
3013#ifdef IGANET_WITH_GISMO
3018 auto [coeffs_cpu, coeffs_accessor] =
3019 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
3020 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
3022 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
3025 std::array<gismo::gsKnotVector<real_t>,
parDim_> kv;
3028 auto [knots_cpu, knots_accessor] =
3029 utils::to_tensorAccessor<real_t, 1>(
knots_[i], torch::kCPU);
3030 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
3031 kv[i] = gismo::gsKnotVector<real_t>(
degrees_[i], knots_cpu_ptr,
3032 knots_cpu_ptr + knots_cpu.size(0));
3037 return gismo::gsBSpline<real_t>(gismo::give(kv[0]), gismo::give(coefs));
3039 }
else if constexpr (
parDim_ == 2) {
3041 return gismo::gsTensorBSpline<parDim_, real_t>(
3042 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(coefs));
3044 }
else if constexpr (
parDim_ == 3) {
3046 return gismo::gsTensorBSpline<parDim_, real_t>(
3047 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(kv[2]),
3048 gismo::give(coefs));
3050 }
else if constexpr (
parDim_ == 4) {
3052 return gismo::gsTensorBSpline<parDim_, real_t>(
3053 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(kv[2]),
3054 gismo::give(kv[3]), gismo::give(coefs));
3057 throw std::runtime_error(
"Invalid parametric dimension");
3060 throw std::runtime_error(
3061 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3065#ifdef IGANET_WITH_GISMO
3068 gismo::gsBSpline<real_t> &
to_gismo(gismo::gsBSpline<real_t> &bspline,
3069 bool updateKnotVector =
true,
3070 bool updateCoeffs =
true)
const {
3072 if (updateKnotVector) {
3076 if (bspline.degree(0) !=
degrees_[0])
3077 throw std::runtime_error(
"Degrees mismatch");
3079 auto [knots_cpu, knots_accessor] =
3080 utils::to_tensorAccessor<real_t, 1>(
knots_[0], torch::kCPU);
3081 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
3083 gismo::gsKnotVector<real_t> kv(
degrees_[0], knots_cpu_ptr,
3084 knots_cpu_ptr + knots_cpu.size(0));
3086 bspline.knots(0).swap(kv);
3089 throw std::runtime_error(
"Invalid parametric dimension");
3095 auto [coeffs_cpu, coeffs_accessor] =
3096 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
3097 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
3098 bspline.coefs().col(g) =
3099 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
3107 gismo::gsTensorBSpline<parDim_, real_t> &
3108 to_gismo(gismo::gsTensorBSpline<parDim_, real_t> &bspline,
3109 bool updateKnotVector =
true,
bool updateCoeffs =
true)
const {
3111 if (updateKnotVector) {
3115 assert(bspline.degree(i) ==
degrees_[i]);
3118 auto [knots_cpu, knots_accessor] =
3119 utils::to_tensorAccessor<real_t, 1>(
knots_[i], torch::kCPU);
3120 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
3122 gismo::gsKnotVector<real_t> kv(
degrees_[i], knots_cpu_ptr,
3123 knots_cpu_ptr + knots_cpu.size(0));
3124 bspline.knots(i).swap(kv);
3131 auto [coeffs_cpu, coeffs_accessor] =
3132 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
3133 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
3134 bspline.coefs().col(g) =
3135 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
3150 template <
typename BSpline>
3151 BSpline &
to_gismo(BSpline &bspline,
bool updateKnotVector,
3152 bool updateCoeffs)
const {
3153 throw std::runtime_error(
3154 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3160#ifdef IGANET_WITH_GISMO
3163 auto &
from_gismo(
const gismo::gsBSpline<real_t> &bspline,
3164 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3166 if (updateKnotVector) {
3168 throw std::runtime_error(
3169 "Knot vectors can only be updated for Non-uniform B-splines");
3174 if (bspline.coefs().cols() !=
geoDim_)
3175 throw std::runtime_error(
"Geometric dimensions mismatch");
3178 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3182 auto [coeffs_cpu, coeffs_accessor] =
3183 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
3185 const real_t *coeffs_ptr = bspline.coefs().col(g).data();
3187 for (int64_t i = 0; i <
ncoeffs_[g]; ++i)
3188 coeffs_accessor[i] = coeffs_ptr[i];
3198 auto &
from_gismo(
const gismo::gsTensorBSpline<parDim_, real_t> &bspline,
3199 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3201 if (updateKnotVector) {
3203 throw std::runtime_error(
3204 "Knot vectors can only be updated for Non-uniform B-splines");
3209 if (bspline.coefs().cols() !=
geoDim_)
3210 throw std::runtime_error(
"Geometric dimensions mismatch");
3213 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3217 auto [coeffs_cpu, coeffs_accessor] =
3218 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
3220 const real_t *coeffs_ptr = bspline.coefs().col(g).data();
3222 for (int64_t i = 0; i <
ncoeffs_[g]; ++i)
3223 coeffs_accessor[i] = coeffs_ptr[i];
3240 template <
typename BSpline>
3242 bool updateKnotVector) {
3243 throw std::runtime_error(
3244 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3259inline torch::serialize::OutputArchive &
3260operator<<(torch::serialize::OutputArchive &archive,
3262 return obj.
write(archive);
3273inline torch::serialize::InputArchive &
3276 return obj.
read(archive);
3301 template <
template <
typename,
short_t,
short_t...>
class BSpline,
3302 std::make_signed_t<short_t> degree_elevate = 0>
3303 using derived_type = BSpline<real_t, GeoDim, (Degrees + degree_elevate)...>;
3307 template <std::make_
signed_t<
short_t> degree_elevate = 0>
3314 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
3320 template <
typename other_t>
3339 :
Base(std::move(other)) {}
3349 const std::array<std::vector<typename Base::value_type>,
Base::parDim_>
3384 .to(
options.requires_grad(
false))
3394 const std::array<std::vector<typename Base::value_type>,
Base::parDim_>
3400 throw std::runtime_error(
"Knot vector is too short for an open knot "
3401 "vector (n+p+1 > 2*(p+1))");
3420 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3421 inline auto eval(
const torch::Tensor &xi)
const {
3432 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3443 return Base::template eval<deriv, memory_optimized>(
3453 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3466 return Base::template eval<deriv, memory_optimized>(xi, knot_indices);
3476 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3479 const torch::Tensor &coeff_indices)
const {
3489 return Base::template eval<deriv, memory_optimized>(xi, knot_indices,
3511 return torch::zeros_like(
Base::coeffs_[0]).to(torch::kInt64);
3524 auto nnz =
Base::knots_[i].repeat({xi[i].numel(), 1}) >
3525 xi[i].flatten().view({-1, 1});
3527 torch::remainder(std::get<1>(((nnz.cumsum(1) == 1) & nnz).max(1)) - 1,
3529 .view(xi[i].sizes());
3546 assert(numRefine > 0);
3554 auto [kv_cpu, kv_accessor] =
3555 utils::to_tensorAccessor<typename Base::value_type, 1>(
3558 std::vector<typename Base::value_type> kv;
3560 kv.push_back(kv_accessor[0]);
3562 for (int64_t j = 1; j < kv_accessor.size(0); ++j) {
3564 if ((dim == -1 || dim == i) && (kv_accessor[j - 1] < kv_accessor[j]))
3565 for (
int refine = 1; refine < (2 << (numRefine - 1)); ++refine)
3567 kv_accessor[j - 1] +
3570 (kv_accessor[j] - kv_accessor[j - 1]));
3572 kv.push_back(kv_accessor[j]);
3586 knots_indices[i] =
knots[i].index({torch::indexing::Slice(
3632 knots_indices[i] =
knots_[i].index({torch::indexing::Slice(
3660 assert(numReduce > 0);
3668 auto [kv_cpu, kv_accessor] =
3669 utils::to_tensorAccessor<typename Base::value_type, 1>(
3672 std::vector<typename Base::value_type> kv;
3674 kv.push_back(kv_accessor[0]);
3676 for (int64_t j = 1; j < kv_accessor.size(0); ++j) {
3678 if ((dim == -1 || dim == i) && (kv_accessor[j - 1] < kv_accessor[j]) &&
3679 (kv_accessor[j] < kv_accessor[kv_accessor.size(0) - 1]))
3680 for (
int reduce = 0; reduce < numReduce; ++reduce)
3681 kv.push_back(kv_accessor[j]);
3683 kv.push_back(kv_accessor[j]);
3697 knots_indices[i] =
knots[i].index({torch::indexing::Slice(
3718#ifdef IGANET_WITH_GISMO
3721 auto &
from_gismo(
const gismo::gsBSpline<typename Base::value_type> &bspline,
3722 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3724 if (updateKnotVector) {
3729 throw std::runtime_error(
"Degrees mismatch");
3732 throw std::runtime_error(
"Knot vector dimensions mismatch");
3734 auto [knots0_cpu, knots0_accessor] =
3735 utils::to_tensorAccessor<typename Base::value_type, 1>(
3739 bspline.knots(0).asMatrix().data();
3742 knots0_accessor[i] = knots0_ptr[i];
3747 throw std::runtime_error(
"Invalid parametric dimension");
3753 throw std::runtime_error(
"Geometric dimensions mismatch");
3756 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3760 auto [coeffs_cpu, coeffs_accessor] =
3761 utils::to_tensorAccessor<typename Base::value_type, 1>(
3765 bspline.coefs().row(g).data();
3768 coeffs_accessor[i] = coeffs_ptr[i];
3779 const gismo::gsTensorBSpline<Base::parDim_, typename Base::value_type>
3781 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3783 if (updateKnotVector) {
3787 throw std::runtime_error(
"Degrees mismatch");
3790 throw std::runtime_error(
"Knot vector dimensions mismatch");
3792 auto [knots_cpu, knots_accessor] =
3793 utils::to_tensorAccessor<typename Base::value_type, 1>(
3797 bspline.knots(i).asMatrix().data();
3800 knots_accessor[i] = knots_ptr[i];
3809 throw std::runtime_error(
"Geometric dimensions mismatch");
3812 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3816 auto [coeffs_cpu, coeffs_accessor] =
3817 utils::to_tensorAccessor<typename Base::value_type, 1>(
3821 bspline.coefs().row(g).data();
3824 coeffs_accessor[i] = coeffs_ptr[i];
3841 template <
typename BSpline>
3843 bool updateKnotVector) {
3844 throw std::runtime_error(
3845 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3857template <
typename T>
3862template <
typename T>
3864 std::is_base_of_v<Spline_, T> && std::is_base_of_v<UniformSplineCore_, T> &&
3865 !std::is_base_of_v<NonUniformSplineCore_, T>;
3869template <
typename T>
3871 std::is_base_of_v<NonUniformSplineCore_, T>;
3886template <
typename BSplineCore>
3893 using BSplineCore::BSplineCore;
3901 std::make_signed_t<short_t> degree_elevate = 0>
3907 template <std::make_
signed_t<
short_t> degree_elevate = 0>
3917 real_t, GeoDim, Degrees...>>;
3921 template <
typename other_t>
3926 using Ptr = std::shared_ptr<BSplineCommon>;
3929 using uPtr = std::unique_ptr<BSplineCommon>;
3939 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3940 BSplineCore::coeffs_[i] = other.coeffs(i).
clone();
3952 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3953 BSplineCore::coeffs_[i] = coeffs[i].
clone();
3955 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3956 BSplineCore::coeffs_[i] = coeffs[i];
3965 template <
typename OtherCore>
3968 : BSplineCore(
std::forward<OtherCore>(core)) {}
3983 std::move(
static_cast<BSplineCore &
>(*
this)));
3998 BSplineCore::options());
3999 for (
short_t i = 0; i < BSplineCore::parDim(); ++i)
4000 if (!torch::equal(BSplineCore::knots(i), canonical.knots(i)))
4001 throw std::runtime_error(
4002 "Cannot convert a non-uniform B-spline to a uniform B-spline");
4005 std::move(
static_cast<target_core &
>(*
this)));
4014 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
4015 BSplineCore::coeffs_[i] = std::move(coeffs[i]);
4075 make_unique(
const std::array<std::vector<typename BSplineCore::value_type>,
4076 BSplineCore::parDim_> &kv,
4090 make_unique(
const std::array<std::vector<typename BSplineCore::value_type>,
4091 BSplineCore::parDim_> &kv,
4107 return std::make_shared<BSplineCommon>(options);
4120 return std::make_shared<BSplineCommon>(ncoeffs,
init, options);
4135 return std::make_shared<BSplineCommon>(ncoeffs, coeffs,
clone, options);
4148 return std::make_shared<BSplineCommon>(ncoeffs, coeffs, options);
4157 make_shared(
const std::array<std::vector<typename BSplineCore::value_type>,
4158 BSplineCore::parDim_> &kv,
4162 return std::make_shared<BSplineCommon>(kv,
init, options);
4172 make_shared(
const std::array<std::vector<typename BSplineCore::value_type>,
4173 BSplineCore::parDim_> &kv,
4178 return std::make_shared<BSplineCommon>(kv, coeffs,
clone, options);
4192 BSplineCore::uniform_refine(numRefine, dim);
4201 result.nknots_ = BSplineCore::nknots_;
4202 result.ncoeffs_ = BSplineCore::ncoeffs_;
4203 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
4205 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4206 result.knots_[i] = BSplineCore::knots_[i].
clone();
4208 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
4209 result.coeffs_[i] = BSplineCore::coeffs_[i].
clone();
4222 result.nknots_ = BSplineCore::nknots_;
4223 result.ncoeffs_ = BSplineCore::ncoeffs_;
4224 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
4226 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4227 result.knots_[i] = BSplineCore::knots_[i].
to(options);
4229 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
4230 result.coeffs_[i] = BSplineCore::coeffs_[i].
to(options);
4238 inline auto to(torch::Device device)
const {
4241 result.nknots_ = BSplineCore::nknots_;
4242 result.ncoeffs_ = BSplineCore::ncoeffs_;
4243 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
4245 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4246 result.knots_[i] = BSplineCore::knots_[i].
to(device);
4248 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
4249 result.coeffs_[i] = BSplineCore::coeffs_[i].
to(device);
4257 template <
typename real_t>
inline auto to()
const {
4258 return to(BSplineCore::options_.
template dtype<real_t>());
4271 return this->
clone().diff_(other, dim);
4285 bool compatible(
true);
4287 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4288 compatible *= (BSplineCore::nknots(i) == other.nknots(i));
4290 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4291 compatible *= (BSplineCore::ncoeffs(i) == other.ncoeffs(i));
4294 throw std::runtime_error(
"B-splines are not compatible");
4297 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
4298 BSplineCore::coeffs(i) -= other.coeffs(i);
4300 BSplineCore::coeffs(dim) -= other.coeffs(dim);
4315 return this->
clone().abs_diff_(other, dim);
4329 bool compatible(
true);
4331 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4332 compatible *= (BSplineCore::nknots(i) == other.nknots(i));
4334 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4335 compatible *= (BSplineCore::ncoeffs(i) == other.ncoeffs(i));
4338 throw std::runtime_error(
"B-splines are not compatible");
4341 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
4342 BSplineCore::coeffs(i) =
4343 torch::abs(BSplineCore::coeffs(i) - other.coeffs(i));
4345 BSplineCore::coeffs(dim) =
4346 torch::abs(BSplineCore::coeffs(dim) - other.coeffs(dim));
4357 torch::pow(BSplineCore::eval(BSplineCore::greville())(0), 2));
4364 inline auto scale(BSplineCore::value_type s,
int dim = -1)
const {
4365 return this->
clone().scale_(s, dim);
4372 inline auto scale_(BSplineCore::value_type s,
int dim = -1) {
4374 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
4375 BSplineCore::coeffs(i) *= s;
4377 BSplineCore::coeffs(dim) *= s;
4385 scale(std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
4387 return this->
clone().scale_(v);
4394 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
4395 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
4396 BSplineCore::coeffs(i) *= v[i];
4404 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
4406 return this->
clone().translate_(v);
4413 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
4414 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
4415 BSplineCore::coeffs(i) += v[i];
4422 inline auto rotate(BSplineCore::value_type angle)
const {
4423 return this->
clone().rotate_(angle);
4429 inline auto rotate_(BSplineCore::value_type angle) {
4431 static_assert(BSplineCore::geoDim() == 2,
4432 "Rotation about one angle is only available in 2D");
4435 coeffs[0] = std::cos(angle) * BSplineCore::coeffs(0) -
4436 std::sin(angle) * BSplineCore::coeffs(1);
4437 coeffs[1] = std::sin(angle) * BSplineCore::coeffs(0) +
4438 std::cos(angle) * BSplineCore::coeffs(1);
4440 BSplineCore::coeffs().swap(coeffs);
4448 rotate(std::array<typename BSplineCore::value_type, 3> angle)
const {
4449 return this->
clone().rotate_(angle);
4455 inline auto rotate_(std::array<typename BSplineCore::value_type, 3> angle) {
4457 static_assert(BSplineCore::geoDim() == 3,
4458 "Rotation about two angles is only available in 3D");
4462 std::cos(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(0) +
4463 (std::sin(angle[0]) * std::sin(angle[1]) * std::cos(angle[2]) -
4464 std::cos(angle[0]) * std::sin(angle[2])) *
4465 BSplineCore::coeffs(1) +
4466 (std::cos(angle[0]) * std::sin(angle[1]) * std::cos(angle[2]) +
4467 std::sin(angle[0]) * std::sin(angle[2])) *
4468 BSplineCore::coeffs(2);
4471 std::cos(angle[1]) * std::sin(angle[2]) * BSplineCore::coeffs(0) +
4472 (std::sin(angle[0]) * std::sin(angle[1]) * std::sin(angle[2]) +
4473 std::cos(angle[0]) * std::cos(angle[2])) *
4474 BSplineCore::coeffs(1) +
4475 (std::cos(angle[0]) * std::sin(angle[1]) * std::sin(angle[2]) -
4476 std::sin(angle[0]) * std::cos(angle[2])) *
4477 BSplineCore::coeffs(2);
4480 -std::sin(angle[1]) * BSplineCore::coeffs(0) +
4481 std::sin(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(1) +
4482 std::cos(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(2);
4484 BSplineCore::coeffs().swap(coeffs);
4493 auto min_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4494 return torch::stack({BSplineCore::coeffs(Is).min()...});
4498 auto max_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4499 return torch::stack({BSplineCore::coeffs(Is).max()...});
4502 std::pair<torch::Tensor, torch::Tensor> bbox;
4503 bbox.first = min_(std::make_index_sequence<BSplineCore::geoDim_>{});
4504 bbox.second = max_(std::make_index_sequence<BSplineCore::geoDim_>{});
4515 template <
bool memory_optimized = false>
4516 inline auto nv(
const torch::Tensor &xi)
const {
4525 template <
bool memory_optimized = false>
4527 return nv<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4539 template <
bool memory_optimized = false>
4543 return nv<memory_optimized>(
4545 BSplineCore::template find_coeff_indices<memory_optimized>(
4560 template <
bool memory_optimized = false>
4563 const torch::Tensor &coeff_indices)
const {
4565 if constexpr (BSplineCore::parDim_ == 1 && BSplineCore::geoDim_ == 2) {
4567 auto eval_ = BSplineCore::template eval<deriv::dx, memory_optimized>(
4568 xi, knot_indices, coeff_indices);
4570 }
else if constexpr (BSplineCore::parDim_ == 1 &&
4571 BSplineCore::geoDim_ == 3) {
4573 auto t_ = BSplineCore::template eval<deriv::dx, memory_optimized>(
4574 xi, knot_indices, coeff_indices)
4576 auto a_ = BSplineCore::template eval<deriv::dx ^ 2, memory_optimized>(
4577 xi, knot_indices, coeff_indices);
4578 auto n_ = a_ - a_.dot(t_) * t_;
4580 n_(0, 0), n_(0, 1), n_(0, 2), t_(0, 0), t_(0, 1), t_(0, 2));
4581 }
else if constexpr (BSplineCore::parDim_ == 2 &&
4582 BSplineCore::geoDim_ == 3) {
4584 auto jac_ = jac<memory_optimized>(xi, knot_indices, coeff_indices);
4586 jac_(1, 0) * jac_(2, 1) - jac_(2, 0) * jac_(1, 1),
4587 jac_(2, 0) * jac_(0, 1) - jac_(0, 0) * jac_(2, 1),
4588 jac_(0, 0) * jac_(1, 1) - jac_(1, 0) * jac_(0, 1));
4590 throw std::runtime_error(
"Unsupported parametric/geometric dimension");
4618 template <
bool memory_optimized = false>
4619 inline auto curl(
const torch::Tensor &xi)
const {
4628 template <
bool memory_optimized = false>
4630 return curl<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4658 template <
bool memory_optimized = false>
4662 return curl<memory_optimized>(
4664 BSplineCore::template find_coeff_indices<memory_optimized>(
4694 template <
bool memory_optimized = false>
4697 const torch::Tensor &coeff_indices)
const {
4699 static_assert(BSplineCore::parDim_ == BSplineCore::geoDim_,
4700 "curl(.) requires that parametric and geometric dimension "
4704 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4705 assert(xi[i].sizes() == knot_indices[i].sizes());
4706 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4707 assert(xi[0].sizes() == xi[i].sizes());
4709 if constexpr (BSplineCore::parDim_ == 2)
4717 *BSplineCore::template eval<deriv::dx, memory_optimized>(
4718 xi, knot_indices, coeff_indices)[1] -
4719 *BSplineCore::template eval<deriv::dy, memory_optimized>(
4720 xi, knot_indices, coeff_indices)[0]);
4722 else if constexpr (BSplineCore::parDim_ == 3)
4728 *BSplineCore::template eval<deriv::dy, memory_optimized>(
4729 xi, knot_indices, coeff_indices)[2] -
4730 *BSplineCore::template eval<deriv::dz, memory_optimized>(
4731 xi, knot_indices, coeff_indices)[1],
4732 *BSplineCore::template eval<deriv::dz, memory_optimized>(
4733 xi, knot_indices, coeff_indices)[0] +
4734 *BSplineCore::template eval<deriv::dx, memory_optimized>(
4735 xi, knot_indices, coeff_indices)[2],
4736 *BSplineCore::template eval<deriv::dx, memory_optimized>(
4737 xi, knot_indices, coeff_indices)[1] +
4738 *BSplineCore::template eval<deriv::dy, memory_optimized>(
4739 xi, knot_indices, coeff_indices)[0]);
4742 throw std::runtime_error(
"Unsupported parametric/geometric dimension");
4768 template <
bool memory_optimized = false,
typename Geometry>
4769 inline auto icurl(
const Geometry &G,
const torch::Tensor &xi)
const {
4770 if constexpr (BSplineCore::parDim_ == 0)
4772 torch::zeros_like(BSplineCore::coeffs_[0])};
4783 template <
bool memory_optimized = false,
typename Geometry>
4786 if constexpr (BSplineCore::parDim_ == 0)
4788 torch::zeros_like(BSplineCore::coeffs_[0])};
4790 return icurl<memory_optimized, Geometry>(
4791 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4818 template <
bool memory_optimized = false,
typename Geometry>
4823 if constexpr (BSplineCore::parDim_ == 0)
4825 torch::zeros_like(BSplineCore::coeffs_[0])};
4827 return icurl<memory_optimized, Geometry>(
4828 G, xi, knot_indices,
4829 BSplineCore::template find_coeff_indices<memory_optimized>(
4832 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4865 template <
bool memory_optimized = false,
typename Geometry>
4869 const torch::Tensor &coeff_indices,
4871 const torch::Tensor &coeff_indices_G)
const {
4873 if constexpr (BSplineCore::parDim_ == 0)
4875 torch::zeros_like(BSplineCore::coeffs_[0])};
4878 det[0] = std::make_shared<torch::Tensor>(torch::reciprocal(
4879 G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G)
4882 return det * (curl<memory_optimized>(xi, knot_indices, coeff_indices) *
4883 G.template jac<memory_optimized>(xi, knot_indices_G,
4909 template <
bool memory_optimized = false>
4910 inline auto div(
const torch::Tensor &xi)
const {
4911 if constexpr (BSplineCore::parDim_ == 0)
4913 torch::zeros_like(BSplineCore::coeffs_[0])};
4921 template <
bool memory_optimized = false>
4923 if constexpr (BSplineCore::parDim_ == 0)
4925 torch::zeros_like(BSplineCore::coeffs_[0])};
4926 return div<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4952 template <
bool memory_optimized = false>
4956 if constexpr (BSplineCore::parDim_ == 0)
4958 torch::zeros_like(BSplineCore::coeffs_[0])};
4959 return div<memory_optimized>(
4961 BSplineCore::template find_coeff_indices<memory_optimized>(
4990 template <
bool memory_optimized = false>
4993 const torch::Tensor &coeff_indices)
const {
4995 static_assert(BSplineCore::parDim_ == BSplineCore::geoDim_,
4996 "div(.) requires parDim == geoDim");
4998 if constexpr (BSplineCore::parDim_ == 0)
5000 torch::zeros_like(BSplineCore::coeffs_[0])};
5005 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5006 assert(xi[i].sizes() == knot_indices[i].sizes());
5007 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5008 assert(xi[0].sizes() == xi[i].sizes());
5011 auto div_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5013 (*BSplineCore::template eval<
5015 memory_optimized>(xi, knot_indices, coeff_indices)[Is] +
5019 return div_(std::make_index_sequence<BSplineCore::parDim_>{});
5045 template <
bool memory_optimized = false,
typename Geometry>
5046 inline auto idiv(
const Geometry &G,
const torch::Tensor &xi) {
5047 if constexpr (BSplineCore::parDim_ == 0)
5049 torch::zeros_like(BSplineCore::coeffs_[0])};
5060 template <
bool memory_optimized = false,
typename Geometry>
5061 inline auto idiv(
const Geometry &G,
5063 if constexpr (BSplineCore::parDim_ == 0)
5065 torch::zeros_like(BSplineCore::coeffs_[0])};
5067 return idiv<memory_optimized, Geometry>(
5068 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5097 template <
bool memory_optimized = false,
typename Geometry>
5102 if constexpr (BSplineCore::parDim_ == 0)
5104 torch::zeros_like(BSplineCore::coeffs_[0])};
5106 return idiv<memory_optimized, Geometry>(
5107 G, xi, knot_indices,
5108 BSplineCore::template find_coeff_indices<memory_optimized>(
5111 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5145 template <
bool memory_optimized = false,
typename Geometry>
5146 inline auto idiv(
const Geometry &G,
5149 const torch::Tensor &coeff_indices,
5151 const torch::Tensor &coeff_indices_G)
const {
5152 if constexpr (BSplineCore::parDim_ == 0)
5154 torch::zeros_like(BSplineCore::coeffs_[0])};
5156 return ijac<memory_optimized, Geometry>(G, xi, knot_indices,
5157 coeff_indices, knot_indices_G,
5182 template <
bool memory_optimized = false>
5183 inline auto grad(
const torch::Tensor &xi)
const {
5185 static_assert(BSplineCore::geoDim_ == 1,
5186 "grad(.) requires 1D variable, use jac(.) instead");
5188 if constexpr (BSplineCore::parDim_ == 0)
5190 torch::zeros_like(BSplineCore::coeffs_[0])};
5199 template <
bool memory_optimized = false>
5202 static_assert(BSplineCore::geoDim_ == 1,
5203 "grad(.) requires 1D variable, use jac(.) instead");
5205 if constexpr (BSplineCore::parDim_ == 0)
5207 torch::zeros_like(BSplineCore::coeffs_[0])};
5209 return grad<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5234 template <
bool memory_optimized = false>
5239 static_assert(BSplineCore::geoDim_ == 1,
5240 "grad(.) requires 1D variable, use jac(.) instead");
5242 if constexpr (BSplineCore::parDim_ == 0)
5244 torch::zeros_like(BSplineCore::coeffs_[0])};
5246 return grad<memory_optimized>(
5248 BSplineCore::template find_coeff_indices<memory_optimized>(
5276 template <
bool memory_optimized = false>
5279 const torch::Tensor &coeff_indices)
const {
5281 static_assert(BSplineCore::geoDim_ == 1,
5282 "grad(.) requires 1D variable, use jac(.) instead");
5284 if constexpr (BSplineCore::parDim_ == 0)
5286 torch::zeros_like(BSplineCore::coeffs_[0])};
5290 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5291 assert(xi[i].sizes() == knot_indices[i].sizes());
5292 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5293 assert(xi[0].sizes() == xi[i].sizes());
5296 auto grad_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5298 BSplineCore::template eval<
5300 memory_optimized>(xi, knot_indices, coeff_indices)...};
5303 return grad_(std::make_index_sequence<BSplineCore::parDim_>{});
5328 template <
bool memory_optimized = false,
typename Geometry>
5329 inline auto igrad(
const Geometry &G,
const torch::Tensor &xi)
const {
5330 if constexpr (BSplineCore::parDim_ == 0)
5332 torch::zeros_like(BSplineCore::coeffs_[0])};
5343 template <
bool memory_optimized = false,
typename Geometry>
5346 if constexpr (BSplineCore::parDim_ == 0)
5348 torch::zeros_like(BSplineCore::coeffs_[0])};
5350 return igrad<memory_optimized, Geometry>(
5351 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5378 template <
bool memory_optimized = false,
typename Geometry>
5383 if constexpr (BSplineCore::parDim_ == 0)
5385 torch::zeros_like(BSplineCore::coeffs_[0])};
5387 return igrad<memory_optimized, Geometry>(
5388 G, xi, knot_indices,
5389 BSplineCore::template find_coeff_indices<memory_optimized>(
5392 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5425 template <
bool memory_optimized = false,
typename Geometry>
5429 const torch::Tensor &coeff_indices,
5431 const torch::Tensor &coeff_indices_G)
const {
5432 if constexpr (BSplineCore::parDim_ == 0)
5434 torch::zeros_like(BSplineCore::coeffs_[0])};
5436 return grad<memory_optimized>(xi, knot_indices, coeff_indices) *
5437 G.template jac<memory_optimized>(xi, knot_indices_G,
5478 template <
bool memory_optimized = false>
5479 inline auto hess(
const torch::Tensor &xi)
const {
5480 if constexpr (BSplineCore::parDim_ == 0)
5482 torch::zeros_like(BSplineCore::coeffs_[0])};
5491 template <
bool memory_optimized = false>
5493 if constexpr (BSplineCore::parDim_ == 0)
5495 torch::zeros_like(BSplineCore::coeffs_[0])};
5497 return hess<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5537 template <
bool memory_optimized = false>
5541 if constexpr (BSplineCore::parDim_ == 0)
5543 torch::zeros_like(BSplineCore::coeffs_[0])};
5545 return hess<memory_optimized>(
5547 BSplineCore::template find_coeff_indices<memory_optimized>(
5589 template <
bool memory_optimized = false>
5592 const torch::Tensor &coeff_indices)
const {
5594 if constexpr (BSplineCore::parDim_ == 0)
5596 torch::zeros_like(BSplineCore::coeffs_[0])};
5600 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5601 assert(xi[i].sizes() == knot_indices[i].sizes());
5602 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5603 assert(xi[0].sizes() == xi[i].sizes());
5606 auto hess_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5608 BSplineCore::geoDim_, BSplineCore::parDim_>{
5609 BSplineCore::template eval<
5613 10, Is % BSplineCore::parDim_>::value),
5614 memory_optimized>(xi, knot_indices, coeff_indices)...}
5618 return hess_(std::make_index_sequence<BSplineCore::parDim_ *
5619 BSplineCore::parDim_>{});
5650 template <
bool memory_optimized = false,
typename Geometry>
5651 inline auto ihess(
const Geometry &G,
const torch::Tensor &xi)
const {
5652 if constexpr (BSplineCore::parDim_ == 0)
5654 torch::zeros_like(BSplineCore::coeffs_[0])};
5665 template <
bool memory_optimized = false,
typename Geometry>
5668 if constexpr (BSplineCore::parDim_ == 0)
5670 torch::zeros_like(BSplineCore::coeffs_[0])};
5672 return ihess<memory_optimized, Geometry>(
5673 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5706 template <
bool memory_optimized = false,
typename Geometry>
5711 if constexpr (BSplineCore::parDim_ == 0)
5713 torch::zeros_like(BSplineCore::coeffs_[0])};
5715 return ihess<memory_optimized, Geometry>(
5716 G, xi, knot_indices,
5717 BSplineCore::template find_coeff_indices<memory_optimized>(
5720 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5758 template <
bool memory_optimized = false,
typename Geometry>
5762 const torch::Tensor &coeff_indices,
5764 const torch::Tensor &coeff_indices_G)
const {
5766 if constexpr (BSplineCore::parDim_ == 0)
5768 torch::zeros_like(BSplineCore::coeffs_[0])};
5771 BSplineCore::parDim_, BSplineCore::geoDim_>
5774 auto hessG = G.template hess<memory_optimized>(xi, knot_indices_G,
5776 auto ijacG = ijac<memory_optimized>(G, xi, knot_indices, coeff_indices,
5777 knot_indices_G, coeff_indices_G);
5779 for (
short_t component = 0; component < BSplineCore::geoDim_;
5781 auto hess_component =
5782 hess<memory_optimized>(xi, knot_indices, coeff_indices)
5785 for (
short_t k = 0; k < hessG.slices(); ++k) {
5786 hess_component -= ijacG(component, k) * hessG.slice(k);
5789 auto jacInv = G.template jac<memory_optimized>(xi, knot_indices_G,
5792 auto hessu_component = jacInv.tr() * hess_component * jacInv;
5794 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5795 for (
short_t j = 0; j < BSplineCore::parDim_; ++j)
5796 hessu.set(i, j, component, hessu_component(i, j));
5834 template <
bool memory_optimized = false>
5835 inline auto jac(
const torch::Tensor &xi)
const {
5836 if constexpr (BSplineCore::parDim_ == 0)
5838 torch::zeros_like(BSplineCore::coeffs_[0])};
5847 template <
bool memory_optimized = false>
5849 if constexpr (BSplineCore::parDim_ == 0)
5851 torch::zeros_like(BSplineCore::coeffs_[0])};
5853 return jac<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5888 template <
bool memory_optimized = false>
5892 if constexpr (BSplineCore::parDim_ == 0)
5894 torch::zeros_like(BSplineCore::coeffs_[0])};
5896 return jac<memory_optimized>(
5898 BSplineCore::template find_coeff_indices<memory_optimized>(
5941 template <
bool memory_optimized = false>
5944 const torch::Tensor &coeff_indices)
const {
5946 if constexpr (BSplineCore::parDim_ == 0)
5948 torch::zeros_like(BSplineCore::coeffs_[0])};
5952 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5953 assert(xi[i].sizes() == knot_indices[i].sizes());
5954 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5955 assert(xi[0].sizes() == xi[i].sizes());
5958 auto jac_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5960 BSplineCore::geoDim_>{
5961 BSplineCore::template eval<
5963 memory_optimized>(xi, knot_indices, coeff_indices)...}
5967 return jac_(std::make_index_sequence<BSplineCore::parDim_>{});
5992 template <
bool memory_optimized = false,
typename Geometry>
5993 inline auto ijac(
const Geometry &G,
const torch::Tensor &xi)
const {
5994 if constexpr (BSplineCore::parDim_ == 0)
5996 torch::zeros_like(BSplineCore::coeffs_[0])};
6007 template <
bool memory_optimized = false,
typename Geometry>
6008 inline auto ijac(
const Geometry &G,
6010 if constexpr (BSplineCore::parDim_ == 0)
6012 torch::zeros_like(BSplineCore::coeffs_[0])};
6014 return ijac<memory_optimized, Geometry>(
6015 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
6043 template <
bool memory_optimized = false,
typename Geometry>
6048 if constexpr (BSplineCore::parDim_ == 0)
6050 torch::zeros_like(BSplineCore::coeffs_[0])};
6052 return ijac<memory_optimized, Geometry>(
6053 G, xi, knot_indices,
6054 BSplineCore::template find_coeff_indices<memory_optimized>(
6057 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
6090 template <
bool memory_optimized = false,
typename Geometry>
6091 inline auto ijac(
const Geometry &G,
6094 const torch::Tensor &coeff_indices,
6096 const torch::Tensor &coeff_indices_G)
const {
6097 if constexpr (BSplineCore::parDim_ == 0)
6099 torch::zeros_like(BSplineCore::coeffs_[0])};
6101 return jac<memory_optimized>(xi, knot_indices, coeff_indices) *
6102 G.template jac<memory_optimized>(xi, knot_indices_G,
6127 template <
bool memory_optimized = false>
6128 inline auto lapl(
const torch::Tensor &xi)
const {
6129 if constexpr (BSplineCore::parDim_ == 0)
6131 torch::zeros_like(BSplineCore::coeffs_[0])};
6140 template <
bool memory_optimized = false>
6142 if constexpr (BSplineCore::parDim_ == 0)
6144 torch::zeros_like(BSplineCore::coeffs_[0])};
6146 return lapl<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
6171 template <
bool memory_optimized = false>
6175 if constexpr (BSplineCore::parDim_ == 0)
6177 torch::zeros_like(BSplineCore::coeffs_[0])};
6179 return lapl<memory_optimized>(
6181 BSplineCore::template find_coeff_indices<memory_optimized>(
6209 template <
bool memory_optimized = false>
6212 const torch::Tensor &coeff_indices)
const {
6214 if constexpr (BSplineCore::parDim_ == 0)
6216 torch::zeros_like(BSplineCore::coeffs_[0])};
6220 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
6221 assert(xi[i].sizes() == knot_indices[i].sizes());
6222 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
6223 assert(xi[0].sizes() == xi[i].sizes());
6226 auto lapl_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
6228 (BSplineCore::template eval<
6230 memory_optimized>(xi, knot_indices, coeff_indices) +
6235 return lapl_(std::make_index_sequence<BSplineCore::parDim_>{});
6267 template <
bool memory_optimized = false,
typename Geometry>
6268 auto ilapl(
const Geometry &G,
const torch::Tensor &xi)
const {
6269 if constexpr (BSplineCore::parDim_ == 0)
6271 torch::zeros_like(BSplineCore::coeffs_[0])};
6282 template <
bool memory_optimized = false,
typename Geometry>
6285 if constexpr (BSplineCore::parDim_ == 0)
6287 torch::zeros_like(BSplineCore::coeffs_[0])};
6289 return ilapl<memory_optimized, Geometry>(
6290 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
6324 template <
bool memory_optimized = false,
typename Geometry>
6329 if constexpr (BSplineCore::parDim_ == 0)
6331 torch::zeros_like(BSplineCore::coeffs_[0])};
6333 return ilapl<memory_optimized, Geometry>(
6334 G, xi, knot_indices,
6335 BSplineCore::template find_coeff_indices<memory_optimized>(
6338 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
6378 template <
bool memory_optimized = false,
typename Geometry>
6382 const torch::Tensor &coeff_indices,
6384 const torch::Tensor &coeff_indices_G)
const {
6386 if constexpr (BSplineCore::parDim_ == 0)
6388 torch::zeros_like(BSplineCore::coeffs_[0])};
6392 hess<memory_optimized>(xi, knot_indices, coeff_indices).slice(0);
6396 igrad<memory_optimized>(G, xi, knot_indices, coeff_indices,
6397 knot_indices_G, coeff_indices_G);
6398 auto hessG = G.template hess<memory_optimized>(xi, knot_indices_G,
6400 assert(igradG.cols() == hessG.slices());
6401 for (
short_t k = 0; k < hessG.slices(); ++k)
6402 hessu -= igradG(0, k) * hessG.slice(k);
6406 G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G)
6409 return (jacInv.tr() * hessu * jacInv).trace();
6418#ifdef IGANET_WITH_MATPLOT
6419 template <
typename Backend = matplot::backend::gnuplot>
6421 template <
typename Backend =
void>
6423 inline auto plot(
const nlohmann::json &json = {})
const {
6424 return plot<Backend>(*
this, json);
6434#ifdef IGANET_WITH_MATPLOT
6435 template <
typename Backend = matplot::backend::gnuplot>
6437 template <
typename Backend =
void>
6440 const nlohmann::json &json = {})
const {
6442 return plot<Backend>(*
this, xi, json);
6452#ifdef IGANET_WITH_MATPLOT
6453 template <
typename Backend = matplot::backend::gnuplot>
6455 template <
typename Backend =
void>
6459 const nlohmann::json &json = {})
const {
6461 return plot<Backend>(*
this, xi, json);
6471#ifdef IGANET_WITH_MATPLOT
6472 template <
typename Backend = matplot::backend::gnuplot,
6473 typename BSplineCoreColor>
6475 template <
typename Backend =
void,
typename BSplineCoreColor>
6478 const nlohmann::json &json = {})
const {
6479#ifdef IGANET_WITH_MATPLOT
6480 static_assert(BSplineCore::parDim() == BSplineCoreColor::parDim(),
6481 "Parametric dimensions must match");
6483 if ((
void *)
this != (
void *)&color && BSplineCoreColor::geoDim() > 1)
6484 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6486 if constexpr (BSplineCore::parDim() == 1 && BSplineCore::geoDim() == 1) {
6492 int64_t res0 = BSplineCore::ncoeffs(0);
6493 if (json.contains(
"res0"))
6494 res0 = json[
"res0"].get<int64_t>();
6497 auto f = matplot::figure<Backend>(
false);
6498 f->backend()->run_command(
"unset warnings");
6500 auto ax = f->current_axes();
6504 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6507 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6508 Coords(0), torch::kCPU);
6509 auto XAccessor = std::get<1>(Coords_cpu);
6511 auto [Coords_cpu, XAccessor] =
6512 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6513 Coords(0), torch::kCPU);
6516 matplot::vector_1d Xfine(res0, 0.0);
6517 matplot::vector_1d Yfine(res0, 0.0);
6519#pragma omp parallel for simd
6520 for (int64_t i = 0; i < res0; ++i)
6521 Xfine[i] = XAccessor[i];
6524 if ((
void *)
this != (
void *)&color) {
6525 if constexpr (BSplineCoreColor::geoDim_ == 1) {
6529 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6533 1>(Color(0), torch::kCPU);
6534 auto CAccessor = std::get<1>(Color_cpu);
6536 auto [Color_cpu, CAccessor] =
6538 1>(Color(0), torch::kCPU);
6541 matplot::vector_1d Cfine(res0, 0.0);
6543#pragma omp parallel for simd
6544 for (int64_t i = 0; i < res0; ++i)
6545 Cfine[i] = CAccessor[i];
6547 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
6548 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
6550 auto Cmap = matplot::colormap();
6552 auto a = Cmap.size() / (Cmax - Cmin);
6556 ax->hold(matplot::on);
6557 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
6558 ax->plot({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]})
6560 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
6561 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
6562 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
6563 ax->hold(matplot::off);
6564 matplot::colorbar(ax);
6566 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6569 ax->plot(Xfine, Yfine,
"b-")->line_width(2);
6573 if (
json.contains(
"cnet"))
6574 cnet =
json[
"cnet"].get<
bool>();
6580 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6581 BSplineCore::coeffs(0), torch::kCPU);
6582 auto xAccessor = std::get<1>(coeffs_cpu);
6584 auto [coeffs_cpu, xAccessor] =
6585 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6586 BSplineCore::coeffs(0), torch::kCPU);
6588 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6589 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6591#pragma omp parallel for simd
6592 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6593 X[i] = xAccessor[i];
6597 ax->hold(matplot::on);
6598 ax->plot(X, Y,
".k-")->line_width(1);
6599 ax->hold(matplot::off);
6603 if (
json.contains(
"title"))
6604 ax->title(json[
"title"].get<std::string>());
6606 ax->title(
"BSpline: [0,1] -> R");
6609 if (
json.contains(
"xlabel"))
6610 ax->xlabel(json[
"xlabel"].get<std::string>());
6615 if (
json.contains(
"ylabel"))
6616 ax->ylabel(json[
"ylabel"].get<std::string>());
6623 else if constexpr (BSplineCore::parDim_ == 1 && BSplineCore::geoDim_ == 2) {
6629 int64_t res0 = BSplineCore::ncoeffs(0);
6630 if (
json.contains(
"res0"))
6631 res0 =
json[
"res0"].get<int64_t>();
6634 auto f = matplot::figure<Backend>(
false);
6635 f->backend()->run_command(
"unset warnings");
6637 auto ax = f->current_axes();
6641 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6644 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6645 Coords, torch::kCPU);
6646 auto XAccessor = std::get<1>(Coords_cpu)[0];
6647 auto YAccessor = std::get<1>(Coords_cpu)[1];
6649 auto [Coords0_cpu, XAccessor] =
6650 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6651 Coords(0), torch::kCPU);
6652 auto [Coords1_cpu, YAccessor] =
6653 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6654 Coords(1), torch::kCPU);
6657 matplot::vector_1d Xfine(res0, 0.0);
6658 matplot::vector_1d Yfine(res0, 0.0);
6660#pragma omp parallel for simd
6661 for (int64_t i = 0; i < res0; ++i) {
6662 Xfine[i] = XAccessor[i];
6663 Yfine[i] = YAccessor[i];
6667 if ((
void *)
this != (
void *)&color) {
6668 if constexpr (BSplineCoreColor::geoDim() == 1) {
6672 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6676 1>(Color(0), torch::kCPU);
6677 auto CAccessor = std::get<1>(Color_cpu);
6679 auto [Color_cpu, CAccessor] =
6681 1>(Color(0), torch::kCPU);
6684 matplot::vector_1d Cfine(res0, 0.0);
6686#pragma omp parallel for simd
6687 for (int64_t i = 0; i < res0; ++i) {
6688 Cfine[i] = CAccessor[i];
6691 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
6692 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
6694 auto Cmap = matplot::colormap();
6696 auto a = Cmap.size() / (Cmax - Cmin);
6700 ax->hold(matplot::on);
6701 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
6702 ax->plot({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]})
6704 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
6705 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
6706 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
6707 ax->hold(matplot::off);
6708 matplot::colorbar(ax);
6710 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6713 ax->plot(Xfine, Yfine,
"b-")->line_width(2);
6717 if (
json.contains(
"cnet"))
6718 cnet =
json[
"cnet"].get<
bool>();
6724 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6725 BSplineCore::coeffs(), torch::kCPU);
6726 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6727 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6729 auto [coeffs0_cpu, xAccessor] =
6730 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6731 BSplineCore::coeffs(0), torch::kCPU);
6732 auto [coeffs1_cpu, yAccessor] =
6733 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6734 BSplineCore::coeffs(1), torch::kCPU);
6737 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6738 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6740#pragma omp parallel for simd
6741 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6742 X[i] = xAccessor[i];
6743 Y[i] = yAccessor[i];
6747 ax->hold(matplot::on);
6748 ax->plot(X, Y,
".k-")->line_width(1);
6749 ax->hold(matplot::off);
6753 if (
json.contains(
"title"))
6754 ax->title(json[
"title"].get<std::string>());
6756 ax->title(
"BSpline: [0,1] -> R^2");
6759 if (
json.contains(
"xlabel"))
6760 ax->xlabel(json[
"xlabel"].get<std::string>());
6765 if (
json.contains(
"ylabel"))
6766 ax->ylabel(json[
"ylabel"].get<std::string>());
6773 else if constexpr (BSplineCore::parDim() == 1 &&
6774 BSplineCore::geoDim() == 3) {
6780 int64_t res0 = BSplineCore::ncoeffs(0);
6781 if (
json.contains(
"res0"))
6782 res0 =
json[
"res0"].get<int64_t>();
6785 auto f = matplot::figure<Backend>(
false);
6786 f->backend()->run_command(
"unset warnings");
6788 auto ax = f->current_axes();
6791 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6794 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6795 Coords, torch::kCPU);
6796 auto XAccessor = std::get<1>(Coords_cpu)[0];
6797 auto YAccessor = std::get<1>(Coords_cpu)[1];
6798 auto ZAccessor = std::get<1>(Coords_cpu)[2];
6800 auto [Coords0_cpu, XAccessor] =
6801 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6802 Coords(0), torch::kCPU);
6803 auto [Coords1_cpu, YAccessor] =
6804 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6805 Coords(1), torch::kCPU);
6806 auto [Coords2_cpu, ZAccessor] =
6807 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6808 Coords(2), torch::kCPU);
6812 matplot::vector_1d Xfine(res0, 0.0);
6813 matplot::vector_1d Yfine(res0, 0.0);
6814 matplot::vector_1d Zfine(res0, 0.0);
6816#pragma omp parallel for simd
6817 for (int64_t i = 0; i < res0; ++i) {
6818 Xfine[i] = XAccessor[i];
6819 Yfine[i] = YAccessor[i];
6820 Zfine[i] = ZAccessor[i];
6824 if ((
void *)
this != (
void *)&color) {
6825 if constexpr (BSplineCoreColor::geoDim() == 1) {
6828 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6832 1>(Color(0), torch::kCPU);
6833 auto CAccessor = std::get<1>(Color_cpu);
6835 auto [Color_cpu, CAccessor] =
6837 1>(Color(0), torch::kCPU);
6841 matplot::vector_1d Cfine(matplot::vector_1d(res0, 0.0));
6843#pragma omp parallel for simd
6844 for (int64_t i = 0; i < res0; ++i) {
6845 Cfine[i] = CAccessor[i];
6848 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
6849 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
6851 auto Cmap = matplot::colormap();
6853 auto a = Cmap.size() / (Cmax - Cmin);
6857 ax->hold(matplot::on);
6858 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
6859 ax->plot3({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]},
6860 {Zfine[i], Zfine[i + 1]})
6862 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
6863 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
6864 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
6865 ax->hold(matplot::off);
6866 matplot::colorbar(ax);
6868 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6871 ax->plot3(Xfine, Yfine, Zfine,
"b-")->line_width(2);
6875 if (
json.contains(
"cnet"))
6876 cnet =
json[
"cnet"].get<
bool>();
6882 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6883 BSplineCore::coeffs(), torch::kCPU);
6884 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6885 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6886 auto zAccessor = std::get<1>(coeffs_cpu)[2];
6888 auto [coeffs0_cpu, xAccessor] =
6889 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6890 BSplineCore::coeffs(0), torch::kCPU);
6891 auto [coeffs1_cpu, yAccessor] =
6892 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6893 BSplineCore::coeffs(1), torch::kCPU);
6894 auto [coeffs2_cpu, zAccessor] =
6895 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6896 BSplineCore::coeffs(2), torch::kCPU);
6899 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6900 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6901 matplot::vector_1d Z(BSplineCore::ncoeffs(0), 0.0);
6903#pragma omp parallel for simd
6904 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6905 X[i] = xAccessor[i];
6906 Y[i] = yAccessor[i];
6907 Z[i] = zAccessor[i];
6911 ax->hold(matplot::on);
6912 ax->plot3(X, Y, Z,
".k-")->line_width(1);
6913 ax->hold(matplot::off);
6917 if (
json.contains(
"title"))
6918 ax->title(json[
"title"].get<std::string>());
6920 ax->title(
"BSpline: [0,1] -> R^3");
6923 if (
json.contains(
"xlabel"))
6924 ax->xlabel(json[
"xlabel"].get<std::string>());
6929 if (
json.contains(
"ylabel"))
6930 ax->ylabel(json[
"ylabel"].get<std::string>());
6935 if (
json.contains(
"zlabel"))
6936 ax->zlabel(json[
"zlabel"].get<std::string>());
6943 else if constexpr (BSplineCore::parDim() == 2 &&
6944 BSplineCore::geoDim() == 2) {
6950 int64_t res0 = BSplineCore::ncoeffs(0);
6951 int64_t res1 = BSplineCore::ncoeffs(1);
6952 if (
json.contains(
"res0"))
6953 res0 =
json[
"res0"].get<int64_t>();
6954 if (
json.contains(
"res1"))
6955 res1 = json[
"res1"].get<int64_t>();
6958 auto f = matplot::figure<Backend>(
false);
6959 f->backend()->run_command(
"unset warnings");
6961 auto ax = f->current_axes();
6964 utils::TensorArray<2> meshgrid = utils::to_array<2>(
6965 torch::meshgrid({torch::linspace(0, 1, res0, BSplineCore::options_),
6966 torch::linspace(0, 1, res1, BSplineCore::options_)},
6968 auto Coords = BSplineCore::eval(meshgrid);
6971 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6972 Coords, torch::kCPU);
6973 auto XAccessor = std::get<1>(Coords_cpu)[0];
6974 auto YAccessor = std::get<1>(Coords_cpu)[1];
6976 auto [Coords0_cpu, XAccessor] =
6977 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6978 Coords(0), torch::kCPU);
6979 auto [Coords1_cpu, YAccessor] =
6980 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6981 Coords(1), torch::kCPU);
6984 matplot::vector_2d Xfine(res1, matplot::vector_1d(res0, 0.0));
6985 matplot::vector_2d Yfine(res1, matplot::vector_1d(res0, 0.0));
6986 matplot::vector_2d Zfine(res1, matplot::vector_1d(res0, 0.0));
6988#pragma omp parallel for simd collapse(2)
6989 for (int64_t i = 0; i < res0; ++i)
6990 for (int64_t j = 0; j < res1; ++j) {
6991 Xfine[j][i] = XAccessor[j][i];
6992 Yfine[j][i] = YAccessor[j][i];
6996 if ((
void *)
this != (
void *)&color) {
6997 if constexpr (BSplineCoreColor::geoDim() == 1) {
7000 auto Color = color.eval(meshgrid);
7003 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
7004 2>(Color, torch::kCPU);
7005 auto CAccessor = std::get<1>(Color_cpu)[0];
7007 auto [Color0_cpu, CAccessor] =
7008 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
7009 2>(Color(0), torch::kCPU);
7012 matplot::vector_2d Cfine(res1, matplot::vector_1d(res0, 0.0));
7014#pragma omp parallel for simd collapse(2)
7015 for (int64_t i = 0; i < res0; ++i)
7016 for (int64_t j = 0; j < res1; ++j)
7017 Cfine[j][i] = CAccessor[j][i];
7021 ax->mesh(Xfine, Yfine, Cfine)->hidden_3d(
false);
7022 matplot::colorbar(ax);
7024 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
7028 matplot::colormap(std::vector<std::vector<double>>{{0.0, 0.0, 1.0}});
7029 ax->mesh(Xfine, Yfine, Zfine)->hidden_3d(
false).line_width(2);
7033 if (
json.contains(
"cnet"))
7034 cnet =
json[
"cnet"].get<
bool>();
7040 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7041 BSplineCore::coeffs(), torch::kCPU);
7042 auto xAccessor = std::get<1>(coeffs_cpu)[0];
7043 auto yAccessor = std::get<1>(coeffs_cpu)[1];
7045 auto [coeffs0_cpu, xAccessor] =
7046 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7047 BSplineCore::coeffs(0), torch::kCPU);
7048 auto [coeffs1_cpu, yAccessor] =
7049 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7050 BSplineCore::coeffs(1), torch::kCPU);
7053 matplot::vector_2d X(BSplineCore::ncoeffs(1),
7054 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
7055 matplot::vector_2d Y(BSplineCore::ncoeffs(1),
7056 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
7057 matplot::vector_2d Z(BSplineCore::ncoeffs(1),
7058 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
7060#pragma omp parallel for simd collapse(2)
7061 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i)
7062 for (int64_t j = 0; j < BSplineCore::ncoeffs(1); ++j) {
7063 X[j][i] = xAccessor[j * BSplineCore::ncoeffs(0) + i];
7064 Y[j][i] = yAccessor[j * BSplineCore::ncoeffs(0) + i];
7068 ax->hold(matplot::on);
7070 ->palette_map_at_surface(
true)
7073 for (std::size_t i = 0; i < X.size(); ++i)
7074 ax->scatter3(X[i], Y[i], Z[i],
"k.");
7075 ax->hold(matplot::off);
7079 if (
json.contains(
"title"))
7080 ax->title(json[
"title"].get<std::string>());
7082 ax->title(
"BSpline: [0,1]^2 -> R^2");
7085 if (
json.contains(
"xlabel"))
7086 ax->xlabel(json[
"xlabel"].get<std::string>());
7091 if (
json.contains(
"ylabel"))
7092 ax->ylabel(json[
"ylabel"].get<std::string>());
7097 if (
json.contains(
"zlabel"))
7098 ax->zlabel(json[
"zlabel"].get<std::string>());
7105 else if constexpr (BSplineCore::parDim() == 2 &&
7106 BSplineCore::geoDim() == 3) {
7112 int64_t res0 = BSplineCore::ncoeffs(0);
7113 int64_t res1 = BSplineCore::ncoeffs(1);
7114 if (
json.contains(
"res0"))
7115 res0 =
json[
"res0"].get<int64_t>();
7116 if (
json.contains(
"res1"))
7117 res1 = json[
"res1"].get<int64_t>();
7120 auto f = matplot::figure<Backend>(
false);
7121 f->backend()->run_command(
"unset warnings");
7123 auto ax = f->current_axes();
7126 utils::TensorArray<2> meshgrid = utils::to_array<2>(
7127 torch::meshgrid({torch::linspace(0, 1, res0, BSplineCore::options_),
7128 torch::linspace(0, 1, res1, BSplineCore::options_)},
7130 auto Coords = BSplineCore::eval(meshgrid);
7133 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
7134 Coords, torch::kCPU);
7135 auto XAccessor = std::get<1>(Coords_cpu)[0];
7136 auto YAccessor = std::get<1>(Coords_cpu)[1];
7137 auto ZAccessor = std::get<1>(Coords_cpu)[2];
7139 auto [Coords0_cpu, XAccessor] =
7140 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
7141 Coords(0), torch::kCPU);
7142 auto [Coords1_cpu, YAccessor] =
7143 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
7144 Coords(1), torch::kCPU);
7145 auto [Coords2_cpu, ZAccessor] =
7146 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
7147 Coords(2), torch::kCPU);
7150 matplot::vector_2d Xfine(res1, matplot::vector_1d(res0, 0.0));
7151 matplot::vector_2d Yfine(res1, matplot::vector_1d(res0, 0.0));
7152 matplot::vector_2d Zfine(res1, matplot::vector_1d(res0, 0.0));
7154#pragma omp parallel for simd collapse(2)
7155 for (int64_t i = 0; i < res0; ++i)
7156 for (int64_t j = 0; j < res1; ++j) {
7157 Xfine[j][i] = XAccessor[j][i];
7158 Yfine[j][i] = YAccessor[j][i];
7159 Zfine[j][i] = ZAccessor[j][i];
7163 if ((
void *)
this != (
void *)&color) {
7164 if constexpr (BSplineCoreColor::geoDim() == 1) {
7167 auto Color = color.eval(meshgrid);
7170 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
7171 2>(Color, torch::kCPU);
7172 auto CAccessor = std::get<1>(Color_cpu)[0];
7174 auto [Color_cpu, CAccessor] =
7175 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
7176 2>(Color(0), torch::kCPU);
7179 matplot::vector_2d Cfine(res1, matplot::vector_1d(res0, 0.0));
7181#pragma omp parallel for simd collapse(2)
7182 for (int64_t i = 0; i < res0; ++i)
7183 for (int64_t j = 0; j < res1; ++j) {
7184 Cfine[j][i] = CAccessor[j][i];
7188 ax->mesh(Xfine, Yfine, Zfine, Cfine)->hidden_3d(
false);
7189 matplot::colorbar(ax);
7191 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
7194 matplot::colormap(std::vector<std::vector<double>>{{0.0, 0.0, 1.0}});
7195 ax->mesh(Xfine, Yfine, Zfine)->hidden_3d(
false).line_width(2);
7199 if (
json.contains(
"cnet"))
7200 cnet =
json[
"cnet"].get<
bool>();
7206 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7207 BSplineCore::coeffs(), torch::kCPU);
7208 auto xAccessor = std::get<1>(coeffs_cpu)[0];
7209 auto yAccessor = std::get<1>(coeffs_cpu)[1];
7210 auto zAccessor = std::get<1>(coeffs_cpu)[2];
7212 auto [coeffs0_cpu, xAccessor] =
7213 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7214 BSplineCore::coeffs(0), torch::kCPU);
7215 auto [coeffs1_cpu, yAccessor] =
7216 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7217 BSplineCore::coeffs(1), torch::kCPU);
7218 auto [coeffs2_cpu, zAccessor] =
7219 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7220 BSplineCore::coeffs(2), torch::kCPU);
7223 matplot::vector_2d X(BSplineCore::ncoeffs(1),
7224 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
7225 matplot::vector_2d Y(BSplineCore::ncoeffs(1),
7226 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
7227 matplot::vector_2d Z(BSplineCore::ncoeffs(1),
7228 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
7230#pragma omp parallel for simd collapse(2)
7231 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i)
7232 for (int64_t j = 0; j < BSplineCore::ncoeffs(1); ++j) {
7233 X[j][i] = xAccessor[j * BSplineCore::ncoeffs(0) + i];
7234 Y[j][i] = yAccessor[j * BSplineCore::ncoeffs(0) + i];
7235 Z[j][i] = zAccessor[j * BSplineCore::ncoeffs(0) + i];
7239 ax->hold(matplot::on);
7241 ->palette_map_at_surface(
true)
7244 for (std::size_t i = 0; i < X.size(); ++i)
7245 ax->scatter3(X[i], Y[i], Z[i],
"k.");
7246 ax->hold(matplot::off);
7250 if (
json.contains(
"title"))
7251 ax->title(json[
"title"].get<std::string>());
7253 ax->title(
"BSpline: [0,1]^2 -> R^3");
7256 if (
json.contains(
"xlabel"))
7257 ax->xlabel(json[
"xlabel"].get<std::string>());
7262 if (
json.contains(
"ylabel"))
7263 ax->ylabel(json[
"ylabel"].get<std::string>());
7268 if (
json.contains(
"zlabel"))
7269 ax->zlabel(json[
"zlabel"].get<std::string>());
7277 throw std::runtime_error(
7278 "Unsupported combination of parametric/geometric dimensions");
7280 throw std::runtime_error(
7281 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
7295#ifdef IGANET_WITH_MATPLOT
7296 template <
typename Backend = matplot::backend::gnuplot,
7297 typename BSplineCoreColor>
7299 template <
typename Backend =
void,
typename BSplineCoreColor>
7303 const nlohmann::json &json = {})
const {
7305#ifdef IGANET_WITH_MATPLOT
7306 auto f = plot<Backend>(color, json);
7307 auto ax = f->current_axes();
7311 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7313 auto xiAccessor = std::get<1>(xi_cpu);
7315 auto [xi_cpu, xiAccessor] =
7316 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7320 if constexpr (BSplineCore::parDim_ == 1) {
7321 matplot::vector_1d X(xi[0].size(0), 0.0);
7322 matplot::vector_1d Y(xi[0].size(0), 0.0);
7324#pragma omp parallel for simd
7325 for (int64_t i = 0; i < xi[0].size(0); ++i) {
7326 X[i] = xiAccessor[0][i];
7329 ax->hold(matplot::on);
7330 ax->scatter(X, Y,
".");
7331 ax->hold(matplot::off);
7332 }
else if constexpr (BSplineCore::parDim_ == 2) {
7333 matplot::vector_1d X(xi[0].size(0), 0.0);
7334 matplot::vector_1d Y(xi[0].size(0), 0.0);
7335 matplot::vector_1d Z(xi[0].size(0), 0.0);
7337#pragma omp parallel for simd
7338 for (int64_t i = 0; i < xi[0].size(0); ++i) {
7339 X[i] = xiAccessor[0][i];
7340 Y[i] = xiAccessor[1][i];
7343 ax->hold(matplot::on);
7344 ax->scatter3(X, Y, Z,
".");
7345 ax->hold(matplot::off);
7346 }
else if constexpr (BSplineCore::parDim_ == 3) {
7347 matplot::vector_1d X(xi[0].size(0), 0.0);
7348 matplot::vector_1d Y(xi[0].size(0), 0.0);
7349 matplot::vector_1d Z(xi[0].size(0), 0.0);
7351#pragma omp parallel for simd
7352 for (int64_t i = 0; i < xi[0].size(0); ++i) {
7353 X[i] = xiAccessor[0][i];
7354 Y[i] = xiAccessor[1][i];
7355 Z[i] = xiAccessor[2][i];
7358 ax->hold(matplot::on);
7359 ax->scatter3(X, Y, Z,
".");
7360 ax->hold(matplot::off);
7362 throw std::runtime_error(
"Invalid parametric dimension");
7366 throw std::runtime_error(
7367 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
7381#ifdef IGANET_WITH_MATPLOT
7382 template <
typename Backend = matplot::backend::gnuplot,
7383 typename BSplineCoreColor>
7385 template <
typename Backend =
void,
typename BSplineCoreColor>
7390 const nlohmann::json &json = {})
const {
7392#ifdef IGANET_WITH_MATPLOT
7393 auto f = plot<Backend>(color, json);
7394 auto ax = f->current_axes();
7396 for (
const auto &xi : xi) {
7399 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7401 auto xiAccessor = std::get<1>(xi_cpu);
7403 auto [xi_cpu, xiAccessor] =
7404 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
7408 if constexpr (BSplineCore::parDim_ == 1) {
7409 matplot::vector_1d X(xi[0].size(0), 0.0);
7410 matplot::vector_1d Y(xi[0].size(0), 0.0);
7412#pragma omp parallel for simd
7413 for (int64_t i = 0; i < xi[0].size(0); ++i) {
7414 X[i] = xiAccessor[0][i];
7417 ax->hold(matplot::on);
7418 ax->scatter(X, Y,
".");
7419 ax->hold(matplot::off);
7420 }
else if constexpr (BSplineCore::parDim_ == 2) {
7421 matplot::vector_1d X(xi[0].size(0), 0.0);
7422 matplot::vector_1d Y(xi[0].size(0), 0.0);
7423 matplot::vector_1d Z(xi[0].size(0), 0.0);
7425#pragma omp parallel for simd
7426 for (int64_t i = 0; i < xi[0].size(0); ++i) {
7427 X[i] = xiAccessor[0][i];
7428 Y[i] = xiAccessor[1][i];
7431 ax->hold(matplot::on);
7432 ax->scatter3(X, Y, Z,
".");
7433 ax->hold(matplot::off);
7434 }
else if constexpr (BSplineCore::parDim_ == 3) {
7435 matplot::vector_1d X(xi[0].size(0), 0.0);
7436 matplot::vector_1d Y(xi[0].size(0), 0.0);
7437 matplot::vector_1d Z(xi[0].size(0), 0.0);
7439#pragma omp parallel for simd
7440 for (int64_t i = 0; i < xi[0].size(0); ++i) {
7441 X[i] = xiAccessor[0][i];
7442 Y[i] = xiAccessor[1][i];
7443 Z[i] = xiAccessor[2][i];
7446 ax->hold(matplot::on);
7447 ax->scatter3(X, Y, Z,
".");
7448 ax->hold(matplot::off);
7451 throw std::runtime_error(
"Invalid parametric dimension");
7455 throw std::runtime_error(
7456 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
7463 os << name() <<
"(\nparDim = " << BSplineCore::parDim()
7464 <<
", geoDim = " << BSplineCore::geoDim() <<
", degrees = ";
7466 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
7467 os << BSplineCore::degree(i) <<
"x";
7468 if (BSplineCore::parDim() > 0)
7469 os << BSplineCore::degree(BSplineCore::parDim() - 1);
7474 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
7475 os << BSplineCore::nknots(i) <<
"x";
7476 if (BSplineCore::parDim() > 0)
7477 os << BSplineCore::nknots(BSplineCore::parDim() - 1);
7481 os <<
", coeffs = ";
7482 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
7483 os << BSplineCore::ncoeffs(i) <<
"x";
7484 if (BSplineCore::parDim() > 0)
7485 os << BSplineCore::ncoeffs(BSplineCore::parDim() - 1);
7489 os <<
", options = "
7490 <<
static_cast<torch::TensorOptions
>(BSplineCore::options_);
7494 for (
const torch::Tensor &knots : BSplineCore::knots()) {
7495 os << (knots.is_view() ?
"view/" :
"owns/");
7496 os << (knots.is_contiguous() ?
"cont " :
"non-cont ");
7498 if (BSplineCore::parDim() > 0)
7499 os <<
"] = " << BSplineCore::knots();
7503 os <<
"\ncoeffs [ ";
7504 for (
const torch::Tensor &coeffs : BSplineCore::coeffs()) {
7505 os << (coeffs.is_view() ?
"view/" :
"owns/");
7506 os << (coeffs.is_contiguous() ?
"cont " :
"non-cont ");
7508 if (BSplineCore::ncumcoeffs() > 0)
7509 os <<
"] = " << BSplineCore::coeffs_view();
7530 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7531 result.coeffs(i) += other.coeffs(i);
7550 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7551 result.coeffs(i) -= other.coeffs(i);
7564 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7565 result.coeffs(i) *= s;
7575 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
7580 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7581 result.coeffs(i) *= v[i];
7594 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7595 result.coeffs(i) /= s;
7605 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
7610 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7611 result.coeffs(i) /= v[i];
7626 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7627 BSplineCore::coeffs(i) += other.coeffs(i);
7642 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7643 BSplineCore::coeffs(i) -= other.coeffs(i);
7653 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7654 BSplineCore::coeffs(i) *= s;
7663 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
7665 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7666 BSplineCore::coeffs(i) *= v[i];
7676 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7677 BSplineCore::coeffs(i) /= s;
7686 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
7688 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7689 BSplineCore::coeffs(i) /= v[i];
7696template <
typename real_t, short_t GeoDim, short_t... Degrees>
7711inline std::ostream &
7712operator<<(std::ostream &os,
7727template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
7728std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
7730 const std::array<iganet::short_t, ParDim> °rees,
7731 const std::array<int64_t, ParDim> &ncoeffs,
7734 static_assert(std::is_same_v<real_t, float> ||
7735 std::is_same_v<real_t, double> ||
7736 std::is_same_v<real_t, long double>,
7737 "Unsupported scalar type for JIT source generation");
7741 std::string includes = R
"(
7742#include <iganet/splines/bspline.hpp>
7745#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
7749 std::ostringstream src;
7750 src << "using real_t = " << iganet::type_name_v<real_t> <<
";\n"
7751 <<
"using patch_t = iganet::BSplinePatch<real_t, " << GeoDim <<
", "
7753 <<
"using bspline_t = iganet::UniformBSpline<real_t, " << GeoDim;
7754 for (
const auto degree : degrees)
7755 src <<
", " << degree;
7757 <<
"EXPORT std::shared_ptr<patch_t> create_patch(\n"
7758 <<
" const std::array<int64_t, " << ParDim <<
"> &ncoeffs,\n"
7759 <<
" enum iganet::init init, iganet::Options<real_t> options) {\n"
7760 <<
" return std::make_shared<bspline_t>(ncoeffs, init, options);\n"
7763 const auto libname =
7764 iganet::jit{}.compile(includes, src.str(),
"CreateUniformBSpline");
7765 auto handler = std::make_shared<iganet::DLHandler>(libname);
7767 using create_patch_fn =
7768 std::shared_ptr<patch_t> (*)(
const std::array<int64_t, ParDim> &,
7770 const auto create_patch =
7771 reinterpret_cast<create_patch_fn
>(handler->getSymbol(
"create_patch"));
7772 auto patch = create_patch(ncoeffs, init, options);
7773 auto *patch_ptr = patch.get();
7777 return std::shared_ptr<patch_t>(
7778 patch_ptr, [patch = std::move(patch),
7779 handler = std::move(handler)](patch_t *)
mutable {
7805inline std::ostream &
7806operator<<(std::ostream &os,
7821template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
7822std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
7824 const std::array<iganet::short_t, ParDim> °rees,
7825 const std::array<int64_t, ParDim> &ncoeffs,
7828 static_assert(std::is_same_v<real_t, float> ||
7829 std::is_same_v<real_t, double> ||
7830 std::is_same_v<real_t, long double>,
7831 "Unsupported scalar type for JIT source generation");
7835 std::string includes = R
"(
7836#include <iganet/splines/bspline.hpp>
7839#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
7843 std::ostringstream src;
7844 src << "using real_t = " << iganet::type_name_v<real_t> <<
";\n"
7845 <<
"using patch_t = iganet::BSplinePatch<real_t, " << GeoDim <<
", "
7847 <<
"using bspline_t = iganet::NonUniformBSpline<real_t, " << GeoDim;
7848 for (
const auto degree : degrees)
7849 src <<
", " << degree;
7851 <<
"EXPORT std::shared_ptr<patch_t> create_patch(\n"
7852 <<
" const std::array<int64_t, " << ParDim <<
"> &ncoeffs,\n"
7853 <<
" enum iganet::init init, iganet::Options<real_t> options) {\n"
7854 <<
" return std::make_shared<bspline_t>(ncoeffs, init, options);\n"
7857 const auto libname =
7858 iganet::jit{}.compile(includes, src.str(),
"CreateNonUniformBSpline");
7859 auto handler = std::make_shared<iganet::DLHandler>(libname);
7861 using create_patch_fn =
7862 std::shared_ptr<patch_t> (*)(
const std::array<int64_t, ParDim> &,
7864 const auto create_patch =
7865 reinterpret_cast<create_patch_fn
>(handler->getSymbol(
"create_patch"));
7866 auto patch = create_patch(ncoeffs, init, options);
7867 auto *patch_ptr = patch.get();
7871 return std::shared_ptr<patch_t>(
7872 patch_ptr, [patch = std::move(patch),
7873 handler = std::move(handler)](patch_t *)
mutable {
7882 std::array<iganet::short_t, ParDim> degrees{};
7883 std::array<int64_t, ParDim> ncoeffs{};
7897template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
7899 const std::string &label,
7901 pugi::xml_node geometry;
7902 const std::string geometryType =
7903 ParDim == 0 ?
"Point"
7906 : std::string(
"TensorBSpline").append(std::to_string(ParDim));
7907 for (
const auto &candidate : root.children(
"Geometry"))
7908 if (std::string_view{candidate.attribute(
"type").value()} == geometryType &&
7909 (
id < 0 || candidate.attribute(
"id").as_int() == id) &&
7910 (index < 0 || candidate.attribute(
"index").as_int() == index) &&
7911 (label.empty() || candidate.attribute(
"label").value() == label)) {
7912 geometry = candidate;
7916 throw std::runtime_error(
7917 "XML object does not provide geometry with given attributes");
7918 const auto coefs = geometry.child(
"coefs");
7919 if (!coefs || coefs.attribute(
"geoDim").as_int() != GeoDim)
7920 throw std::runtime_error(
7921 "XML object provides an incompatible geometric dimension");
7924 const auto readBasis = [&](
const pugi::xml_node &basis,
int direction) {
7925 const auto knots = basis.child(
"KnotVector");
7926 if (std::string_view{basis.attribute(
"type").value()} !=
"BSplineBasis" ||
7927 !knots || !knots.attribute(
"degree"))
7928 throw std::runtime_error(
"XML object does not provide a valid basis");
7929 metadata.
degrees[direction] = knots.attribute(
"degree").as_int();
7930 std::stringstream values(knots.child_value());
7933 while (values >> value)
7935 metadata.
ncoeffs[direction] = nknots - metadata.
degrees[direction] - 1;
7936 if (metadata.
ncoeffs[direction] <= 0)
7937 throw std::runtime_error(
"XML object provides an invalid knot vector");
7939 if constexpr (ParDim == 1)
7940 readBasis(geometry.child(
"Basis"), 0);
7941 else if constexpr (ParDim > 1) {
7942 const auto bases = geometry.child(
"Basis");
7943 const std::string basisType =
7944 std::string(
"TensorBSplineBasis").append(std::to_string(ParDim));
7945 if (std::string_view{bases.attribute(
"type").value()} != basisType)
7946 throw std::runtime_error(
"XML object does not provide a valid basis");
7947 std::array<bool, ParDim> found{};
7948 for (
const auto &basis : bases.children(
"Basis")) {
7949 const int direction = basis.attribute(
"index").as_int(-1);
7951 if (direction < 0 || direction >= ParDim || found[direction])
7952 throw std::runtime_error(
"XML object does not provide a valid basis");
7953 readBasis(basis, direction);
7954 found[direction] =
true;
7956 if (std::any_of(found.begin(), found.end(),
7957 [](
bool value) { return !value; }))
7958 throw std::runtime_error(
"XML object does not provide all bases");
7969template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
7972 throw std::runtime_error(
7973 "JSON object provides an incompatible geometric dimension");
7975 throw std::runtime_error(
7976 "JSON object provides an incompatible parametric dimension");
7978 json.at(
"degrees").template get<std::array<iganet::short_t, ParDim>>(),
7979 json.at(
"ncoeffs").template get<std::array<int64_t, ParDim>>()};
7980 if (std::any_of(metadata.ncoeffs.begin(), metadata.ncoeffs.end(),
7981 [](int64_t count) {
return count <= 0; }))
7982 throw std::runtime_error(
"JSON object provides invalid coefficient counts");
7999template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8000std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8002 const pugi::xml_node &root,
int id = 0,
const std::string &label =
"",
8005 const auto metadata = detail::parseBSplineMetadata<real_t, GeoDim, ParDim>(
8006 root,
id, label, index);
8008 auto patch = createNonUniformBSpline<real_t, GeoDim, ParDim>(
8010 patch->from_xml(root,
id, label, index);
8025template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8026std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8028 const pugi::xml_document &doc,
int id = 0,
const std::string &label =
"",
8031 return createNonUniformBSpline<real_t, GeoDim, ParDim>(doc.child(
"xml"),
id,
8032 label, index, options);
8043template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8044std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8046 const nlohmann::json &json,
8048 const auto metadata =
8049 detail::parseBSplineMetadata<real_t, GeoDim, ParDim>(json);
8051 auto patch = createNonUniformBSpline<real_t, GeoDim, ParDim>(
8053 patch->from_json(json);
8072template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8073std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8075 const std::array<iganet::short_t, ParDim> °rees,
8080 const std::string includes = R
"(
8081#include <iganet/splines/bspline.hpp>
8084#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
8088 std::ostringstream src;
8089 src << "using real_t = " << iganet::type_name_v<real_t> <<
";\n"
8090 <<
"using patch_t = iganet::BSplinePatch<real_t, " << GeoDim <<
", "
8092 <<
"using nonuniform_t = iganet::NonUniformBSpline<real_t, " << GeoDim;
8093 for (
const auto degree : degrees)
8094 src <<
", " << degree;
8096 <<
"using uniform_t = iganet::UniformBSpline<real_t, " << GeoDim;
8097 for (
const auto degree : degrees)
8098 src <<
", " << degree;
8100 <<
"EXPORT std::shared_ptr<patch_t> create_patch_from_xml(\n"
8101 <<
" const std::string &serialized, int id,\n"
8102 <<
" const std::string &label, int index,\n"
8103 <<
" iganet::Options<real_t> options) {\n"
8104 <<
" pugi::xml_document doc;\n"
8105 <<
" if (!doc.load_string(serialized.c_str()))\n"
8106 <<
" throw std::runtime_error(\"Unable to parse XML object\");\n"
8107 <<
" nonuniform_t spline(options);\n"
8108 <<
" spline.from_xml(doc, id, label, index);\n"
8109 <<
" return std::make_shared<uniform_t>(\n"
8110 <<
" std::move(spline).to_uniform());\n"
8112 <<
"EXPORT std::shared_ptr<patch_t> create_patch_from_json(\n"
8113 <<
" const std::string &serialized,\n"
8114 <<
" iganet::Options<real_t> options) {\n"
8115 <<
" nonuniform_t spline(options);\n"
8116 <<
" spline.from_json(nlohmann::json::parse(serialized));\n"
8117 <<
" return std::make_shared<uniform_t>(\n"
8118 <<
" std::move(spline).to_uniform());\n"
8121 const auto libname = iganet::jit{}.compile(
8122 includes, src.str(),
"CreateUniformBSplineFromSerialized");
8123 auto handler = std::make_shared<iganet::DLHandler>(libname);
8124 std::shared_ptr<patch_t> patch;
8125 if (format == SerializationFormat::xml) {
8126 using create_fn = std::shared_ptr<patch_t> (*)(
const std::string &, int,
8127 const std::string &, int,
8129 const auto create =
reinterpret_cast<create_fn
>(
8130 handler->getSymbol(
"create_patch_from_xml"));
8131 patch = create(serialized,
id, label, index, options);
8133 using create_fn = std::shared_ptr<patch_t> (*)(
const std::string &,
8135 const auto create =
reinterpret_cast<create_fn
>(
8136 handler->getSymbol(
"create_patch_from_json"));
8137 patch = create(serialized, options);
8140 auto *patch_ptr = patch.get();
8141 return std::shared_ptr<patch_t>(
8142 patch_ptr, [patch = std::move(patch),
8143 handler = std::move(handler)](patch_t *)
mutable {
8166template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8167std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8169 const pugi::xml_node &root,
int id = 0,
const std::string &label =
"",
8172 const auto metadata = detail::parseBSplineMetadata<real_t, GeoDim, ParDim>(
8173 root,
id, label, index);
8175 std::ostringstream serialized;
8176 root.print(serialized);
8177 return detail::createUniformBSplineFromSerialized<real_t, GeoDim, ParDim>(
8178 metadata.degrees, serialized.str(), detail::SerializationFormat::xml,
id,
8179 label, index, options);
8193template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8194std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8196 const pugi::xml_document &doc,
int id = 0,
const std::string &label =
"",
8199 return createUniformBSpline<real_t, GeoDim, ParDim>(doc.child(
"xml"),
id,
8200 label, index, options);
8209template <
typename real_t, iganet::
short_t GeoDim, iganet::
short_t ParDim>
8210std::shared_ptr<iganet::BSplinePatch<real_t, GeoDim, ParDim>>
8212 const nlohmann::json &json,
8214 const auto metadata =
8215 detail::parseBSplineMetadata<real_t, GeoDim, ParDim>(json);
8217 return detail::createUniformBSplineFromSerialized<real_t, GeoDim, ParDim>(
8218 metadata.degrees, json.dump(), detail::SerializationFormat::json, 0,
"",
Compile-time block tensor.
B-spline (common high-level functionality).
Definition bspline.hpp:3890
auto ijac(const Geometry G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G) const
Returns a block-tensor with the Jacobian of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:6045
auto to_nonuniform() &&
Converts a uniform B-spline into a non-uniform B-spline.
Definition bspline.hpp:3976
auto rotate(std::array< typename BSplineCore::value_type, 3 > angle) const
Rotates the B-spline object by three angles in 3d.
Definition bspline.hpp:4448
BSplineCommon & operator*=(BSplineCore::value_type s)
Scales the coefficients by a scalar.
Definition bspline.hpp:7651
auto icurl(const Geometry G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G) const
Returns a block-tensor with the curl of the B-spline object in the points xi with respect to the phys...
Definition bspline.hpp:4820
auto ihess(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the ihess operation.
Definition bspline.hpp:5666
auto curl(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the curl of the B-spline object with respect to the parametric variables.
Definition bspline.hpp:4695
auto curl(const torch::Tensor &xi) const
Returns a block-tensor with the curl of the B-spline object with respect to the parametric variables.
Definition bspline.hpp:4619
auto ihess(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G, const torch::Tensor &coeff_indices_G) const
Returns a block-tensor with the Hessian of the B-spline object in the points xi with respect to the p...
Definition bspline.hpp:5760
auto diff_(const BSplineCommon &other, int dim=-1)
Computes the difference between two compatible B-spline objects in-place.
Definition bspline.hpp:4283
auto ilapl(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the ilapl operation.
Definition bspline.hpp:6283
auto ihess(const Geometry &G, const torch::Tensor &xi) const
Returns a block-tensor with the Hessian of the B-spline object in the points xi with respect to the p...
Definition bspline.hpp:5651
auto rotate_(std::array< typename BSplineCore::value_type, 3 > angle)
Rotates the B-spline object by three angles in 3d in-place.
Definition bspline.hpp:4455
BSplineCommon & operator*=(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the coefficients by a vector.
Definition bspline.hpp:7662
auto rotate_(BSplineCore::value_type angle)
Rotates the B-spline object by an angle in 2d in-place.
Definition bspline.hpp:4429
auto idiv(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the idiv operation.
Definition bspline.hpp:5061
auto grad(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the gradient of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5277
auto plot(const BSplineCommon< BSplineCoreColor > &color, const std::initializer_list< utils::TensorArray< BSplineCore::parDim_ > > &xi, const nlohmann::json &json={}) const
Plots the B-spline object colored by another B-spline object together with a set of sampling points.
Definition bspline.hpp:7387
auto div(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the div operation.
Definition bspline.hpp:4922
void pretty_print(std::ostream &os) const noexcept override
Returns a string representation of the BSplineCommon object.
Definition bspline.hpp:7462
BSplineCommon operator*(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v) const
Returns a new B-spline object whose coefficients are scaled by a vector.
Definition bspline.hpp:7574
auto nv(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the outward pointing normal vector of the B-spline object.
Definition bspline.hpp:4541
BSplineCommon & operator/=(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the coefficients by a vector.
Definition bspline.hpp:7685
auto translate_(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Translates the B-spline object by a vector in-place.
Definition bspline.hpp:4412
auto lapl(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the Laplacian of the B-spline object in the points xi with respect to the...
Definition bspline.hpp:6210
auto ijac(const Geometry &G, const torch::Tensor &xi) const
Returns a block-tensor with the Jacobian of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5993
auto icurl(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G, const torch::Tensor &coeff_indices_G) const
Returns a block-tensor with the curl of the B-spline object in the points xi with respect to the phys...
Definition bspline.hpp:4867
auto ijac(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G, const torch::Tensor &coeff_indices_G) const
Returns a block-tensor with the Jacobian of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:6091
auto jac(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the Jacobian of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5890
auto jac(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the Jacobian of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5942
auto hess(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the hess operation.
Definition bspline.hpp:5492
std::unique_ptr< BSplineCommon > uPtr
Unique pointer for BSplineCommon.
Definition bspline.hpp:3929
BSplineCommon(const BSplineCommon &other, bool clone)
Copy/clone constructor.
Definition bspline.hpp:3937
auto idiv(const Geometry G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G) const
Returns a block-tensor with the divergence of the B-spline object with respect to the physical variab...
Definition bspline.hpp:5099
static Ptr make_unique(const std::array< int64_t, BSplineCore::parDim_ > &ncoeffs, enum init init=init::greville, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_unique operation.
Definition bspline.hpp:4034
auto lapl(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the lapl operation.
Definition bspline.hpp:6141
auto abs_diff_(const BSplineCommon &other, int dim=-1)
Computes the absolute difference between two compatible B-spline objects in-place.
Definition bspline.hpp:4327
auto ilapl(const Geometry G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G) const
Returns a block-tensor with the Laplacian of the B-spline object in the points xi with respect to the...
Definition bspline.hpp:6326
static Ptr make_shared(const std::array< std::vector< typename BSplineCore::value_type >, BSplineCore::parDim_ > &kv, enum init init=init::greville, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_shared operation.
Definition bspline.hpp:4157
auto grad(const torch::Tensor &xi) const
Returns a block-tensor with the gradient of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5183
auto diff(const BSplineCommon &other, int dim=-1) const
Computes the difference between two compatible B-spline objects.
Definition bspline.hpp:4270
auto idiv(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G, const torch::Tensor &coeff_indices_G) const
Returns a block-tensor with the divergence of the B-spline object with respect to the physical variab...
Definition bspline.hpp:5146
auto curl(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the curl operation.
Definition bspline.hpp:4629
auto hess(const torch::Tensor &xi) const
Returns a block-tensor with the Hessian of the B-spline object in the points xi with respect to the p...
Definition bspline.hpp:5479
auto igrad(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the igrad operation.
Definition bspline.hpp:5344
static Ptr make_unique(const std::array< int64_t, BSplineCore::parDim_ > &ncoeffs, utils::TensorArray< BSplineCore::geoDim_ > &&coeffs, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_unique operation.
Definition bspline.hpp:4062
auto to_uniform() &&
Converts a non-uniform B-spline into a uniform B-spline.
Definition bspline.hpp:3991
BSplineCommon operator*(BSplineCore::value_type s) const
Returns a new B-spline object whose coefficients are scaled by a scalar.
Definition bspline.hpp:7560
auto scale(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v) const
Scales the B-spline object by a vector.
Definition bspline.hpp:4385
auto nv(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the outward pointing normal vector of the B-spline object.
Definition bspline.hpp:4561
auto igrad(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G, const torch::Tensor &coeff_indices_G) const
Returns a block-tensor with the gradient of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5427
auto div(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the divergence of the B-spline object with respect to the parametric vari...
Definition bspline.hpp:4991
std::shared_ptr< BSplineCommon > Ptr
Shared pointer for BSplineCommon.
Definition bspline.hpp:3926
auto hess(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices) const
Returns a block-tensor with the Hessian of the B-spline object in the points xi with respect to the p...
Definition bspline.hpp:5590
auto plot(const BSplineCommon< BSplineCoreColor > &color, const utils::TensorArray< BSplineCore::parDim_ > &xi, const nlohmann::json &json={}) const
Plots the B-spline object colored by another B-spline object together with a set of sampling points.
Definition bspline.hpp:7301
static Ptr make_unique(const std::array< int64_t, BSplineCore::parDim_ > &ncoeffs, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_unique operation.
Definition bspline.hpp:4048
static Ptr make_shared(const std::array< int64_t, BSplineCore::parDim_ > &ncoeffs, utils::TensorArray< BSplineCore::geoDim_ > &&coeffs, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_shared operation.
Definition bspline.hpp:4144
auto scale(BSplineCore::value_type s, int dim=-1) const
Scales the B-spline object by a scalar.
Definition bspline.hpp:4364
BSplineCommon(const BSplineCommon &)=default
Copy constructor.
auto hess(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the Hessian of the B-spline object in the points xi with respect to the p...
Definition bspline.hpp:5539
auto plot(const utils::TensorArray< BSplineCore::parDim_ > &xi, const nlohmann::json &json={}) const
Plots the B-spline object together with a set of sampling points.
Definition bspline.hpp:6439
static Ptr make_unique(const std::array< std::vector< typename BSplineCore::value_type >, BSplineCore::parDim_ > &kv, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_unique operation.
Definition bspline.hpp:4090
auto curl(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the curl of the B-spline object with respect to the parametric variables.
Definition bspline.hpp:4660
static Ptr make_unique(Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Creates a new B-spline object as unique pointer.
Definition bspline.hpp:4023
BSplineCommon(BSplineCommon &&)=default
Move constructor.
BSplineCommon operator/(BSplineCore::value_type s) const
Returns a new B-spline object whose coefficients are scaled by a scalar.
Definition bspline.hpp:7590
auto norm() const
Computes the norm of the B-spline object by computing the mean-squared sum of the function values eva...
Definition bspline.hpp:4355
auto plot(const nlohmann::json &json={}) const
Plots the B-spline object.
Definition bspline.hpp:6423
BSplineCommon(BSplineCommon &&other, utils::TensorArray< BSplineCore::geoDim_ > &&coeffs)
Move constructor with external coefficients.
Definition bspline.hpp:4011
BSplineCommon & operator+=(const BSplineCommon &other)
Adds the coefficients of another B-spline object.
Definition bspline.hpp:7624
auto ilapl(const Geometry &G, const torch::Tensor &xi) const
Returns a block-tensor with the Laplacian of the B-spline object in the points xi with respect to the...
Definition bspline.hpp:6268
auto plot(const BSplineCommon< BSplineCoreColor > &color, const nlohmann::json &json={}) const
Plots the B-spline object colored by another B-spline object.
Definition bspline.hpp:6477
auto to() const
Returns a copy of the B-spline object with real_t type.
Definition bspline.hpp:4257
auto nv(const torch::Tensor &xi) const
Returns a block-tensor with the outward pointing normal vector of the B-spline object.
Definition bspline.hpp:4516
BSplineCommon operator+(const BSplineCommon &other) const
Returns a new B-spline object whose coefficients are the sum of that of two compatible B-spline objec...
Definition bspline.hpp:7526
auto icurl(const Geometry &G, const torch::Tensor &xi) const
Returns a block-tensor with the curl of the B-spline object in the points xi with respect to the phys...
Definition bspline.hpp:4769
auto ihess(const Geometry G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G) const
Returns a block-tensor with the Hessian of the B-spline object in the points xi with respect to the p...
Definition bspline.hpp:5708
auto idiv(const Geometry &G, const torch::Tensor &xi)
Returns a block-tensor with the divergence of the B-spline object with respect to the physical variab...
Definition bspline.hpp:5046
auto ijac(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the ijac operation.
Definition bspline.hpp:6008
auto translate(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v) const
Translates the B-spline object by a vector.
Definition bspline.hpp:4403
auto boundingBox() const
Computes the bounding box of the B-spline object.
Definition bspline.hpp:4490
static Ptr make_shared(const std::array< int64_t, BSplineCore::parDim_ > &ncoeffs, enum init init=init::greville, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_shared operation.
Definition bspline.hpp:4116
auto div(const torch::Tensor &xi) const
Returns a block-tensor with the divergence of the B-spline object with respect to the parametric vari...
Definition bspline.hpp:4910
auto icurl(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the icurl operation.
Definition bspline.hpp:4784
auto grad(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the gradient of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5236
auto igrad(const Geometry &G, const torch::Tensor &xi) const
Returns a block-tensor with the gradient of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5329
auto scale_(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the B-spline object by a vector in-place.
Definition bspline.hpp:4393
auto jac(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the jac operation.
Definition bspline.hpp:5848
static Ptr make_shared(const std::array< int64_t, BSplineCore::parDim_ > &ncoeffs, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_shared operation.
Definition bspline.hpp:4130
BSplineCommon & operator/=(BSplineCore::value_type s)
Scales the coefficients by a scalar.
Definition bspline.hpp:7674
static Ptr make_unique(const std::array< std::vector< typename BSplineCore::value_type >, BSplineCore::parDim_ > &kv, enum init init=init::greville, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_unique operation.
Definition bspline.hpp:4075
auto rotate(BSplineCore::value_type angle) const
Rotates the B-spline object by an angle in 2d.
Definition bspline.hpp:4422
auto nv(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the nv operation.
Definition bspline.hpp:4526
BSplineCommon & operator-=(const BSplineCommon &other)
Substracts the coefficients of another B-spline object.
Definition bspline.hpp:7640
BSplineCommon operator-(const BSplineCommon &other) const
Returns a new B-spline object whose coefficients are the difference of that of two compatible B-splin...
Definition bspline.hpp:7546
BSplineCommon(const BSplineCommon &other, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false)
Copy constructor with external coefficients.
Definition bspline.hpp:3947
auto scale_(BSplineCore::value_type s, int dim=-1)
Scales the B-spline object by a scalar in-place.
Definition bspline.hpp:4372
auto ilapl(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const torch::Tensor &coeff_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G, const torch::Tensor &coeff_indices_G) const
Returns a block-tensor with the Laplacian of the B-spline object in the points xi with respect to the...
Definition bspline.hpp:6380
auto grad(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Provides the grad operation.
Definition bspline.hpp:5200
auto to(torch::Device device) const
Returns a copy of the B-spline object with settings from device.
Definition bspline.hpp:4238
auto igrad(const Geometry G, const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices, const utils::TensorArray< Geometry::parDim()> &knot_indices_G) const
Returns a block-tensor with the gradient of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5380
auto lapl(const torch::Tensor &xi) const
Returns a block-tensor with the Laplacian of the B-spline object in the points xi with respect to the...
Definition bspline.hpp:6128
auto clone() const
Returns a clone of the B-spline object.
Definition bspline.hpp:4198
BSplineCommon operator/(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v) const
Returns a new B-spline object whose coefficients are scaled by a vector.
Definition bspline.hpp:7604
static Ptr make_shared(Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Creates a new B-spline object as shared pointer.
Definition bspline.hpp:4105
auto jac(const torch::Tensor &xi) const
Returns a block-tensor with the Jacobian of the B-spline object in the points xi with respect to the ...
Definition bspline.hpp:5835
static Ptr make_shared(const std::array< std::vector< typename BSplineCore::value_type >, BSplineCore::parDim_ > &kv, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false, Options< typename BSplineCore::value_type > options=Options< typename BSplineCore::value_type >{})
Provides the make_shared operation.
Definition bspline.hpp:4172
auto lapl(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the Laplacian of the B-spline object in the points xi with respect to the...
Definition bspline.hpp:6173
auto div(const utils::TensorArray< BSplineCore::parDim_ > &xi, const utils::TensorArray< BSplineCore::parDim_ > &knot_indices) const
Returns a block-tensor with the divergence of the B-spline object with respect to the parametric vari...
Definition bspline.hpp:4954
BSplineCommon & uniform_refine(int numRefine=1, int dim=-1)
Returns the B-spline object with uniformly refined knot and coefficient vectors.
Definition bspline.hpp:4191
auto plot(const std::initializer_list< utils::TensorArray< BSplineCore::parDim_ > > &xi, const nlohmann::json &json={}) const
Plots the B-spline object together with a set of sampling points.
Definition bspline.hpp:6457
auto abs_diff(const BSplineCommon &other, int dim=-1) const
Computes the absolute difference between two compatible B-spline objects.
Definition bspline.hpp:4314
auto to(Options< real_t > options) const
Returns a copy of the B-spline object with settings from options.
Definition bspline.hpp:4218
BSplineCommon(OtherCore &&core)
Constructs the high-level B-spline from a compatible core.
Definition bspline.hpp:3967
Abstract patch function base class.
Definition patch.hpp:53
The Options class handles the automated determination of dtype from the template argument and the sel...
Definition options.hpp:47
Spline base class.
Definition bspline.hpp:3853
SplineCore base class.
Definition bspline.hpp:130
Full qualified name descriptor.
Definition fqn.hpp:22
Concept to identify template parameters that are derived from iganet::SplineCore_.
Definition bspline.hpp:141
Concept to identify template parameters that are derived from iganet::Spline_.
Definition bspline.hpp:3858
Definition bspline.hpp:123
Definition bspline.hpp:116
Container utility functions.
Full qualified name utility functions.
Integer sequence utility function.
Integer power utility function.
Linear algebra utility functions.
SerializationFormat
Definition bspline.hpp:7886
std::shared_ptr< iganet::BSplinePatch< real_t, GeoDim, ParDim > > createUniformBSplineFromSerialized(const std::array< iganet::short_t, ParDim > °rees, const std::string &serialized, SerializationFormat format, int id, const std::string &label, int index, iganet::Options< real_t > options)
Loads a non-uniform B-spline and converts it to a uniform B-spline inside one JIT-generated dynamic l...
Definition bspline.hpp:8074
std::array< int64_t, ParDim > ncoeffs
Definition bspline.hpp:7883
std::array< iganet::short_t, ParDim > degrees
Definition bspline.hpp:7882
BSplineMetadata< ParDim > parseBSplineMetadata(const pugi::xml_node &root, int id, const std::string &label, int index)
Parses B-spline metadata from an XML node.
Definition bspline.hpp:7898
auto kron(T0 &&t0, T1 &&t1)
Computes the Kronecker-product between two or more tensors.
Definition linalg.hpp:214
std::array< torch::Tensor, N > TensorArray
Definition tensorarray.hpp:26
auto to_tensor(const std::array< T, N > &array, torch::IntArrayRef sizes=torch::IntArrayRef{-1}, const iganet::Options< T > &options=iganet::Options< T >{})
Converts a std::array to torch::Tensor.
Definition container.hpp:79
auto kronproduct(T0 &&t0, T1 &&t1)
Computes the directional Kronecker-product between two tensors along the given dimension.
Definition linalg.hpp:60
TensorArray< 1 > TensorArray1
Definition tensorarray.hpp:29
auto to_tensorAccessor(const torch::Tensor &tensor)
Converts a torch::Tensor object to a torch::TensorAccessor object.
Definition tensorarray.hpp:95
T prod(std::array< T, N > array, std::size_t start_index=0, std::size_t stop_index=N - 1)
Computes the (partial) product of all std::array entries.
Definition linalg.hpp:239
constexpr std::array< T, N - M > remove_from_back(std::array< T, N > array)
Derives a std::array object from a given std::array object dropping the last M entries.
Definition container.hpp:510
auto VSlice(torch::Tensor index, int64_t start_offset, int64_t stop_offset)
Vectorized version of torch::indexing::Slice (see https://pytorch.org/cppdocs/notes/tensor_indexing....
Definition vslice.hpp:48
auto dotproduct(T0 &&t0, T1 &&t1)
Computes the directional dot-product between two tensors with summation along the given dimension.
Definition linalg.hpp:36
auto to_ArrayRef(const std::array< T, N > &array)
Converts a std::array<int64_t, N> to an at::IntArrayRef object.
Definition container.hpp:241
Forward declaration of BlockTensor.
Definition blocktensor.hpp:47
std::shared_ptr< iganet::BSplinePatch< real_t, GeoDim, ParDim > > createUniformBSpline(const std::array< iganet::short_t, ParDim > °rees, 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 > °rees, 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
deriv
Enumerator for specifying the derivative of B-spline evaluation.
Definition bspline.hpp:76
constexpr auto operator+(deriv lhs, deriv rhs)
Adds two enumerators for specifying the derivative of B-spline evaluation.
Definition bspline.hpp:93
bool is_verbose(std::ostream &os)
Tests whether verbose output is enabled on a stream.
Definition core.hpp:871
std::ostream & operator<<(std::ostream &os, const MemoryDebugger< id > &obj)
Prints a memory debugger object.
Definition memory.hpp:145
constexpr auto operator^(deriv lhs, short_t rhs)
Raises an enumerator for specifying the derivative of B-spline evaluation to a higher exponent.
Definition bspline.hpp:106
init
Enumerator for specifying the initialization of B-spline coefficients.
Definition bspline.hpp:58
torch::serialize::InputArchive & operator>>(torch::serialize::InputArchive &archive, UniformBSplineCore< real_t, GeoDim, Degrees... > &obj)
Deserializes a B-spline object.
Definition bspline.hpp:3274
@ none
Definition boundary.hpp:38
short int short_t
Signed short integer type used by IgANet's compact enumerations.
Definition core.hpp:76
Serialization utility functions.
Serialization prototype.
Definition serialize.hpp:29
Computes the power of integer E to the N at compile time.
Definition integer_pow.hpp:21
Reverse index sequence.
Definition index_sequence.hpp:36
TensorArray utility functions.
#define TENSORARRAY_FORALL(obj, func,...)
Definition tensorarray.hpp:220
VSlice utility functions.