40#define GENERATE_EXPR_SEQ (curl)(div)(grad)(hess)(jac)(lapl)
46#define GENERATE_IEXPR_SEQ (icurl)(idiv)(igrad)(ihess)(ijac)(ilapl)
50using namespace literals;
51using utils::operator+;
113 { t.find_knot_indices(x) };
120 { t.find_coeff_indices(x) };
225 public BSplinePatch<real_t, GeoDim, sizeof...(Degrees)> {
227 template <
typename BSplineCore>
242 static constexpr const std::array<short_t, parDim_>
degrees_ = {Degrees...};
279 std::make_signed_t<short_t> degree_elevate = 0>
280 using derived_type = BSpline<real_t, GeoDim, (Degrees + degree_elevate)...>;
284 template <std::make_
signed_t<
short_t> degree_elevate = 0>
295 template <
typename other_t>
300 [[nodiscard]]
inline torch::Device
device() const noexcept
override {
310 [[nodiscard]]
inline torch::Dtype
dtype() const noexcept
override {
315 [[nodiscard]]
inline torch::Layout
layout() const noexcept
override {
330 [[nodiscard]]
inline bool is_sparse() const noexcept
override {
335 inline static constexpr bool is_uniform() noexcept {
return true; }
426 throw std::runtime_error(
"Invalid number of coefficients");
433 .to(
options.requires_grad(
false))
434 .requires_grad_(
options.requires_grad());
467 template <
typename other_t>
480 .to(
options.requires_grad(
false))
481 .requires_grad_(
options.requires_grad());
487 .to(
options.requires_grad(
false))
488 .requires_grad_(
options.requires_grad());
507 inline static constexpr const std::array<short_t, parDim_> &
537 [[nodiscard]]
inline const torch::Tensor &
knots(
short_t i)
const noexcept {
563 inline const std::array<int64_t, parDim_> &
nknots() const noexcept {
592 [[nodiscard]]
inline const torch::Tensor &
coeffs(
short_t i)
const noexcept {
657 inline const std::array<int64_t, parDim_> &
ncoeffs() const noexcept {
676 template <std::size_t... Is>
677 inline torch::Tensor
as_tensor_(std::index_sequence<Is...>)
const noexcept {
678 return torch::cat({
coeffs_[Is]...});
685 [[nodiscard]]
inline torch::Tensor
as_tensor() const noexcept
override {
686 return as_tensor_(std::make_index_sequence<geoDim_>{});
693 template <std::size_t... Is>
696 const torch::Tensor &tensor)
noexcept {
712 return from_tensor_(std::make_index_sequence<geoDim_>{}, tensor);
758 options_.requires_grad(
false).template dtype<int64_t>())
765 options_.requires_grad(
false).template dtype<int64_t>())
769 auto indices = idx_base + offset + offsets;
773 .index_select(0, indices.flatten())
777 auto greville_ = gathered.mean(1);
818 const torch::Tensor &coeff_indices, int64_t numeval,
819 torch::IntArrayRef sizes)
const override {
827 coeffs(i).index_select(0, coeff_indices).view({-1, numeval}))
834 const torch::Tensor &coeff_indices, int64_t numeval,
835 torch::IntArrayRef sizes)
const override {
850 return torch::matmul(
coeffs(i)
851 .index_select(0, coeff_indices)
852 .view({numeval, -1,
degrees_[0] + 1}),
853 basfunc[0].view({numeval, -1, 1}));
855 return torch::matmul(
856 (eval_(i, dim - 1)).view({numeval, -1,
degrees_[dim] + 1}),
857 basfunc[dim].view({numeval, -1, 1}));
862 result.set(i, (eval_(i,
parDim_ - 1)).view(sizes));
918 template <deriv deriv = deriv::func,
bool memory_optimized = false>
919 inline auto eval(
const torch::Tensor &xi)
const {
923 throw std::runtime_error(
"Invalid parametric dimension");
926 template <deriv deriv = deriv::func,
bool memory_optimized = false>
931 template <deriv deriv = deriv::func,
bool memory_optimized = false>
932 inline auto eval_tr(
const torch::Tensor &xi)
const {
936 throw std::runtime_error(
"Invalid parametric dimension");
939 template <deriv deriv = deriv::func,
bool memory_optimized = false>
960 template <deriv deriv = deriv::func,
bool memory_optimized = false>
963 return eval<deriv, memory_optimized>(
964 xi, knot_indices, find_coeff_indices<memory_optimized>(knot_indices));
967 template <deriv deriv = deriv::func,
bool memory_optimized = false>
970 return eval_tr<deriv, memory_optimized>(
971 xi, knot_indices, find_coeff_indices<memory_optimized>(knot_indices));
992 template <deriv deriv = deriv::func,
bool memory_optimized = false>
995 const torch::Tensor &coeff_indices)
const {
1004 result.set(i, torch::zeros_like(
coeffs_[i]));
1012 assert(xi[i].sizes() == knot_indices[i].sizes());
1014 assert(xi[0].sizes() == xi[i].sizes());
1016 if constexpr (memory_optimized) {
1020 throw std::runtime_error(
1021 "Memory-optimized evaluation requires single-valued coefficient");
1025 eval_basfunc<deriv, memory_optimized>(xi, knot_indices);
1032 return torch::matmul(
1034 .index_select(0, coeff_indices)
1035 .view({xi[0].numel(), -1,
degrees_[0] + 1}),
1036 basfunc[0].view({xi[0].numel(), -1, 1}));
1038 return torch::matmul(
1040 .view({xi[0].numel(), -1,
degrees_[dim] + 1}),
1041 basfunc[dim].view({xi[0].numel(), -1, 1}));
1046 result.set(i, (eval_(i,
parDim_ - 1)).view(xi[0].sizes()));
1055 auto basfunc = eval_basfunc<deriv, memory_optimized>(xi, knot_indices);
1057 if (
coeffs(0).dim() > 1) {
1059 auto sizes = xi[0].sizes() + (-1_i64);
1063 .index_select(0, coeff_indices)
1064 .view({-1, xi[0].numel(),
1072 .index_select(0, coeff_indices)
1073 .view({-1, xi[0].numel()}))
1074 .view(xi[0].sizes()));
1081 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1084 const torch::Tensor &coeff_indices)
const {
1093 result.set(i, torch::zeros_like(
coeffs_[i]));
1101 assert(xi[i].sizes() == knot_indices[i].sizes());
1103 assert(xi[0].sizes() == xi[i].sizes());
1105 if constexpr (memory_optimized) {
1109 throw std::runtime_error(
1110 "Memory-optimized evaluation requires single-valued coefficient");
1114 eval_basfunc_tr<deriv, memory_optimized>(xi, knot_indices);
1121 return torch::matmul(
1123 .index_select(0, coeff_indices)
1124 .view({xi[0].numel(), -1,
degrees_[0] + 1}),
1125 basfunc[0].view({xi[0].numel(), -1, 1}));
1127 return torch::matmul(
1129 .view({xi[0].numel(), -1,
degrees_[dim] + 1}),
1130 basfunc[dim].view({xi[0].numel(), -1, 1}));
1135 result.set(i, (eval_(i,
parDim_ - 1)).view(xi[0].sizes()));
1144 auto basfunc = eval_basfunc_tr<deriv, memory_optimized>(xi, knot_indices);
1146 if (
coeffs(0).dim() > 1) {
1148 auto sizes = xi[0].sizes() + (-1_i64);
1152 .index_select(0, coeff_indices)
1153 .view({-1, xi[0].numel(),
1161 .index_select(0, coeff_indices)
1162 .view({-1, xi[0].numel()}))
1163 .view(xi[0].sizes()));
1190 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1222 template <
bool memory_optimized = false>
1225 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1227 return find_coeff_indices<memory_optimized>(
1231 template <
bool memory_optimized = false>
1234 using utils::operator-;
1237 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1238 else if constexpr (
parDim_ == 1)
1239 return utils::VSlice<memory_optimized>(indices[0].flatten(), -
degrees_[0],
1242 return utils::VSlice<memory_optimized>(
1244 utils::make_array<int64_t>(-
degrees_),
1245 utils::make_array<int64_t, parDim_>(1),
1260 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1264 return torch::ones_like(
coeffs_[0]);
1266 return torch::zeros_like(
coeffs_[0]);
1271 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1275 return torch::ones_like(
coeffs_[0]);
1277 return torch::zeros_like(
coeffs_[0]);
1282 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1286 return torch::ones_like(
coeffs_[0]);
1288 return torch::zeros_like(
coeffs_[0]);
1293 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1297 return torch::ones_like(
coeffs_[0]);
1299 return torch::zeros_like(
coeffs_[0]);
1317 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1319 const torch::Tensor &knot_indices)
const {
1322 return torch::ones_like(
coeffs_[0]);
1324 return torch::zeros_like(
coeffs_[0]);
1326 return eval_basfunc<deriv, memory_optimized>(
1330 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1332 const torch::Tensor &knot_indices)
const {
1335 return torch::ones_like(
coeffs_[0]);
1337 return torch::zeros_like(
coeffs_[0]);
1339 return eval_basfunc_tr<deriv, memory_optimized>(
1343 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1350 return torch::ones_like(
coeffs_[0]);
1352 return torch::zeros_like(
coeffs_[0]);
1358 assert(xi[i].sizes() == knot_indices[i].sizes());
1360 assert(xi[0].sizes() == xi[i].sizes());
1362 if constexpr (memory_optimized) {
1366 this]<std::size_t... Is>(std::index_sequence<Is...>) {
1374 10>(xi[Is].flatten(),
1375 knot_indices[Is].flatten())
1376 .transpose(0, 1))...};
1379 return basfunc_(std::make_index_sequence<parDim_>{});
1390 xi[0].flatten(), knot_indices[0].flatten());
1395 auto basfunc_ = [&,
this]<std::size_t... Is>(
1396 std::index_sequence<Is...>) {
1407 xi[Is].flatten(), knot_indices[Is].flatten())...);
1417 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1424 return torch::ones_like(
coeffs_[0]);
1426 return torch::zeros_like(
coeffs_[0]);
1432 assert(xi[i].sizes() == knot_indices[i].sizes());
1434 assert(xi[0].sizes() == xi[i].sizes());
1436 if constexpr (memory_optimized) {
1440 this]<std::size_t... Is>(std::index_sequence<Is...>) {
1448 10>(xi[Is].flatten(),
1449 knot_indices[Is].flatten())
1450 .transpose(0, 1))...};
1453 return basfunc_(std::make_index_sequence<parDim_>{});
1464 xi[0].flatten(), knot_indices[0].flatten());
1469 auto basfunc_ = [&,
this]<std::size_t... Is>(
1470 std::index_sequence<Is...>) {
1476 utils::kronproduct<-1>(
1481 xi[Is].flatten(), knot_indices[Is].flatten())...);
1495 std::array<real_t, geoDim_>(
const std::array<real_t, parDim_> &)>
1498 static_assert(
parDim_ <= 4,
"Unsupported parametric dimension");
1502 auto c = mapping(std::array<real_t, parDim_>{});
1504 coeffs_[d].detach()[0] = c[d];
1508 else if constexpr (
parDim_ == 1) {
1509#pragma omp parallel for
1510 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1512 std::array<real_t, parDim_>{i / real_t(
ncoeffs_[0] - 1)});
1514 coeffs_[d].detach()[i] = c[d];
1519 else if constexpr (
parDim_ == 2) {
1520#pragma omp parallel for collapse(2)
1521 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1522 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1523 auto c = mapping(std::array<real_t, parDim_>{
1532 else if constexpr (
parDim_ == 3) {
1533#pragma omp parallel for collapse(3)
1534 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1535 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1536 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1537 auto c = mapping(std::array<real_t, parDim_>{
1549 else if constexpr (
parDim_ == 4) {
1550#pragma omp parallel for collapse(4)
1551 for (int64_t l = 0; l <
ncoeffs_[3]; ++l) {
1552 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1553 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1554 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1555 auto c = mapping(std::array<real_t, parDim_>{
1568 throw std::runtime_error(
"Unsupported parametric dimension");
1574 template <std::
size_t N>
1577 std::array<real_t, N>(
const std::array<real_t, parDim_> &)>
1579 std::array<short_t, N> dims) {
1581 static_assert(
parDim_ <= 4,
"Unsupported parametric dimension");
1585 auto c = mapping(std::array<real_t, parDim_>{});
1586 for (std::size_t d = 0; d < N; ++d)
1587 coeffs_[dims[d]].detach()[0] = c[d];
1591 else if constexpr (
parDim_ == 1) {
1592#pragma omp parallel for
1593 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1595 std::array<real_t, parDim_>{i / real_t(
ncoeffs_[0] - 1)});
1596 for (std::size_t d = 0; d < N; ++d)
1597 coeffs_[dims[d]].detach()[i] = c[d];
1602 else if constexpr (
parDim_ == 2) {
1603#pragma omp parallel for collapse(2)
1604 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1605 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1606 auto c = mapping(std::array<real_t, parDim_>{
1608 for (std::size_t d = 0; d < N; ++d)
1615 else if constexpr (
parDim_ == 3) {
1616#pragma omp parallel for collapse(3)
1617 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1618 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1619 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1620 auto c = mapping(std::array<real_t, parDim_>{
1623 for (std::size_t d = 0; d < N; ++d)
1632 else if constexpr (
parDim_ == 4) {
1633#pragma omp parallel for collapse(4)
1634 for (int64_t l = 0; l <
ncoeffs_[3]; ++l) {
1635 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1636 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1637 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1638 auto c = mapping(std::array<real_t, parDim_>{
1641 for (std::size_t d = 0; d < N; ++d)
1651 throw std::runtime_error(
"Unsupported parametric dimension");
1657 [[nodiscard]]
inline nlohmann::json
to_json()
const override {
1658 nlohmann::json json;
1672 return ::iganet::utils::to_json<real_t, 1>(
knots_);
1677 auto coeffs_json = nlohmann::json::array();
1679 auto [coeffs_cpu, coeffs_accessor] =
1680 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
1682 auto json = nlohmann::json::array();
1685 json.push_back(coeffs_accessor[0]);
1690 json.push_back(coeffs_accessor[i]);
1693 coeffs_json.push_back(json);
1701 if (json[
"geoDim"].get<short_t>() !=
geoDim_)
1702 throw std::runtime_error(
1703 "JSON object provides incompatible geometric dimensions");
1705 if (json[
"parDim"].get<short_t>() !=
parDim_)
1706 throw std::runtime_error(
1707 "JSON object provides incompatible parametric dimensions");
1709 if (json[
"degrees"].get<std::array<short_t, parDim_>>() !=
degrees_)
1710 throw std::runtime_error(
"JSON object provides incompatible degrees");
1712 nknots_ = json[
"nknots"].get<std::array<int64_t, parDim_>>();
1713 ncoeffs_ = json[
"ncoeffs"].get<std::array<int64_t, parDim_>>();
1719 auto kv = json[
"knots"].get<std::array<std::vector<real_t>,
parDim_>>();
1724 auto c = json[
"coeffs"].get<std::array<std::vector<real_t>,
geoDim_>>();
1733 [[nodiscard]]
inline pugi::xml_document
1734 to_xml(
int id = 0,
const std::string &label =
"",
int index = -1)
const {
1735 pugi::xml_document doc;
1736 pugi::xml_node root = doc.append_child(
"xml");
1737 to_xml(root,
id, label, index);
1743 inline pugi::xml_node &
to_xml(pugi::xml_node &root,
int id = 0,
1744 const std::string &label =
"",
1745 int index = -1)
const {
1747 pugi::xml_node geo = root.append_child(
"Geometry");
1751 geo.append_attribute(
"type") =
"Point";
1754 geo.append_attribute(
"id") = id;
1757 geo.append_attribute(
"index") = index;
1760 geo.append_attribute(
"label") = label.c_str();
1764 else if constexpr (
parDim_ == 1) {
1765 geo.append_attribute(
"type") =
"BSpline";
1768 geo.append_attribute(
"id") = id;
1771 geo.append_attribute(
"index") = index;
1774 geo.append_attribute(
"label") = label.c_str();
1777 pugi::xml_node basis = geo.append_child(
"Basis");
1778 basis.append_attribute(
"type") =
"BSplineBasis";
1781 pugi::xml_node
knots = basis.append_child(
"KnotVector");
1784 std::stringstream ss;
1785 auto [knots_cpu, knots_accessor] =
1786 utils::to_tensorAccessor<real_t, 1>(
knots_[0], torch::kCPU);
1787 for (int64_t i = 0; i <
nknots_[0]; ++i)
1788 ss << std::to_string(knots_accessor[i])
1789 << (i <
nknots_[0] - 1 ?
" " :
"");
1790 knots.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1795 geo.append_attribute(
"type") =
1796 std::string(
"TensorBSpline").append(std::to_string(
parDim_)).c_str();
1799 geo.append_attribute(
"id") = id;
1802 geo.append_attribute(
"index") = index;
1805 geo.append_attribute(
"label") = label.c_str();
1808 pugi::xml_node bases = geo.append_child(
"Basis");
1809 bases.append_attribute(
"type") = std::string(
"TensorBSplineBasis")
1810 .append(std::to_string(
parDim_))
1814 pugi::xml_node basis = bases.append_child(
"Basis");
1815 basis.append_attribute(
"type") =
"BSplineBasis";
1816 basis.append_attribute(
"index") = index;
1819 pugi::xml_node
knots = basis.append_child(
"KnotVector");
1822 std::stringstream ss;
1823 auto [knots_cpu, knots_accessor] =
1824 utils::to_tensorAccessor<real_t, 1>(
knots_[index], torch::kCPU);
1825 for (int64_t i = 0; i <
nknots_[index]; ++i)
1826 ss << std::to_string(knots_accessor[i])
1827 << (i <
nknots_[index] - 1 ?
" " :
"");
1828 knots.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1834 pugi::xml_node coefs = geo.append_child(
"coefs");
1835 coefs.append_attribute(
"geoDim") =
geoDim_;
1837 auto [coeffs_cpu, coeffs_accessors] =
1838 utils::to_tensorAccessor<real_t, 1>(
coeffs_, torch::kCPU);
1839 std::stringstream ss;
1843 ss << std::to_string(coeffs_accessors[g][0]) <<
" ";
1848 ss << std::to_string(coeffs_accessors[g][i]) <<
" ";
1851 coefs.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1858 const std::string &label =
"",
1860 return from_xml(doc.child(
"xml"),
id, label, index);
1865 const std::string &label =
"",
1868 std::array<bool, std::max(
parDim_,
short_t{1})> nknots_found{
false},
1869 ncoeffs_found{
false};
1872 for (pugi::xml_node geo : root.children(
"Geometry")) {
1878 if (geo.attribute(
"type").value() == std::string(
"Point") &&
1879 (
id >= 0 ? geo.attribute(
"id").as_int() == id :
true) &&
1880 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
1881 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
1883 nknots_found[0] =
true;
1884 ncoeffs_found[0] =
true;
1891 else if constexpr (
parDim_ == 1) {
1894 if (geo.attribute(
"type").value() == std::string(
"BSpline") &&
1895 (
id >= 0 ? geo.attribute(
"id").as_int() == id :
true) &&
1896 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
1897 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
1900 if (pugi::xml_node basis = geo.child(
"Basis");
1901 basis.attribute(
"type").value() == std::string(
"BSplineBasis")) {
1904 if (pugi::xml_node
knots = basis.child(
"KnotVector");
1907 std::vector<real_t> kv;
1908 std::string values = std::regex_replace(
1909 knots.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
1910 for (
auto value = strtok(&values[0],
" "); value !=
nullptr;
1911 value = strtok(
nullptr,
" "))
1912 kv.push_back(
static_cast<real_t
>(std::stod(value)));
1918 nknots_found[0] =
true;
1919 ncoeffs_found[0] =
true;
1934 if (geo.attribute(
"type").value() ==
1935 std::string(
"TensorBSpline").append(std::to_string(
parDim_)) &&
1936 (
id >= 0 ? geo.attribute(
"id").as_int() ==
id :
true) &&
1937 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
1938 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
1941 if (pugi::xml_node bases = geo.child(
"Basis");
1942 bases.attribute(
"type").value() ==
1943 std::string(
"TensorBSplineBasis")
1944 .append(std::to_string(
parDim_))) {
1947 for (pugi::xml_node basis : bases.children(
"Basis")) {
1950 if (basis.attribute(
"type").value() ==
1951 std::string(
"BSplineBasis")) {
1953 int index = basis.attribute(
"index").as_int();
1956 if (pugi::xml_node
knots = basis.child(
"KnotVector");
1959 std::vector<real_t> kv;
1960 std::string values = std::regex_replace(
1961 knots.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
1963 for (
auto value = strtok(&values[0],
" "); value !=
nullptr;
1964 value = strtok(
nullptr,
" "))
1965 kv.push_back(
static_cast<real_t
>(std::stod(value)));
1971 nknots_found[index] =
true;
1972 ncoeffs_found[index] =
true;
1988 if (std::any_of(std::begin(nknots_found), std::end(nknots_found),
1989 [](
bool i) {
return !i; }))
1990 throw std::runtime_error(
1991 "XML object is not compatible with B-spline object");
2003 if (pugi::xml_node coefs = geo.child(
"coefs")) {
2005 std::string values = std::regex_replace(
2006 coefs.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
2007 auto coeffs_accessors = utils::to_tensorAccessor<real_t, 1>(
coeffs_);
2010 auto value = strtok(&values[0],
" ");
2013 if (value ==
nullptr)
2014 throw std::runtime_error(
2015 "XML object does not provide enough coefficients");
2017 coeffs_accessors[g][0] =
static_cast<real_t
>(std::stod(value));
2018 value = strtok(
nullptr,
" ");
2021 if (value !=
nullptr)
2022 throw std::runtime_error(
2023 "XML object provides too many coefficients");
2026 auto value = strtok(&values[0],
" ");
2030 if (value ==
nullptr)
2031 throw std::runtime_error(
2032 "XML object does not provide enough coefficients");
2034 coeffs_accessors[g][i] =
static_cast<real_t
>(std::stod(value));
2035 value = strtok(
nullptr,
" ");
2038 if (value !=
nullptr)
2039 throw std::runtime_error(
2040 "XML object provides too many coefficients");
2048 if (nknots_found[0] && ncoeffs_found[0])
2050 }
else if (std::all_of(std::begin(nknots_found), std::end(nknots_found),
2051 [](
bool i) {
return i; }) &&
2052 std::all_of(std::begin(ncoeffs_found),
2053 std::end(ncoeffs_found),
2054 [](
bool i) {
return i; }))
2058 throw std::runtime_error(
2059 "XML object is not compatible with B-spline object");
2063 throw std::runtime_error(
"XML object does not provide coefficients");
2067 throw std::runtime_error(
"XML object does not provide geometry with given "
2068 "id, index, and/or label");
2073 inline void load(
const std::string &filename,
2074 const std::string &key =
"bspline") {
2075 torch::serialize::InputArchive archive;
2076 archive.load_from(filename);
2081 inline torch::serialize::InputArchive &
2082 read(torch::serialize::InputArchive &archive,
2083 const std::string &key =
"bspline") {
2084 torch::Tensor tensor;
2086 archive.read(key +
".parDim", tensor);
2087 if (tensor.item<int64_t>() !=
parDim_)
2088 throw std::runtime_error(
"parDim mismatch");
2090 archive.read(key +
".geoDim", tensor);
2091 if (tensor.item<int64_t>() !=
geoDim_)
2092 throw std::runtime_error(
"geoDim mismatch");
2095 archive.read(key +
".degree[" + std::to_string(i) +
"]", tensor);
2096 if (tensor.item<int64_t>() !=
degrees_[i])
2097 throw std::runtime_error(
"degrees mismatch");
2101 archive.read(key +
".nknots[" + std::to_string(i) +
"]", tensor);
2102 nknots_[i] = tensor.item<int64_t>();
2106 archive.read(key +
".knots[" + std::to_string(i) +
"]",
knots_[i]);
2109 archive.read(key +
".ncoeffs[" + std::to_string(i) +
"]", tensor);
2110 ncoeffs_[i] = tensor.item<int64_t>();
2117 archive.read(key +
".coeffs[" + std::to_string(i) +
"]",
coeffs_[i]);
2123 inline void save(
const std::string &filename,
2124 const std::string &key =
"bspline")
const {
2125 torch::serialize::OutputArchive archive;
2126 write(archive, key).save_to(filename);
2130 inline torch::serialize::OutputArchive &
2131 write(torch::serialize::OutputArchive &archive,
2132 const std::string &key =
"bspline")
const {
2133 archive.write(key +
".parDim", torch::full({1},
parDim_));
2134 archive.write(key +
".geoDim", torch::full({1},
geoDim_));
2137 archive.write(key +
".degree[" + std::to_string(i) +
"]",
2141 archive.write(key +
".nknots[" + std::to_string(i) +
"]",
2142 torch::full({1},
nknots_[i]));
2145 archive.write(key +
".knots[" + std::to_string(i) +
"]",
knots_[i]);
2148 archive.write(key +
".ncoeffs[" + std::to_string(i) +
"]",
2152 archive.write(key +
".coeffs[" + std::to_string(i) +
"]",
coeffs_[i]);
2159 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2161 real_t rtol = real_t{1e-5}, real_t atol = real_t{1e-8})
const {
2162 if constexpr (!std::is_same_v<real_t, other_t>)
2182 result *= torch::allclose(
knots(i), other.
knots(i), rtol, atol);
2185 result *= torch::allclose(
coeffs(i), other.
coeffs(i), rtol, atol);
2191 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2194 if constexpr (!std::is_same_v<real_t, other_t>)
2214 result *= torch::equal(
knots(i), other.
knots(i));
2223 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2238 assert(numRefine > 0);
2239 assert(dim == -1 || (dim >= 0 && dim <
parDim_));
2245 for (
int refine = 0; refine < numRefine; ++refine) {
2262 std::vector<real_t> kv;
2265 for (int64_t j = 0; j <
degrees_[i]; ++j)
2266 kv.push_back(
static_cast<real_t
>(0));
2269 kv.push_back(
static_cast<real_t
>(j) /
2272 for (int64_t j = 0; j <
degrees_[i]; ++j)
2273 kv.push_back(
static_cast<real_t
>(1));
2284 knots_indices[i] =
knots[i].index(
2285 {torch::indexing::Slice(0,
knots[i].numel() -
degrees_[i] - 1)});
2310 if constexpr (
degree > terminal)
2311 return degree * eval_prefactor<degree - 1, deriv, terminal>();
2324 throw std::runtime_error(
2325 "Not enough coefficients to create open knot vector");
2332 auto inner = torch::empty({0},
options_);
2334 if (num_inner > 0) {
2335 inner = torch::arange(0, num_inner + 1,
options_);
2336 inner = inner /
static_cast<real_t
>(num_inner);
2339 knots_[i] = torch::cat({start, inner, end});
2390 coeffs_[i] = torch::kron(torch::linspace(
static_cast<real_t
>(0),
2391 static_cast<real_t
>(1),
2422 options_.requires_grad(
false).template dtype<int64_t>())
2429 options_.requires_grad(
false).template dtype<int64_t>())
2433 auto indices = idx_base + offsets;
2436 auto gathered =
knots_[j]
2437 .index_select(0, indices.flatten())
2441 auto greville_ = gathered.mean(1);
2462 std::pow(10, i) * 0, std::pow(10, i) * (size - 1), size,
options_);
2468 throw std::runtime_error(
"Unsupported init option");
2479 assert(
knots[i].numel() == knot_indices[i].numel() +
degrees_[i] + 1);
2483 auto basfunc = update_coeffs_univariate<degrees_[0], 0>(
2484 knots[0].flatten(), knot_indices[0].flatten());
2491 .index_select(0, coeff_indices)
2492 .view({-1, knot_indices[0].numel()}))
2493 .view(knot_indices[0].sizes());
2498 auto basfunc_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
2499 if constexpr (
sizeof...(Is) == 1)
2501 knots[Is].flatten(), knot_indices[Is].flatten()),
2505 knots[Is].flatten(), knot_indices[Is].flatten())...);
2515 for (
short_t i = start_index; i <= stop_index; ++i)
2516 result *= array[i].numel();
2525 .repeat_interleave(prod_(knot_indices, 0, i - 1), 0)
2526 .repeat(prod_(knot_indices, i + 1,
parDim_ - 1));
2533 .index_select(0, coeff_indices)
2534 .view({-1, knot_indices_[0].numel()}))
2535 .view(knot_indices_[0].sizes());
2644 template <
short_t degree,
short_t dim,
short_t deriv>
2646 const torch::Tensor &knot_indices)
const {
2647 assert(xi.sizes() == knot_indices.sizes());
2653 torch::Tensor b = torch::ones({xi.numel()},
options_);
2668 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2669 .to(::iganet::dtype<real_t>());
2675 auto w = torch::div(xi.repeat(k) - t1 - mask, t21 - mask);
2678 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2681 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2695 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2696 .to(::iganet::dtype<real_t>());
2702 auto w = torch::div(torch::ones_like(t21,
options_) - mask, t21 - mask);
2705 b = torch::cat({torch::mul(-w, b), torch::zeros_like(xi,
options_)},
2707 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2710 return b.view({
degree + 1, xi.numel()});
2714 template <
short_t degree,
short_t dim,
short_t deriv>
2716 const torch::Tensor &knot_indices)
const {
2717 assert(xi.sizes() == knot_indices.sizes());
2722 torch::Tensor b = torch::ones({xi.numel()},
options_);
2729 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2730 .to(::iganet::dtype<real_t>());
2732 auto w = torch::div(xi.repeat(k) - t1 - mask, t21 - mask);
2734 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2737 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2745 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2746 .to(::iganet::dtype<real_t>());
2748 auto w = torch::div(torch::ones_like(t21,
options_) - mask, t21 - mask);
2750 b = torch::cat({torch::mul(-w, b), torch::zeros_like(xi,
options_)},
2752 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2756 return b.view({
degree + 1, xi.numel()}).transpose(0, 1);
2766 template <
short_t degree,
short_t dim>
2769 const torch::Tensor &knot_indices)
const {
2772 torch::Tensor b = torch::ones({knot_indices.numel()},
options_);
2787 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2788 .to(::iganet::dtype<real_t>());
2794 auto w = torch::div(
2795 knots.index({torch::indexing::Slice(k, knot_indices.numel() + k)})
2801 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2802 torch::zeros_like(knot_indices,
options_)},
2805 {torch::zeros_like(knot_indices,
options_), torch::mul(w, b)}, 0);
2808 return b.view({
degree + 1, knot_indices.numel()});
2817#ifdef IGANET_WITH_GISMO
2822 auto [coeffs_cpu, coeffs_accessor] =
2823 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2824 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
2826 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
2829 std::array<gismo::gsKnotVector<real_t>,
parDim_> kv;
2832 auto [knots_cpu, knots_accessor] =
2833 utils::to_tensorAccessor<real_t, 1>(
knots_[i], torch::kCPU);
2834 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
2835 kv[i] = gismo::gsKnotVector<real_t>(
degrees_[i], knots_cpu_ptr,
2836 knots_cpu_ptr + knots_cpu.size(0));
2841 return gismo::gsBSpline<real_t>(gismo::give(kv[0]), gismo::give(coefs));
2843 }
else if constexpr (
parDim_ == 2) {
2845 return gismo::gsTensorBSpline<parDim_, real_t>(
2846 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(coefs));
2848 }
else if constexpr (
parDim_ == 3) {
2850 return gismo::gsTensorBSpline<parDim_, real_t>(
2851 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(kv[2]),
2852 gismo::give(coefs));
2854 }
else if constexpr (
parDim_ == 4) {
2856 return gismo::gsTensorBSpline<parDim_, real_t>(
2857 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(kv[2]),
2858 gismo::give(kv[3]), gismo::give(coefs));
2861 throw std::runtime_error(
"Invalid parametric dimension");
2864 throw std::runtime_error(
2865 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
2869#ifdef IGANET_WITH_GISMO
2872 gismo::gsBSpline<real_t> &
to_gismo(gismo::gsBSpline<real_t> &bspline,
2873 bool updateKnotVector =
true,
2874 bool updateCoeffs =
true)
const {
2876 if (updateKnotVector) {
2880 if (bspline.degree(0) !=
degrees_[0])
2881 throw std::runtime_error(
"Degrees mismatch");
2883 auto [knots_cpu, knots_accessor] =
2884 utils::to_tensorAccessor<real_t, 1>(
knots_[0], torch::kCPU);
2885 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
2887 gismo::gsKnotVector<real_t> kv(
degrees_[0], knots_cpu_ptr,
2888 knots_cpu_ptr + knots_cpu.size(0));
2890 bspline.knots(0).swap(kv);
2893 throw std::runtime_error(
"Invalid parametric dimension");
2899 auto [coeffs_cpu, coeffs_accessor] =
2900 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2901 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
2902 bspline.coefs().col(g) =
2903 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
2911 gismo::gsTensorBSpline<parDim_, real_t> &
2912 to_gismo(gismo::gsTensorBSpline<parDim_, real_t> &bspline,
2913 bool updateKnotVector =
true,
bool updateCoeffs =
true)
const {
2915 if (updateKnotVector) {
2919 assert(bspline.degree(i) ==
degrees_[i]);
2922 auto [knots_cpu, knots_accessor] =
2923 utils::to_tensorAccessor<real_t, 1>(
knots_[i], torch::kCPU);
2924 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
2926 gismo::gsKnotVector<real_t> kv(
degrees_[i], knots_cpu_ptr,
2927 knots_cpu_ptr + knots_cpu.size(0));
2928 bspline.knots(i).swap(kv);
2935 auto [coeffs_cpu, coeffs_accessor] =
2936 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2937 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
2938 bspline.coefs().col(g) =
2939 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
2948 template <
typename BSpline>
2949 BSpline &
to_gismo(BSpline &bspline,
bool,
bool)
const {
2950 throw std::runtime_error(
2951 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
2957#ifdef IGANET_WITH_GISMO
2960 auto &
from_gismo(
const gismo::gsBSpline<real_t> &bspline,
2961 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
2963 if (updateKnotVector) {
2965 throw std::runtime_error(
2966 "Knot vectors can only be updated for Non-uniform B-splines");
2971 if (bspline.coefs().cols() !=
geoDim_)
2972 throw std::runtime_error(
"Geometric dimensions mismatch");
2975 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
2979 auto [coeffs_cpu, coeffs_accessor] =
2980 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2982 const real_t *coeffs_ptr = bspline.coefs().col(g).data();
2984 for (int64_t i = 0; i <
ncoeffs_[g]; ++i)
2985 coeffs_accessor[i] = coeffs_ptr[i];
2995 auto &
from_gismo(
const gismo::gsTensorBSpline<parDim_, real_t> &bspline,
2996 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
2998 if (updateKnotVector) {
3000 throw std::runtime_error(
3001 "Knot vectors can only be updated for Non-uniform B-splines");
3006 if (bspline.coefs().cols() !=
geoDim_)
3007 throw std::runtime_error(
"Geometric dimensions mismatch");
3010 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3014 auto [coeffs_cpu, coeffs_accessor] =
3015 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
3017 const real_t *coeffs_ptr = bspline.coefs().col(g).data();
3019 for (int64_t i = 0; i <
ncoeffs_[g]; ++i)
3020 coeffs_accessor[i] = coeffs_ptr[i];
3031 template <
typename BSpline>
auto &
from_gismo(BSpline &bspline,
bool,
bool) {
3032 throw std::runtime_error(
3033 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3042inline torch::serialize::OutputArchive &
3043operator<<(torch::serialize::OutputArchive &archive,
3045 return obj.
write(archive);
3050inline torch::serialize::InputArchive &
3053 return obj.
read(archive);
3078 template <
template <
typename,
short_t,
short_t...>
class BSpline,
3079 std::make_signed_t<short_t> degree_elevate = 0>
3080 using derived_type = BSpline<real_t, GeoDim, (Degrees + degree_elevate)...>;
3084 template <std::make_
signed_t<
short_t> degree_elevate = 0>
3091 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
3097 template <
typename other_t>
3118 const std::array<std::vector<typename Base::value_type>,
Base::parDim_>
3153 .to(
options.requires_grad(
false))
3163 const std::array<std::vector<typename Base::value_type>,
Base::parDim_>
3169 throw std::runtime_error(
"Knot vector is too short for an open knot "
3170 "vector (n+p+1 > 2*(p+1))");
3185 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3186 inline auto eval(
const torch::Tensor &xi)
const {
3190 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3201 return Base::template eval<deriv, memory_optimized>(
3205 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3218 return Base::template eval<deriv, memory_optimized>(xi, knot_indices);
3221 template <deriv deriv = deriv::func,
bool memory_optimized = false>
3224 const torch::Tensor &coeff_indices)
const {
3234 return Base::template eval<deriv, memory_optimized>(xi, knot_indices,
3254 return torch::zeros_like(
Base::coeffs_[0]).to(torch::kInt64);
3264 auto nnz =
Base::knots_[i].repeat({xi[i].numel(), 1}) >
3265 xi[i].flatten().view({-1, 1});
3267 torch::remainder(std::get<1>(((nnz.cumsum(1) == 1) & nnz).max(1)) - 1,
3269 .view(xi[i].sizes());
3283 assert(numRefine > 0);
3291 auto [kv_cpu, kv_accessor] =
3292 utils::to_tensorAccessor<typename Base::value_type, 1>(
3295 std::vector<typename Base::value_type> kv;
3297 kv.push_back(kv_accessor[0]);
3299 for (int64_t j = 1; j < kv_accessor.size(0); ++j) {
3301 if ((dim == -1 || dim == i) && (kv_accessor[j - 1] < kv_accessor[j]))
3302 for (
int refine = 1; refine < (2 << (numRefine - 1)); ++refine)
3304 kv_accessor[j - 1] +
3307 (kv_accessor[j] - kv_accessor[j - 1]));
3309 kv.push_back(kv_accessor[j]);
3323 knots_indices[i] =
knots[i].index({torch::indexing::Slice(
3367 knots_indices[i] =
knots_[i].index({torch::indexing::Slice(
3392 assert(numReduce > 0);
3400 auto [kv_cpu, kv_accessor] =
3401 utils::to_tensorAccessor<typename Base::value_type, 1>(
3404 std::vector<typename Base::value_type> kv;
3406 kv.push_back(kv_accessor[0]);
3408 for (int64_t j = 1; j < kv_accessor.size(0); ++j) {
3410 if ((dim == -1 || dim == i) && (kv_accessor[j - 1] < kv_accessor[j]) &&
3411 (kv_accessor[j] < kv_accessor[kv_accessor.size(0) - 1]))
3412 for (
int reduce = 0; reduce < numReduce; ++reduce)
3413 kv.push_back(kv_accessor[j]);
3415 kv.push_back(kv_accessor[j]);
3429 knots_indices[i] =
knots[i].index({torch::indexing::Slice(
3450#ifdef IGANET_WITH_GISMO
3453 auto &
from_gismo(
const gismo::gsBSpline<typename Base::value_type> &bspline,
3454 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3456 if (updateKnotVector) {
3461 throw std::runtime_error(
"Degrees mismatch");
3464 throw std::runtime_error(
"Knot vector dimensions mismatch");
3466 auto [knots0_cpu, knots0_accessor] =
3467 utils::to_tensorAccessor<typename Base::value_type, 1>(
3471 bspline.knots(0).asMatrix().data();
3474 knots0_accessor[i] = knots0_ptr[i];
3479 throw std::runtime_error(
"Invalid parametric dimension");
3485 throw std::runtime_error(
"Geometric dimensions mismatch");
3488 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3492 auto [coeffs_cpu, coeffs_accessor] =
3493 utils::to_tensorAccessor<typename Base::value_type, 1>(
3497 bspline.coefs().row(g).data();
3500 coeffs_accessor[i] = coeffs_ptr[i];
3511 const gismo::gsTensorBSpline<Base::parDim_, typename Base::value_type>
3513 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3515 if (updateKnotVector) {
3519 throw std::runtime_error(
"Degrees mismatch");
3522 throw std::runtime_error(
"Knot vector dimensions mismatch");
3524 auto [knots_cpu, knots_accessor] =
3525 utils::to_tensorAccessor<typename Base::value_type, 1>(
3529 bspline.knots(i).asMatrix().data();
3532 knots_accessor[i] = knots_ptr[i];
3541 throw std::runtime_error(
"Geometric dimensions mismatch");
3544 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3548 auto [coeffs_cpu, coeffs_accessor] =
3549 utils::to_tensorAccessor<typename Base::value_type, 1>(
3553 bspline.coefs().row(g).data();
3556 coeffs_accessor[i] = coeffs_ptr[i];
3567 template <
typename BSpline>
auto &
from_gismo(BSpline &bspline,
bool,
bool) {
3568 throw std::runtime_error(
3569 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3581template <
typename T>
3586template <
typename T>
3588 std::is_base_of_v<Spline_, T> && std::is_base_of_v<UniformSplineCore_, T> &&
3589 !std::is_base_of_v<NonUniformSplineCore_, T>;
3593template <
typename T>
3595 std::is_base_of_v<NonUniformSplineCore_, T>;
3610template <
typename BSplineCore>
3617 using BSplineCore::BSplineCore;
3625 std::make_signed_t<short_t> degree_elevate = 0>
3631 template <std::make_
signed_t<
short_t> degree_elevate = 0>
3641 real_t, GeoDim, Degrees...>>;
3645 template <
typename other_t>
3650 using Ptr = std::shared_ptr<BSplineCommon>;
3653 using uPtr = std::unique_ptr<BSplineCommon>;
3661 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3662 BSplineCore::coeffs_[i] = other.coeffs(i).
clone();
3671 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3672 BSplineCore::coeffs_[i] = coeffs[i].
clone();
3674 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3675 BSplineCore::coeffs_[i] = coeffs[i];
3685 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3686 BSplineCore::coeffs_[i] = std::move(coeffs[i]);
3723 make_unique(
const std::array<std::vector<typename BSplineCore::value_type>,
3724 BSplineCore::parDim_> &kv,
3732 make_unique(
const std::array<std::vector<typename BSplineCore::value_type>,
3733 BSplineCore::parDim_> &kv,
3747 return std::make_shared<BSplineCommon>(options);
3755 return std::make_shared<BSplineCommon>(ncoeffs,
init, options);
3764 return std::make_shared<BSplineCommon>(ncoeffs, coeffs,
clone, options);
3772 return std::make_shared<BSplineCommon>(ncoeffs, coeffs, options);
3776 make_shared(
const std::array<std::vector<typename BSplineCore::value_type>,
3777 BSplineCore::parDim_> &kv,
3781 return std::make_shared<BSplineCommon>(kv,
init, options);
3785 make_shared(
const std::array<std::vector<typename BSplineCore::value_type>,
3786 BSplineCore::parDim_> &kv,
3791 return std::make_shared<BSplineCommon>(kv, coeffs,
clone, options);
3802 BSplineCore::uniform_refine(numRefine, dim);
3810 result.nknots_ = BSplineCore::nknots_;
3811 result.ncoeffs_ = BSplineCore::ncoeffs_;
3812 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
3814 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3815 result.knots_[i] = BSplineCore::knots_[i].
clone();
3817 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3818 result.coeffs_[i] = BSplineCore::coeffs_[i].
clone();
3828 result.nknots_ = BSplineCore::nknots_;
3829 result.ncoeffs_ = BSplineCore::ncoeffs_;
3830 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
3832 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3833 result.knots_[i] = BSplineCore::knots_[i].
to(options);
3835 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3836 result.coeffs_[i] = BSplineCore::coeffs_[i].
to(options);
3842 inline auto to(torch::Device device)
const {
3845 result.nknots_ = BSplineCore::nknots_;
3846 result.ncoeffs_ = BSplineCore::ncoeffs_;
3847 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
3849 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3850 result.knots_[i] = BSplineCore::knots_[i].
to(device);
3852 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3853 result.coeffs_[i] = BSplineCore::coeffs_[i].
to(device);
3859 template <
typename real_t>
inline auto to()
const {
3860 return to(BSplineCore::options_.
template dtype<real_t>());
3870 return this->
clone().diff_(other, dim);
3881 bool compatible(
true);
3883 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3884 compatible *= (BSplineCore::nknots(i) == other.nknots(i));
3886 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3887 compatible *= (BSplineCore::ncoeffs(i) == other.ncoeffs(i));
3890 throw std::runtime_error(
"B-splines are not compatible");
3893 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3894 BSplineCore::coeffs(i) -= other.coeffs(i);
3896 BSplineCore::coeffs(dim) -= other.coeffs(dim);
3908 return this->
clone().abs_diff_(other, dim);
3919 bool compatible(
true);
3921 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3922 compatible *= (BSplineCore::nknots(i) == other.nknots(i));
3924 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3925 compatible *= (BSplineCore::ncoeffs(i) == other.ncoeffs(i));
3928 throw std::runtime_error(
"B-splines are not compatible");
3931 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3932 BSplineCore::coeffs(i) =
3933 torch::abs(BSplineCore::coeffs(i) - other.coeffs(i));
3935 BSplineCore::coeffs(dim) =
3936 torch::abs(BSplineCore::coeffs(dim) - other.coeffs(dim));
3945 return torch::mean(torch::pow(BSplineCore::eval(BSplineCore::greville())(0), 2));
3949 inline auto scale(BSplineCore::value_type s,
int dim = -1)
const {
3950 return this->
clone().scale_(s, dim);
3954 inline auto scale_(BSplineCore::value_type s,
int dim = -1) {
3956 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
3957 BSplineCore::coeffs(i) *= s;
3959 BSplineCore::coeffs(dim) *= s;
3965 scale(std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
const {
3966 return this->
clone().scale_(v);
3971 scale_(std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
3972 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
3973 BSplineCore::coeffs(i) *= v[i];
3979 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
const {
3980 return this->
clone().translate_(v);
3985 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
3986 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
3987 BSplineCore::coeffs(i) += v[i];
3992 inline auto rotate(BSplineCore::value_type angle)
const {
3993 return this->
clone().rotate_(angle);
3997 inline auto rotate_(BSplineCore::value_type angle) {
3999 static_assert(BSplineCore::geoDim() == 2,
4000 "Rotation about one angle is only available in 2D");
4003 coeffs[0] = std::cos(angle) * BSplineCore::coeffs(0) -
4004 std::sin(angle) * BSplineCore::coeffs(1);
4005 coeffs[1] = std::sin(angle) * BSplineCore::coeffs(0) +
4006 std::cos(angle) * BSplineCore::coeffs(1);
4008 BSplineCore::coeffs().swap(coeffs);
4013 inline auto rotate(std::array<typename BSplineCore::value_type, 3> angle)
const {
4014 return this->
clone().rotate_(angle);
4018 inline auto rotate_(std::array<typename BSplineCore::value_type, 3> angle) {
4020 static_assert(BSplineCore::geoDim() == 3,
4021 "Rotation about two angles is only available in 3D");
4025 std::cos(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(0) +
4026 (std::sin(angle[0]) * std::sin(angle[1]) * std::cos(angle[2]) -
4027 std::cos(angle[0]) * std::sin(angle[2])) *
4028 BSplineCore::coeffs(1) +
4029 (std::cos(angle[0]) * std::sin(angle[1]) * std::cos(angle[2]) +
4030 std::sin(angle[0]) * std::sin(angle[2])) *
4031 BSplineCore::coeffs(2);
4034 std::cos(angle[1]) * std::sin(angle[2]) * BSplineCore::coeffs(0) +
4035 (std::sin(angle[0]) * std::sin(angle[1]) * std::sin(angle[2]) +
4036 std::cos(angle[0]) * std::cos(angle[2])) *
4037 BSplineCore::coeffs(1) +
4038 (std::cos(angle[0]) * std::sin(angle[1]) * std::sin(angle[2]) -
4039 std::sin(angle[0]) * std::cos(angle[2])) *
4040 BSplineCore::coeffs(2);
4043 -std::sin(angle[1]) * BSplineCore::coeffs(0) +
4044 std::sin(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(1) +
4045 std::cos(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(2);
4047 BSplineCore::coeffs().swap(coeffs);
4055 auto min_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4056 return torch::stack({BSplineCore::coeffs(Is).min()...});
4060 auto max_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4061 return torch::stack({BSplineCore::coeffs(Is).max()...});
4064 std::pair<torch::Tensor, torch::Tensor> bbox;
4065 bbox.first = min_(std::make_index_sequence<BSplineCore::geoDim_>{});
4066 bbox.second = max_(std::make_index_sequence<BSplineCore::geoDim_>{});
4077 template <
bool memory_optimized = false>
4078 inline auto nv(
const torch::Tensor &xi)
const {
4082 template <
bool memory_optimized = false>
4084 return nv<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4096 template <
bool memory_optimized = false>
4100 return nv<memory_optimized>(
4102 BSplineCore::template find_coeff_indices<memory_optimized>(
4117 template <
bool memory_optimized = false>
4120 const torch::Tensor &coeff_indices)
const {
4122 if constexpr (BSplineCore::parDim_ == 1 && BSplineCore::geoDim_ == 2) {
4124 auto eval_ = BSplineCore::template eval<deriv::dx, memory_optimized>(
4125 xi, knot_indices, coeff_indices);
4127 }
else if constexpr (BSplineCore::parDim_ == 1 &&
4128 BSplineCore::geoDim_ == 3) {
4130 auto t_ = BSplineCore::template eval<deriv::dx, memory_optimized>(
4131 xi, knot_indices, coeff_indices)
4133 auto a_ = BSplineCore::template eval<deriv::dx ^ 2, memory_optimized>(
4134 xi, knot_indices, coeff_indices);
4135 auto n_ = a_ - a_.dot(t_) * t_;
4137 n_(0, 0), n_(0, 1), n_(0, 2), t_(0, 0), t_(0, 1), t_(0, 2));
4138 }
else if constexpr (BSplineCore::parDim_ == 2 &&
4139 BSplineCore::geoDim_ == 3) {
4141 auto jac_ = jac<memory_optimized>(xi, knot_indices, coeff_indices);
4143 jac_(1, 0) * jac_(2, 1) - jac_(2, 0) * jac_(1, 1),
4144 jac_(2, 0) * jac_(0, 1) - jac_(0, 0) * jac_(2, 1),
4145 jac_(0, 0) * jac_(1, 1) - jac_(1, 0) * jac_(0, 1));
4147 throw std::runtime_error(
"Unsupported parametric/geometric dimension");
4175 template <
bool memory_optimized = false>
4176 inline auto curl(
const torch::Tensor &xi)
const {
4180 template <
bool memory_optimized = false>
4182 return curl<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4210 template <
bool memory_optimized = false>
4214 return curl<memory_optimized>(
4216 BSplineCore::template find_coeff_indices<memory_optimized>(
4246 template <
bool memory_optimized = false>
4249 const torch::Tensor &coeff_indices)
const {
4251 static_assert(BSplineCore::parDim_ == BSplineCore::geoDim_,
4252 "curl(.) requires that parametric and geometric dimension "
4256 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4257 assert(xi[i].sizes() == knot_indices[i].sizes());
4258 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4259 assert(xi[0].sizes() == xi[i].sizes());
4261 if constexpr (BSplineCore::parDim_ == 2)
4269 *BSplineCore::template eval<deriv::dx, memory_optimized>(
4270 xi, knot_indices, coeff_indices)[1] -
4271 *BSplineCore::template eval<deriv::dy, memory_optimized>(
4272 xi, knot_indices, coeff_indices)[0]);
4274 else if constexpr (BSplineCore::parDim_ == 3)
4280 *BSplineCore::template eval<deriv::dy, memory_optimized>(
4281 xi, knot_indices, coeff_indices)[2] -
4282 *BSplineCore::template eval<deriv::dz, memory_optimized>(
4283 xi, knot_indices, coeff_indices)[1],
4284 *BSplineCore::template eval<deriv::dz, memory_optimized>(
4285 xi, knot_indices, coeff_indices)[0] +
4286 *BSplineCore::template eval<deriv::dx, memory_optimized>(
4287 xi, knot_indices, coeff_indices)[2],
4288 *BSplineCore::template eval<deriv::dx, memory_optimized>(
4289 xi, knot_indices, coeff_indices)[1] +
4290 *BSplineCore::template eval<deriv::dy, memory_optimized>(
4291 xi, knot_indices, coeff_indices)[0]);
4294 throw std::runtime_error(
"Unsupported parametric/geometric dimension");
4320 template <
bool memory_optimized = false,
typename Geometry>
4321 inline auto icurl(
const Geometry &G,
const torch::Tensor &xi)
const {
4322 if constexpr (BSplineCore::parDim_ == 0)
4324 torch::zeros_like(BSplineCore::coeffs_[0])};
4329 template <
bool memory_optimized = false,
typename Geometry>
4332 if constexpr (BSplineCore::parDim_ == 0)
4334 torch::zeros_like(BSplineCore::coeffs_[0])};
4336 return icurl<memory_optimized, Geometry>(
4337 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4364 template <
bool memory_optimized = false,
typename Geometry>
4369 if constexpr (BSplineCore::parDim_ == 0)
4371 torch::zeros_like(BSplineCore::coeffs_[0])};
4373 return icurl<memory_optimized, Geometry>(
4374 G, xi, knot_indices,
4375 BSplineCore::template find_coeff_indices<memory_optimized>(
4378 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4411 template <
bool memory_optimized = false,
typename Geometry>
4415 const torch::Tensor &coeff_indices,
4417 const torch::Tensor &coeff_indices_G)
const {
4419 if constexpr (BSplineCore::parDim_ == 0)
4421 torch::zeros_like(BSplineCore::coeffs_[0])};
4424 det[0] = std::make_shared<torch::Tensor>(torch::reciprocal(
4425 G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G)
4428 return det * (curl<memory_optimized>(xi, knot_indices, coeff_indices) *
4429 G.template jac<memory_optimized>(xi, knot_indices_G,
4455 template <
bool memory_optimized = false>
4456 inline auto div(
const torch::Tensor &xi)
const {
4457 if constexpr (BSplineCore::parDim_ == 0)
4459 torch::zeros_like(BSplineCore::coeffs_[0])};
4463 template <
bool memory_optimized = false>
4465 if constexpr (BSplineCore::parDim_ == 0)
4467 torch::zeros_like(BSplineCore::coeffs_[0])};
4468 return div<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4494 template <
bool memory_optimized = false>
4498 if constexpr (BSplineCore::parDim_ == 0)
4500 torch::zeros_like(BSplineCore::coeffs_[0])};
4501 return div<memory_optimized>(
4503 BSplineCore::template find_coeff_indices<memory_optimized>(
4532 template <
bool memory_optimized = false>
4535 const torch::Tensor &coeff_indices)
const {
4537 static_assert(BSplineCore::parDim_ == BSplineCore::geoDim_,
4538 "div(.) requires parDim == geoDim");
4540 if constexpr (BSplineCore::parDim_ == 0)
4542 torch::zeros_like(BSplineCore::coeffs_[0])};
4547 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4548 assert(xi[i].sizes() == knot_indices[i].sizes());
4549 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4550 assert(xi[0].sizes() == xi[i].sizes());
4553 auto div_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4555 (*BSplineCore::template eval<
4557 memory_optimized>(xi, knot_indices, coeff_indices)[Is] +
4561 return div_(std::make_index_sequence<BSplineCore::parDim_>{});
4587 template <
bool memory_optimized = false,
typename Geometry>
4588 inline auto idiv(
const Geometry &G,
const torch::Tensor &xi) {
4589 if constexpr (BSplineCore::parDim_ == 0)
4591 torch::zeros_like(BSplineCore::coeffs_[0])};
4596 template <
bool memory_optimized = false,
typename Geometry>
4597 inline auto idiv(
const Geometry &G,
4599 if constexpr (BSplineCore::parDim_ == 0)
4601 torch::zeros_like(BSplineCore::coeffs_[0])};
4603 return idiv<memory_optimized, Geometry>(
4604 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4633 template <
bool memory_optimized = false,
typename Geometry>
4638 if constexpr (BSplineCore::parDim_ == 0)
4640 torch::zeros_like(BSplineCore::coeffs_[0])};
4642 return idiv<memory_optimized, Geometry>(
4643 G, xi, knot_indices,
4644 BSplineCore::template find_coeff_indices<memory_optimized>(
4647 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4681 template <
bool memory_optimized = false,
typename Geometry>
4682 inline auto idiv(
const Geometry &G,
4685 const torch::Tensor &coeff_indices,
4687 const torch::Tensor &coeff_indices_G)
const {
4688 if constexpr (BSplineCore::parDim_ == 0)
4690 torch::zeros_like(BSplineCore::coeffs_[0])};
4692 return ijac<memory_optimized, Geometry>(G, xi, knot_indices,
4693 coeff_indices, knot_indices_G,
4718 template <
bool memory_optimized = false>
4719 inline auto grad(
const torch::Tensor &xi)
const {
4721 static_assert(BSplineCore::geoDim_ == 1,
4722 "grad(.) requires 1D variable, use jac(.) instead");
4724 if constexpr (BSplineCore::parDim_ == 0)
4726 torch::zeros_like(BSplineCore::coeffs_[0])};
4731 template <
bool memory_optimized = false>
4734 static_assert(BSplineCore::geoDim_ == 1,
4735 "grad(.) requires 1D variable, use jac(.) instead");
4737 if constexpr (BSplineCore::parDim_ == 0)
4739 torch::zeros_like(BSplineCore::coeffs_[0])};
4741 return grad<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4766 template <
bool memory_optimized = false>
4771 static_assert(BSplineCore::geoDim_ == 1,
4772 "grad(.) requires 1D variable, use jac(.) instead");
4774 if constexpr (BSplineCore::parDim_ == 0)
4776 torch::zeros_like(BSplineCore::coeffs_[0])};
4778 return grad<memory_optimized>(
4780 BSplineCore::template find_coeff_indices<memory_optimized>(
4808 template <
bool memory_optimized = false>
4811 const torch::Tensor &coeff_indices)
const {
4813 static_assert(BSplineCore::geoDim_ == 1,
4814 "grad(.) requires 1D variable, use jac(.) instead");
4816 if constexpr (BSplineCore::parDim_ == 0)
4818 torch::zeros_like(BSplineCore::coeffs_[0])};
4822 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4823 assert(xi[i].sizes() == knot_indices[i].sizes());
4824 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4825 assert(xi[0].sizes() == xi[i].sizes());
4828 auto grad_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4830 BSplineCore::template eval<
4832 memory_optimized>(xi, knot_indices, coeff_indices)...};
4835 return grad_(std::make_index_sequence<BSplineCore::parDim_>{});
4860 template <
bool memory_optimized = false,
typename Geometry>
4861 inline auto igrad(
const Geometry &G,
const torch::Tensor &xi)
const {
4862 if constexpr (BSplineCore::parDim_ == 0)
4864 torch::zeros_like(BSplineCore::coeffs_[0])};
4869 template <
bool memory_optimized = false,
typename Geometry>
4872 if constexpr (BSplineCore::parDim_ == 0)
4874 torch::zeros_like(BSplineCore::coeffs_[0])};
4876 return igrad<memory_optimized, Geometry>(
4877 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4904 template <
bool memory_optimized = false,
typename Geometry>
4909 if constexpr (BSplineCore::parDim_ == 0)
4911 torch::zeros_like(BSplineCore::coeffs_[0])};
4913 return igrad<memory_optimized, Geometry>(
4914 G, xi, knot_indices,
4915 BSplineCore::template find_coeff_indices<memory_optimized>(
4918 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4951 template <
bool memory_optimized = false,
typename Geometry>
4955 const torch::Tensor &coeff_indices,
4957 const torch::Tensor &coeff_indices_G)
const {
4958 if constexpr (BSplineCore::parDim_ == 0)
4960 torch::zeros_like(BSplineCore::coeffs_[0])};
4962 return grad<memory_optimized>(xi, knot_indices, coeff_indices) *
4963 G.template jac<memory_optimized>(xi, knot_indices_G,
5004 template <
bool memory_optimized = false>
5005 inline auto hess(
const torch::Tensor &xi)
const {
5006 if constexpr (BSplineCore::parDim_ == 0)
5008 torch::zeros_like(BSplineCore::coeffs_[0])};
5013 template <
bool memory_optimized = false>
5015 if constexpr (BSplineCore::parDim_ == 0)
5017 torch::zeros_like(BSplineCore::coeffs_[0])};
5019 return hess<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5059 template <
bool memory_optimized = false>
5063 if constexpr (BSplineCore::parDim_ == 0)
5065 torch::zeros_like(BSplineCore::coeffs_[0])};
5067 return hess<memory_optimized>(
5069 BSplineCore::template find_coeff_indices<memory_optimized>(
5111 template <
bool memory_optimized = false>
5114 const torch::Tensor &coeff_indices)
const {
5116 if constexpr (BSplineCore::parDim_ == 0)
5118 torch::zeros_like(BSplineCore::coeffs_[0])};
5122 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5123 assert(xi[i].sizes() == knot_indices[i].sizes());
5124 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5125 assert(xi[0].sizes() == xi[i].sizes());
5128 auto hess_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5130 BSplineCore::geoDim_, BSplineCore::parDim_>{
5131 BSplineCore::template eval<
5135 10, Is % BSplineCore::parDim_>::value),
5136 memory_optimized>(xi, knot_indices, coeff_indices)...}
5140 return hess_(std::make_index_sequence<BSplineCore::parDim_ *
5141 BSplineCore::parDim_>{});
5172 template <
bool memory_optimized = false,
typename Geometry>
5173 inline auto ihess(
const Geometry &G,
const torch::Tensor &xi)
const {
5174 if constexpr (BSplineCore::parDim_ == 0)
5176 torch::zeros_like(BSplineCore::coeffs_[0])};
5181 template <
bool memory_optimized = false,
typename Geometry>
5184 if constexpr (BSplineCore::parDim_ == 0)
5186 torch::zeros_like(BSplineCore::coeffs_[0])};
5188 return ihess<memory_optimized, Geometry>(
5189 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5222 template <
bool memory_optimized = false,
typename Geometry>
5227 if constexpr (BSplineCore::parDim_ == 0)
5229 torch::zeros_like(BSplineCore::coeffs_[0])};
5231 return ihess<memory_optimized, Geometry>(
5232 G, xi, knot_indices,
5233 BSplineCore::template find_coeff_indices<memory_optimized>(
5236 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5274 template <
bool memory_optimized = false,
typename Geometry>
5278 const torch::Tensor &coeff_indices,
5280 const torch::Tensor &coeff_indices_G)
const {
5282 if constexpr (BSplineCore::parDim_ == 0)
5284 torch::zeros_like(BSplineCore::coeffs_[0])};
5287 BSplineCore::parDim_, BSplineCore::geoDim_>
5290 auto hessG = G.template hess<memory_optimized>(xi, knot_indices_G,
5292 auto ijacG = ijac<memory_optimized>(G, xi, knot_indices, coeff_indices,
5293 knot_indices_G, coeff_indices_G);
5295 for (
short_t component = 0; component < BSplineCore::geoDim_;
5297 auto hess_component =
5298 hess<memory_optimized>(xi, knot_indices, coeff_indices)
5301 for (
short_t k = 0; k < hessG.slices(); ++k) {
5302 hess_component -= ijacG(component, k) * hessG.slice(k);
5305 auto jacInv = G.template jac<memory_optimized>(xi, knot_indices_G,
5308 auto hessu_component = jacInv.tr() * hess_component * jacInv;
5310 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5311 for (
short_t j = 0; j < BSplineCore::parDim_; ++j)
5312 hessu.set(i, j, component, hessu_component(i, j));
5350 template <
bool memory_optimized = false>
5351 inline auto jac(
const torch::Tensor &xi)
const {
5352 if constexpr (BSplineCore::parDim_ == 0)
5354 torch::zeros_like(BSplineCore::coeffs_[0])};
5359 template <
bool memory_optimized = false>
5361 if constexpr (BSplineCore::parDim_ == 0)
5363 torch::zeros_like(BSplineCore::coeffs_[0])};
5365 return jac<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5400 template <
bool memory_optimized = false>
5404 if constexpr (BSplineCore::parDim_ == 0)
5406 torch::zeros_like(BSplineCore::coeffs_[0])};
5408 return jac<memory_optimized>(
5410 BSplineCore::template find_coeff_indices<memory_optimized>(
5453 template <
bool memory_optimized = false>
5456 const torch::Tensor &coeff_indices)
const {
5458 if constexpr (BSplineCore::parDim_ == 0)
5460 torch::zeros_like(BSplineCore::coeffs_[0])};
5464 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5465 assert(xi[i].sizes() == knot_indices[i].sizes());
5466 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5467 assert(xi[0].sizes() == xi[i].sizes());
5470 auto jac_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5472 BSplineCore::geoDim_>{
5473 BSplineCore::template eval<
5475 memory_optimized>(xi, knot_indices, coeff_indices)...}
5479 return jac_(std::make_index_sequence<BSplineCore::parDim_>{});
5504 template <
bool memory_optimized = false,
typename Geometry>
5505 inline auto ijac(
const Geometry &G,
const torch::Tensor &xi)
const {
5506 if constexpr (BSplineCore::parDim_ == 0)
5508 torch::zeros_like(BSplineCore::coeffs_[0])};
5513 template <
bool memory_optimized = false,
typename Geometry>
5514 inline auto ijac(
const Geometry &G,
5516 if constexpr (BSplineCore::parDim_ == 0)
5518 torch::zeros_like(BSplineCore::coeffs_[0])};
5520 return ijac<memory_optimized, Geometry>(
5521 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5549 template <
bool memory_optimized = false,
typename Geometry>
5554 if constexpr (BSplineCore::parDim_ == 0)
5556 torch::zeros_like(BSplineCore::coeffs_[0])};
5558 return ijac<memory_optimized, Geometry>(
5559 G, xi, knot_indices,
5560 BSplineCore::template find_coeff_indices<memory_optimized>(
5563 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5596 template <
bool memory_optimized = false,
typename Geometry>
5597 inline auto ijac(
const Geometry &G,
5600 const torch::Tensor &coeff_indices,
5602 const torch::Tensor &coeff_indices_G)
const {
5603 if constexpr (BSplineCore::parDim_ == 0)
5605 torch::zeros_like(BSplineCore::coeffs_[0])};
5607 return jac<memory_optimized>(xi, knot_indices, coeff_indices) *
5608 G.template jac<memory_optimized>(xi, knot_indices_G,
5633 template <
bool memory_optimized = false>
5634 inline auto lapl(
const torch::Tensor &xi)
const {
5635 if constexpr (BSplineCore::parDim_ == 0)
5637 torch::zeros_like(BSplineCore::coeffs_[0])};
5642 template <
bool memory_optimized = false>
5644 if constexpr (BSplineCore::parDim_ == 0)
5646 torch::zeros_like(BSplineCore::coeffs_[0])};
5648 return lapl<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5673 template <
bool memory_optimized = false>
5677 if constexpr (BSplineCore::parDim_ == 0)
5679 torch::zeros_like(BSplineCore::coeffs_[0])};
5681 return lapl<memory_optimized>(
5683 BSplineCore::template find_coeff_indices<memory_optimized>(
5711 template <
bool memory_optimized = false>
5714 const torch::Tensor &coeff_indices)
const {
5716 if constexpr (BSplineCore::parDim_ == 0)
5718 torch::zeros_like(BSplineCore::coeffs_[0])};
5722 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5723 assert(xi[i].sizes() == knot_indices[i].sizes());
5724 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5725 assert(xi[0].sizes() == xi[i].sizes());
5728 auto lapl_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5730 (BSplineCore::template eval<
5732 memory_optimized>(xi, knot_indices, coeff_indices) +
5737 return lapl_(std::make_index_sequence<BSplineCore::parDim_>{});
5769 template <
bool memory_optimized = false,
typename Geometry>
5770 auto ilapl(
const Geometry &G,
const torch::Tensor &xi)
const {
5771 if constexpr (BSplineCore::parDim_ == 0)
5773 torch::zeros_like(BSplineCore::coeffs_[0])};
5778 template <
bool memory_optimized = false,
typename Geometry>
5781 if constexpr (BSplineCore::parDim_ == 0)
5783 torch::zeros_like(BSplineCore::coeffs_[0])};
5785 return ilapl<memory_optimized, Geometry>(
5786 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5820 template <
bool memory_optimized = false,
typename Geometry>
5825 if constexpr (BSplineCore::parDim_ == 0)
5827 torch::zeros_like(BSplineCore::coeffs_[0])};
5829 return ilapl<memory_optimized, Geometry>(
5830 G, xi, knot_indices,
5831 BSplineCore::template find_coeff_indices<memory_optimized>(
5834 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5874 template <
bool memory_optimized = false,
typename Geometry>
5878 const torch::Tensor &coeff_indices,
5880 const torch::Tensor &coeff_indices_G)
const {
5882 if constexpr (BSplineCore::parDim_ == 0)
5884 torch::zeros_like(BSplineCore::coeffs_[0])};
5888 hess<memory_optimized>(xi, knot_indices, coeff_indices).slice(0);
5892 igrad<memory_optimized>(G, xi, knot_indices, coeff_indices,
5893 knot_indices_G, coeff_indices_G);
5894 auto hessG = G.template hess<memory_optimized>(xi, knot_indices_G,
5896 assert(igradG.cols() == hessG.slices());
5897 for (
short_t k = 0; k < hessG.slices(); ++k)
5898 hessu -= igradG(0, k) * hessG.slice(k);
5902 G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G)
5905 return (jacInv.tr() * hessu * jacInv).trace();
5914#ifdef IGANET_WITH_MATPLOT
5915 template <
typename Backend = matplot::backend::gnuplot>
5917 template <
typename Backend =
void>
5919 inline auto plot(
const nlohmann::json &json = {})
const {
5920 return plot<Backend>(*
this, json);
5930#ifdef IGANET_WITH_MATPLOT
5931 template <
typename Backend = matplot::backend::gnuplot>
5933 template <
typename Backend =
void>
5936 const nlohmann::json &json = {})
const {
5938 return plot<Backend>(*
this, xi, json);
5948#ifdef IGANET_WITH_MATPLOT
5949 template <
typename Backend = matplot::backend::gnuplot>
5951 template <
typename Backend =
void>
5955 const nlohmann::json &json = {})
const {
5957 return plot<Backend>(*
this, xi, json);
5967#ifdef IGANET_WITH_MATPLOT
5968 template <
typename Backend = matplot::backend::gnuplot,
5969 typename BSplineCoreColor>
5971 template <
typename Backend =
void,
typename BSplineCoreColor>
5974 const nlohmann::json &json = {})
const {
5975#ifdef IGANET_WITH_MATPLOT
5976 static_assert(BSplineCore::parDim() == BSplineCoreColor::parDim(),
5977 "Parametric dimensions must match");
5979 if ((
void *)
this != (
void *)&color && BSplineCoreColor::geoDim() > 1)
5980 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
5982 if constexpr (BSplineCore::parDim() == 1 && BSplineCore::geoDim() == 1) {
5988 int64_t res0 = BSplineCore::ncoeffs(0);
5989 if (json.contains(
"res0"))
5990 res0 = json[
"res0"].get<int64_t>();
5993 auto f = matplot::figure<Backend>(
false);
5994 f->backend()->run_command(
"unset warnings");
5996 auto ax = f->current_axes();
6000 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6003 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6004 Coords(0), torch::kCPU);
6005 auto XAccessor = std::get<1>(Coords_cpu);
6007 auto [Coords_cpu, XAccessor] =
6008 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6009 Coords(0), torch::kCPU);
6012 matplot::vector_1d Xfine(res0, 0.0);
6013 matplot::vector_1d Yfine(res0, 0.0);
6015#pragma omp parallel for simd
6016 for (int64_t i = 0; i < res0; ++i)
6017 Xfine[i] = XAccessor[i];
6020 if ((
void *)
this != (
void *)&color) {
6021 if constexpr (BSplineCoreColor::geoDim_ == 1) {
6025 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6029 1>(Color(0), torch::kCPU);
6030 auto CAccessor = std::get<1>(Color_cpu);
6032 auto [Color_cpu, CAccessor] =
6034 1>(Color(0), torch::kCPU);
6037 matplot::vector_1d Cfine(res0, 0.0);
6039#pragma omp parallel for simd
6040 for (int64_t i = 0; i < res0; ++i)
6041 Cfine[i] = CAccessor[i];
6043 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
6044 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
6046 auto Cmap = matplot::colormap();
6048 auto a = Cmap.size() / (Cmax - Cmin);
6052 ax->hold(matplot::on);
6053 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
6054 ax->plot({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]})
6056 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
6057 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
6058 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
6059 ax->hold(matplot::off);
6060 matplot::colorbar(ax);
6062 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6065 ax->plot(Xfine, Yfine,
"b-")->line_width(2);
6069 if (json.contains(
"cnet"))
6070 cnet = json[
"cnet"].get<
bool>();
6076 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6077 BSplineCore::coeffs(0), torch::kCPU);
6078 auto xAccessor = std::get<1>(coeffs_cpu);
6080 auto [coeffs_cpu, xAccessor] =
6081 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6082 BSplineCore::coeffs(0), torch::kCPU);
6084 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6085 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6087#pragma omp parallel for simd
6088 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6089 X[i] = xAccessor[i];
6093 ax->hold(matplot::on);
6094 ax->plot(X, Y,
".k-")->line_width(1);
6095 ax->hold(matplot::off);
6099 if (json.contains(
"title"))
6100 ax->title(json[
"title"].get<std::string>());
6102 ax->title(
"BSpline: [0,1] -> R");
6105 if (json.contains(
"xlabel"))
6106 ax->xlabel(json[
"xlabel"].get<std::string>());
6111 if (json.contains(
"ylabel"))
6112 ax->ylabel(json[
"ylabel"].get<std::string>());
6119 else if constexpr (BSplineCore::parDim_ == 1 && BSplineCore::geoDim_ == 2) {
6125 int64_t res0 = BSplineCore::ncoeffs(0);
6126 if (json.contains(
"res0"))
6127 res0 = json[
"res0"].get<int64_t>();
6130 auto f = matplot::figure<Backend>(
false);
6131 f->backend()->run_command(
"unset warnings");
6133 auto ax = f->current_axes();
6137 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6140 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6141 Coords, torch::kCPU);
6142 auto XAccessor = std::get<1>(Coords_cpu)[0];
6143 auto YAccessor = std::get<1>(Coords_cpu)[1];
6145 auto [Coords0_cpu, XAccessor] =
6146 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6147 Coords(0), torch::kCPU);
6148 auto [Coords1_cpu, YAccessor] =
6149 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6150 Coords(1), torch::kCPU);
6153 matplot::vector_1d Xfine(res0, 0.0);
6154 matplot::vector_1d Yfine(res0, 0.0);
6156#pragma omp parallel for simd
6157 for (int64_t i = 0; i < res0; ++i) {
6158 Xfine[i] = XAccessor[i];
6159 Yfine[i] = YAccessor[i];
6163 if ((
void *)
this != (
void *)&color) {
6164 if constexpr (BSplineCoreColor::geoDim() == 1) {
6168 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6172 1>(Color(0), torch::kCPU);
6173 auto CAccessor = std::get<1>(Color_cpu);
6175 auto [Color_cpu, CAccessor] =
6177 1>(Color(0), torch::kCPU);
6180 matplot::vector_1d Cfine(res0, 0.0);
6182#pragma omp parallel for simd
6183 for (int64_t i = 0; i < res0; ++i) {
6184 Cfine[i] = CAccessor[i];
6187 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
6188 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
6190 auto Cmap = matplot::colormap();
6192 auto a = Cmap.size() / (Cmax - Cmin);
6196 ax->hold(matplot::on);
6197 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
6198 ax->plot({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]})
6200 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
6201 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
6202 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
6203 ax->hold(matplot::off);
6204 matplot::colorbar(ax);
6206 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6209 ax->plot(Xfine, Yfine,
"b-")->line_width(2);
6213 if (json.contains(
"cnet"))
6214 cnet = json[
"cnet"].get<
bool>();
6220 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6221 BSplineCore::coeffs(), torch::kCPU);
6222 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6223 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6225 auto [coeffs0_cpu, xAccessor] =
6226 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6227 BSplineCore::coeffs(0), torch::kCPU);
6228 auto [coeffs1_cpu, yAccessor] =
6229 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6230 BSplineCore::coeffs(1), torch::kCPU);
6233 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6234 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6236#pragma omp parallel for simd
6237 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6238 X[i] = xAccessor[i];
6239 Y[i] = yAccessor[i];
6243 ax->hold(matplot::on);
6244 ax->plot(X, Y,
".k-")->line_width(1);
6245 ax->hold(matplot::off);
6249 if (json.contains(
"title"))
6250 ax->title(json[
"title"].get<std::string>());
6252 ax->title(
"BSpline: [0,1] -> R^2");
6255 if (json.contains(
"xlabel"))
6256 ax->xlabel(json[
"xlabel"].get<std::string>());
6261 if (json.contains(
"ylabel"))
6262 ax->ylabel(json[
"ylabel"].get<std::string>());
6269 else if constexpr (BSplineCore::parDim() == 1 &&
6270 BSplineCore::geoDim() == 3) {
6276 int64_t res0 = BSplineCore::ncoeffs(0);
6277 if (json.contains(
"res0"))
6278 res0 = json[
"res0"].get<int64_t>();
6281 auto f = matplot::figure<Backend>(
false);
6282 f->backend()->run_command(
"unset warnings");
6284 auto ax = f->current_axes();
6287 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6290 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6291 Coords, torch::kCPU);
6292 auto XAccessor = std::get<1>(Coords_cpu)[0];
6293 auto YAccessor = std::get<1>(Coords_cpu)[1];
6294 auto ZAccessor = std::get<1>(Coords_cpu)[2];
6296 auto [Coords0_cpu, XAccessor] =
6297 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6298 Coords(0), torch::kCPU);
6299 auto [Coords1_cpu, YAccessor] =
6300 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6301 Coords(1), torch::kCPU);
6302 auto [Coords2_cpu, ZAccessor] =
6303 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6304 Coords(2), torch::kCPU);
6308 matplot::vector_1d Xfine(res0, 0.0);
6309 matplot::vector_1d Yfine(res0, 0.0);
6310 matplot::vector_1d Zfine(res0, 0.0);
6312#pragma omp parallel for simd
6313 for (int64_t i = 0; i < res0; ++i) {
6314 Xfine[i] = XAccessor[i];
6315 Yfine[i] = YAccessor[i];
6316 Zfine[i] = ZAccessor[i];
6320 if ((
void *)
this != (
void *)&color) {
6321 if constexpr (BSplineCoreColor::geoDim() == 1) {
6324 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
6328 1>(Color(0), torch::kCPU);
6329 auto CAccessor = std::get<1>(Color_cpu);
6331 auto [Color_cpu, CAccessor] =
6333 1>(Color(0), torch::kCPU);
6337 matplot::vector_1d Cfine(matplot::vector_1d(res0, 0.0));
6339#pragma omp parallel for simd
6340 for (int64_t i = 0; i < res0; ++i) {
6341 Cfine[i] = CAccessor[i];
6344 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
6345 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
6347 auto Cmap = matplot::colormap();
6349 auto a = Cmap.size() / (Cmax - Cmin);
6353 ax->hold(matplot::on);
6354 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
6355 ax->plot3({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]},
6356 {Zfine[i], Zfine[i + 1]})
6358 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
6359 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
6360 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
6361 ax->hold(matplot::off);
6362 matplot::colorbar(ax);
6364 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6367 ax->plot3(Xfine, Yfine, Zfine,
"b-")->line_width(2);
6371 if (json.contains(
"cnet"))
6372 cnet = json[
"cnet"].get<
bool>();
6378 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6379 BSplineCore::coeffs(), torch::kCPU);
6380 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6381 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6382 auto zAccessor = std::get<1>(coeffs_cpu)[2];
6384 auto [coeffs0_cpu, xAccessor] =
6385 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6386 BSplineCore::coeffs(0), torch::kCPU);
6387 auto [coeffs1_cpu, yAccessor] =
6388 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6389 BSplineCore::coeffs(1), torch::kCPU);
6390 auto [coeffs2_cpu, zAccessor] =
6391 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6392 BSplineCore::coeffs(2), torch::kCPU);
6395 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6396 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6397 matplot::vector_1d Z(BSplineCore::ncoeffs(0), 0.0);
6399#pragma omp parallel for simd
6400 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6401 X[i] = xAccessor[i];
6402 Y[i] = yAccessor[i];
6403 Z[i] = zAccessor[i];
6407 ax->hold(matplot::on);
6408 ax->plot3(X, Y, Z,
".k-")->line_width(1);
6409 ax->hold(matplot::off);
6413 if (json.contains(
"title"))
6414 ax->title(json[
"title"].get<std::string>());
6416 ax->title(
"BSpline: [0,1] -> R^3");
6419 if (json.contains(
"xlabel"))
6420 ax->xlabel(json[
"xlabel"].get<std::string>());
6425 if (json.contains(
"ylabel"))
6426 ax->ylabel(json[
"ylabel"].get<std::string>());
6431 if (json.contains(
"zlabel"))
6432 ax->zlabel(json[
"zlabel"].get<std::string>());
6439 else if constexpr (BSplineCore::parDim() == 2 &&
6440 BSplineCore::geoDim() == 2) {
6446 int64_t res0 = BSplineCore::ncoeffs(0);
6447 int64_t res1 = BSplineCore::ncoeffs(1);
6448 if (json.contains(
"res0"))
6449 res0 = json[
"res0"].get<int64_t>();
6450 if (json.contains(
"res1"))
6451 res1 = json[
"res1"].get<int64_t>();
6454 auto f = matplot::figure<Backend>(
false);
6455 f->backend()->run_command(
"unset warnings");
6457 auto ax = f->current_axes();
6460 utils::TensorArray<2> meshgrid = utils::to_array<2>(
6461 torch::meshgrid({torch::linspace(0, 1, res0, BSplineCore::options_),
6462 torch::linspace(0, 1, res1, BSplineCore::options_)},
6464 auto Coords = BSplineCore::eval(meshgrid);
6467 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6468 Coords, torch::kCPU);
6469 auto XAccessor = std::get<1>(Coords_cpu)[0];
6470 auto YAccessor = std::get<1>(Coords_cpu)[1];
6472 auto [Coords0_cpu, XAccessor] =
6473 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6474 Coords(0), torch::kCPU);
6475 auto [Coords1_cpu, YAccessor] =
6476 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6477 Coords(1), torch::kCPU);
6480 matplot::vector_2d Xfine(res1, matplot::vector_1d(res0, 0.0));
6481 matplot::vector_2d Yfine(res1, matplot::vector_1d(res0, 0.0));
6482 matplot::vector_2d Zfine(res1, matplot::vector_1d(res0, 0.0));
6484#pragma omp parallel for simd collapse(2)
6485 for (int64_t i = 0; i < res0; ++i)
6486 for (int64_t j = 0; j < res1; ++j) {
6487 Xfine[j][i] = XAccessor[j][i];
6488 Yfine[j][i] = YAccessor[j][i];
6492 if ((
void *)
this != (
void *)&color) {
6493 if constexpr (BSplineCoreColor::geoDim() == 1) {
6496 auto Color = color.eval(meshgrid);
6499 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6500 2>(Color, torch::kCPU);
6501 auto CAccessor = std::get<1>(Color_cpu)[0];
6503 auto [Color0_cpu, CAccessor] =
6504 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6505 2>(Color(0), torch::kCPU);
6508 matplot::vector_2d Cfine(res1, matplot::vector_1d(res0, 0.0));
6510#pragma omp parallel for simd collapse(2)
6511 for (int64_t i = 0; i < res0; ++i)
6512 for (int64_t j = 0; j < res1; ++j)
6513 Cfine[j][i] = CAccessor[j][i];
6517 ax->mesh(Xfine, Yfine, Cfine)->hidden_3d(
false);
6518 matplot::colorbar(ax);
6520 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6524 matplot::colormap(std::vector<std::vector<double>>{{ 0.0, 0.0, 1.0 }});
6525 ax->mesh(Xfine, Yfine, Zfine)->hidden_3d(
false).line_width(2);
6529 if (json.contains(
"cnet"))
6530 cnet = json[
"cnet"].get<
bool>();
6536 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6537 BSplineCore::coeffs(), torch::kCPU);
6538 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6539 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6541 auto [coeffs0_cpu, xAccessor] =
6542 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6543 BSplineCore::coeffs(0), torch::kCPU);
6544 auto [coeffs1_cpu, yAccessor] =
6545 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6546 BSplineCore::coeffs(1), torch::kCPU);
6549 matplot::vector_2d X(BSplineCore::ncoeffs(1),
6550 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6551 matplot::vector_2d Y(BSplineCore::ncoeffs(1),
6552 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6553 matplot::vector_2d Z(BSplineCore::ncoeffs(1),
6554 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6556#pragma omp parallel for simd collapse(2)
6557 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i)
6558 for (int64_t j = 0; j < BSplineCore::ncoeffs(1); ++j) {
6559 X[j][i] = xAccessor[j * BSplineCore::ncoeffs(0) + i];
6560 Y[j][i] = yAccessor[j * BSplineCore::ncoeffs(0) + i];
6564 ax->hold(matplot::on);
6566 ->palette_map_at_surface(
true)
6569 for (std::size_t i = 0; i < X.size(); ++i)
6570 ax->scatter3(X[i], Y[i], Z[i],
"k.");
6571 ax->hold(matplot::off);
6575 if (json.contains(
"title"))
6576 ax->title(json[
"title"].get<std::string>());
6578 ax->title(
"BSpline: [0,1]^2 -> R^2");
6581 if (json.contains(
"xlabel"))
6582 ax->xlabel(json[
"xlabel"].get<std::string>());
6587 if (json.contains(
"ylabel"))
6588 ax->ylabel(json[
"ylabel"].get<std::string>());
6593 if (json.contains(
"zlabel"))
6594 ax->zlabel(json[
"zlabel"].get<std::string>());
6601 else if constexpr (BSplineCore::parDim() == 2 &&
6602 BSplineCore::geoDim() == 3) {
6608 int64_t res0 = BSplineCore::ncoeffs(0);
6609 int64_t res1 = BSplineCore::ncoeffs(1);
6610 if (json.contains(
"res0"))
6611 res0 = json[
"res0"].get<int64_t>();
6612 if (json.contains(
"res1"))
6613 res1 = json[
"res1"].get<int64_t>();
6616 auto f = matplot::figure<Backend>(
false);
6617 f->backend()->run_command(
"unset warnings");
6619 auto ax = f->current_axes();
6622 utils::TensorArray<2> meshgrid = utils::to_array<2>(
6623 torch::meshgrid({torch::linspace(0, 1, res0, BSplineCore::options_),
6624 torch::linspace(0, 1, res1, BSplineCore::options_)},
6626 auto Coords = BSplineCore::eval(meshgrid);
6629 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6630 Coords, torch::kCPU);
6631 auto XAccessor = std::get<1>(Coords_cpu)[0];
6632 auto YAccessor = std::get<1>(Coords_cpu)[1];
6633 auto ZAccessor = std::get<1>(Coords_cpu)[2];
6635 auto [Coords0_cpu, XAccessor] =
6636 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6637 Coords(0), torch::kCPU);
6638 auto [Coords1_cpu, YAccessor] =
6639 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6640 Coords(1), torch::kCPU);
6641 auto [Coords2_cpu, ZAccessor] =
6642 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6643 Coords(2), torch::kCPU);
6646 matplot::vector_2d Xfine(res1, matplot::vector_1d(res0, 0.0));
6647 matplot::vector_2d Yfine(res1, matplot::vector_1d(res0, 0.0));
6648 matplot::vector_2d Zfine(res1, matplot::vector_1d(res0, 0.0));
6650#pragma omp parallel for simd collapse(2)
6651 for (int64_t i = 0; i < res0; ++i)
6652 for (int64_t j = 0; j < res1; ++j) {
6653 Xfine[j][i] = XAccessor[j][i];
6654 Yfine[j][i] = YAccessor[j][i];
6655 Zfine[j][i] = ZAccessor[j][i];
6659 if ((
void *)
this != (
void *)&color) {
6660 if constexpr (BSplineCoreColor::geoDim() == 1) {
6663 auto Color = color.eval(meshgrid);
6666 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6667 2>(Color, torch::kCPU);
6668 auto CAccessor = std::get<1>(Color_cpu)[0];
6670 auto [Color_cpu, CAccessor] =
6671 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6672 2>(Color(0), torch::kCPU);
6675 matplot::vector_2d Cfine(res1, matplot::vector_1d(res0, 0.0));
6677#pragma omp parallel for simd collapse(2)
6678 for (int64_t i = 0; i < res0; ++i)
6679 for (int64_t j = 0; j < res1; ++j) {
6680 Cfine[j][i] = CAccessor[j][i];
6684 ax->mesh(Xfine, Yfine, Zfine, Cfine)->hidden_3d(
false);
6685 matplot::colorbar(ax);
6687 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6690 matplot::colormap(std::vector<std::vector<double>>{{ 0.0, 0.0, 1.0 }});
6691 ax->mesh(Xfine, Yfine, Zfine)->hidden_3d(
false).line_width(2);
6695 if (json.contains(
"cnet"))
6696 cnet = json[
"cnet"].get<
bool>();
6702 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6703 BSplineCore::coeffs(), torch::kCPU);
6704 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6705 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6706 auto zAccessor = std::get<1>(coeffs_cpu)[2];
6708 auto [coeffs0_cpu, xAccessor] =
6709 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6710 BSplineCore::coeffs(0), torch::kCPU);
6711 auto [coeffs1_cpu, yAccessor] =
6712 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6713 BSplineCore::coeffs(1), torch::kCPU);
6714 auto [coeffs2_cpu, zAccessor] =
6715 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6716 BSplineCore::coeffs(2), torch::kCPU);
6719 matplot::vector_2d X(BSplineCore::ncoeffs(1),
6720 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6721 matplot::vector_2d Y(BSplineCore::ncoeffs(1),
6722 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6723 matplot::vector_2d Z(BSplineCore::ncoeffs(1),
6724 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6726#pragma omp parallel for simd collapse(2)
6727 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i)
6728 for (int64_t j = 0; j < BSplineCore::ncoeffs(1); ++j) {
6729 X[j][i] = xAccessor[j * BSplineCore::ncoeffs(0) + i];
6730 Y[j][i] = yAccessor[j * BSplineCore::ncoeffs(0) + i];
6731 Z[j][i] = zAccessor[j * BSplineCore::ncoeffs(0) + i];
6735 ax->hold(matplot::on);
6737 ->palette_map_at_surface(
true)
6740 for (std::size_t i = 0; i < X.size(); ++i)
6741 ax->scatter3(X[i], Y[i], Z[i],
"k.");
6742 ax->hold(matplot::off);
6746 if (json.contains(
"title"))
6747 ax->title(json[
"title"].get<std::string>());
6749 ax->title(
"BSpline: [0,1]^2 -> R^3");
6752 if (json.contains(
"xlabel"))
6753 ax->xlabel(json[
"xlabel"].get<std::string>());
6758 if (json.contains(
"ylabel"))
6759 ax->ylabel(json[
"ylabel"].get<std::string>());
6764 if (json.contains(
"zlabel"))
6765 ax->zlabel(json[
"zlabel"].get<std::string>());
6773 throw std::runtime_error(
6774 "Unsupported combination of parametric/geometric dimensions");
6776 throw std::runtime_error(
6777 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
6791#ifdef IGANET_WITH_MATPLOT
6792 template <
typename Backend = matplot::backend::gnuplot,
6793 typename BSplineCoreColor>
6795 template <
typename Backend =
void,
typename BSplineCoreColor>
6799 const nlohmann::json &json = {})
const {
6801#ifdef IGANET_WITH_MATPLOT
6802 auto f = plot<Backend>(color, json);
6803 auto ax = f->current_axes();
6807 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6809 auto xiAccessor = std::get<1>(xi_cpu);
6811 auto [xi_cpu, xiAccessor] =
6812 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6816 if constexpr (BSplineCore::parDim_ == 1) {
6817 matplot::vector_1d X(xi[0].size(0), 0.0);
6818 matplot::vector_1d Y(xi[0].size(0), 0.0);
6820#pragma omp parallel for simd
6821 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6822 X[i] = xiAccessor[0][i];
6825 ax->hold(matplot::on);
6826 ax->scatter(X, Y,
".");
6827 ax->hold(matplot::off);
6828 }
else if constexpr (BSplineCore::parDim_ == 2) {
6829 matplot::vector_1d X(xi[0].size(0), 0.0);
6830 matplot::vector_1d Y(xi[0].size(0), 0.0);
6831 matplot::vector_1d Z(xi[0].size(0), 0.0);
6833#pragma omp parallel for simd
6834 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6835 X[i] = xiAccessor[0][i];
6836 Y[i] = xiAccessor[1][i];
6839 ax->hold(matplot::on);
6840 ax->scatter3(X, Y, Z,
".");
6841 ax->hold(matplot::off);
6842 }
else if constexpr (BSplineCore::parDim_ == 3) {
6843 matplot::vector_1d X(xi[0].size(0), 0.0);
6844 matplot::vector_1d Y(xi[0].size(0), 0.0);
6845 matplot::vector_1d Z(xi[0].size(0), 0.0);
6847#pragma omp parallel for simd
6848 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6849 X[i] = xiAccessor[0][i];
6850 Y[i] = xiAccessor[1][i];
6851 Z[i] = xiAccessor[2][i];
6854 ax->hold(matplot::on);
6855 ax->scatter3(X, Y, Z,
".");
6856 ax->hold(matplot::off);
6858 throw std::runtime_error(
"Invalid parametric dimension");
6862 throw std::runtime_error(
6863 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
6877#ifdef IGANET_WITH_MATPLOT
6878 template <
typename Backend = matplot::backend::gnuplot,
6879 typename BSplineCoreColor>
6881 template <
typename Backend =
void,
typename BSplineCoreColor>
6886 const nlohmann::json &json = {})
const {
6888#ifdef IGANET_WITH_MATPLOT
6889 auto f = plot<Backend>(color, json);
6890 auto ax = f->current_axes();
6892 for (
const auto &xi : xi) {
6895 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6897 auto xiAccessor = std::get<1>(xi_cpu);
6899 auto [xi_cpu, xiAccessor] =
6900 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6904 if constexpr (BSplineCore::parDim_ == 1) {
6905 matplot::vector_1d X(xi[0].size(0), 0.0);
6906 matplot::vector_1d Y(xi[0].size(0), 0.0);
6908#pragma omp parallel for simd
6909 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6910 X[i] = xiAccessor[0][i];
6913 ax->hold(matplot::on);
6914 ax->scatter(X, Y,
".");
6915 ax->hold(matplot::off);
6916 }
else if constexpr (BSplineCore::parDim_ == 2) {
6917 matplot::vector_1d X(xi[0].size(0), 0.0);
6918 matplot::vector_1d Y(xi[0].size(0), 0.0);
6919 matplot::vector_1d Z(xi[0].size(0), 0.0);
6921#pragma omp parallel for simd
6922 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6923 X[i] = xiAccessor[0][i];
6924 Y[i] = xiAccessor[1][i];
6927 ax->hold(matplot::on);
6928 ax->scatter3(X, Y, Z,
".");
6929 ax->hold(matplot::off);
6930 }
else if constexpr (BSplineCore::parDim_ == 3) {
6931 matplot::vector_1d X(xi[0].size(0), 0.0);
6932 matplot::vector_1d Y(xi[0].size(0), 0.0);
6933 matplot::vector_1d Z(xi[0].size(0), 0.0);
6935#pragma omp parallel for simd
6936 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6937 X[i] = xiAccessor[0][i];
6938 Y[i] = xiAccessor[1][i];
6939 Z[i] = xiAccessor[2][i];
6942 ax->hold(matplot::on);
6943 ax->scatter3(X, Y, Z,
".");
6944 ax->hold(matplot::off);
6947 throw std::runtime_error(
"Invalid parametric dimension");
6951 throw std::runtime_error(
6952 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
6958 os << name() <<
"(\nparDim = " << BSplineCore::parDim()
6959 <<
", geoDim = " << BSplineCore::geoDim() <<
", degrees = ";
6961 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
6962 os << BSplineCore::degree(i) <<
"x";
6963 if (BSplineCore::parDim() > 0)
6964 os << BSplineCore::degree(BSplineCore::parDim() - 1);
6969 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
6970 os << BSplineCore::nknots(i) <<
"x";
6971 if (BSplineCore::parDim() > 0)
6972 os << BSplineCore::nknots(BSplineCore::parDim() - 1);
6976 os <<
", coeffs = ";
6977 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
6978 os << BSplineCore::ncoeffs(i) <<
"x";
6979 if (BSplineCore::parDim() > 0)
6980 os << BSplineCore::ncoeffs(BSplineCore::parDim() - 1);
6984 os <<
", options = "
6985 <<
static_cast<torch::TensorOptions
>(BSplineCore::options_);
6989 for (
const torch::Tensor &knots : BSplineCore::knots()) {
6990 os << (knots.is_view() ?
"view/" :
"owns/");
6991 os << (knots.is_contiguous() ?
"cont " :
"non-cont ");
6993 if (BSplineCore::parDim() > 0)
6994 os <<
"] = " << BSplineCore::knots();
6998 os <<
"\ncoeffs [ ";
6999 for (
const torch::Tensor &coeffs : BSplineCore::coeffs()) {
7000 os << (coeffs.is_view() ?
"view/" :
"owns/");
7001 os << (coeffs.is_contiguous() ?
"cont " :
"non-cont ");
7003 if (BSplineCore::ncumcoeffs() > 0)
7004 os <<
"] = " << BSplineCore::coeffs_view();
7023 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7024 result.coeffs(i) += other.coeffs(i);
7041 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7042 result.coeffs(i) -= other.coeffs(i);
7053 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7054 result.coeffs(i) *= s;
7062 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
7067 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7068 result.coeffs(i) *= v[i];
7079 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7080 result.coeffs(i) /= s;
7088 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
7093 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7094 result.coeffs(i) /= v[i];
7107 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7108 BSplineCore::coeffs(i) += other.coeffs(i);
7121 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7122 BSplineCore::coeffs(i) -= other.coeffs(i);
7130 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7131 BSplineCore::coeffs(i) *= s;
7138 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
7140 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7141 BSplineCore::coeffs(i) *= v[i];
7149 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7150 BSplineCore::coeffs(i) /= s;
7157 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
7159 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
7160 BSplineCore::coeffs(i) /= v[i];
7167template <
typename real_t, short_t GeoDim, short_t... Degrees>
7173inline std::ostream &
7174operator<<(std::ostream &os,
7181template <
typename real_t, short_t GeoDim, short_t... Degrees>
7187inline std::ostream &
7188operator<<(std::ostream &os,
Compile-time block tensor.
B-spline (common high-level functionality)
Definition bspline.hpp:3614
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:5551
auto rotate(std::array< typename BSplineCore::value_type, 3 > angle) const
Rotates the B-spline object by three angles in 3d.
Definition bspline.hpp:4013
BSplineCommon & operator*=(BSplineCore::value_type s)
Scales the coefficients by a scalar.
Definition bspline.hpp:7128
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:4366
auto ihess(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &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:5182
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:4247
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:4176
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:5276
auto diff_(const BSplineCommon &other, int dim=-1)
Computes the difference between two compatible B-spline objects in-place.
Definition bspline.hpp:3879
auto ilapl(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &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:5779
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:5173
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:4018
BSplineCommon & operator*=(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the coefficients by a vector.
Definition bspline.hpp:7137
auto rotate_(BSplineCore::value_type angle)
Rotates the B-spline object by an angle in 2d in-place.
Definition bspline.hpp:3997
auto idiv(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Returns a block-tensor with the divergence of the B-spline object with respect to the physical variab...
Definition bspline.hpp:4597
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:4809
auto plot(const BSplineCommon< BSplineCoreColor > &color, const std::initializer_list< utils::TensorArray< BSplineCore::parDim_ > > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:6883
auto div(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Returns a block-tensor with the divergence of the B-spline object with respect to the parametric vari...
Definition bspline.hpp:4464
void pretty_print(std::ostream &os) const noexcept override
Returns a string representation of the BSplineCommon object.
Definition bspline.hpp:6957
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:7061
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:4098
BSplineCommon & operator/=(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the coefficients by a vector.
Definition bspline.hpp:7156
auto translate_(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Translates the B-spline object by a vector in-place.
Definition bspline.hpp:3984
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:5712
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:5505
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:4413
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:5597
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:5402
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:5454
auto hess(const utils::TensorArray< BSplineCore::parDim_ > &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:5014
std::unique_ptr< BSplineCommon > uPtr
Unique pointer for BSplineCommon.
Definition bspline.hpp:3653
BSplineCommon(const BSplineCommon &other, bool clone)
Copy/clone constructor.
Definition bspline.hpp:3659
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:4635
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 >{})
Creates a new B-spline object as unique pointer.
Definition bspline.hpp:3698
auto lapl(const utils::TensorArray< BSplineCore::parDim_ > &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:5643
auto abs_diff_(const BSplineCommon &other, int dim=-1)
Computes the absolute difference between two compatible B-spline objects in-place.
Definition bspline.hpp:3917
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:5822
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 >{})
Creates a new B-spline object as shared pointer.
Definition bspline.hpp:3776
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:4719
auto diff(const BSplineCommon &other, int dim=-1) const
Computes the difference between two compatible B-spline objects.
Definition bspline.hpp:3869
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:4682
auto curl(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Returns a block-tensor with the curl of the B-spline object with respect to the parametric variables.
Definition bspline.hpp:4181
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:5005
auto igrad(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &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:4870
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 >{})
Creates a new B-spline object as unique pointer.
Definition bspline.hpp:3715
BSplineCommon operator*(BSplineCore::value_type s) const
Returns a new B-spline object whose coefficients are scaled by a scalar.
Definition bspline.hpp:7049
auto scale(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v) const
Scales the B-spline object by a vector.
Definition bspline.hpp:3965
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:4118
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:4953
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:4533
std::shared_ptr< BSplineCommon > Ptr
Shared pointer for BSplineCommon.
Definition bspline.hpp:3650
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:5112
auto plot(const BSplineCommon< BSplineCoreColor > &color, const utils::TensorArray< BSplineCore::parDim_ > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:6797
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 >{})
Creates a new B-spline object as unique pointer.
Definition bspline.hpp:3706
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 >{})
Creates a new B-spline object as shared pointer.
Definition bspline.hpp:3768
auto scale(BSplineCore::value_type s, int dim=-1) const
Scales the B-spline object by a scalar.
Definition bspline.hpp:3949
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:5061
auto plot(const utils::TensorArray< BSplineCore::parDim_ > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:5935
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 >{})
Creates a new B-spline object as unique pointer.
Definition bspline.hpp:3732
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:4212
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:3692
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:7075
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:3944
auto plot(const nlohmann::json &json={}) const
Definition bspline.hpp:5919
BSplineCommon(BSplineCommon &&other, utils::TensorArray< BSplineCore::geoDim_ > &&coeffs)
Move constructor with external coefficients.
Definition bspline.hpp:3682
BSplineCommon & operator+=(const BSplineCommon &other)
Adds the coefficients of another B-spline object.
Definition bspline.hpp:7105
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:5770
auto plot(const BSplineCommon< BSplineCoreColor > &color, const nlohmann::json &json={}) const
Definition bspline.hpp:5973
auto to() const
Returns a copy of the B-spline object with real_t type.
Definition bspline.hpp:3859
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:4078
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:7019
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:4321
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:5224
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:4588
auto ijac(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &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:5514
auto translate(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v) const
Translates the B-spline object by a vector.
Definition bspline.hpp:3978
auto boundingBox() const
Computes the bounding box of the B-spline object.
Definition bspline.hpp:4052
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 >{})
Creates a new B-spline object as shared pointer.
Definition bspline.hpp:3751
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:4456
auto icurl(const Geometry &G, const utils::TensorArray< BSplineCore::parDim_ > &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:4330
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:4768
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:4861
auto scale_(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the B-spline object by a vector in-place.
Definition bspline.hpp:3971
auto jac(const utils::TensorArray< BSplineCore::parDim_ > &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:5360
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 >{})
Creates a new B-spline object as shared pointer.
Definition bspline.hpp:3759
BSplineCommon & operator/=(BSplineCore::value_type s)
Scales the coefficients by a scalar.
Definition bspline.hpp:7147
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 >{})
Creates a new B-spline object as unique pointer.
Definition bspline.hpp:3723
auto rotate(BSplineCore::value_type angle) const
Rotates the B-spline object by an angle in 2d.
Definition bspline.hpp:3992
auto nv(const utils::TensorArray< BSplineCore::parDim_ > &xi) const
Returns a block-tensor with the outward pointing normal vector of the B-spline object.
Definition bspline.hpp:4083
BSplineCommon & operator-=(const BSplineCommon &other)
Substracts the coefficients of another B-spline object.
Definition bspline.hpp:7119
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:7037
BSplineCommon(const BSplineCommon &other, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false)
Copy constructor with external coefficients.
Definition bspline.hpp:3666
auto scale_(BSplineCore::value_type s, int dim=-1)
Scales the B-spline object by a scalar in-place.
Definition bspline.hpp:3954
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:5876
auto grad(const utils::TensorArray< BSplineCore::parDim_ > &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:4732
auto to(torch::Device device) const
Returns a copy of the B-spline object with settings from device.
Definition bspline.hpp:3842
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:4906
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:5634
auto clone() const
Returns a clone of the B-spline object.
Definition bspline.hpp:3807
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:7087
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:3745
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:5351
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 >{})
Creates a new B-spline object as shared pointer.
Definition bspline.hpp:3785
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:5675
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:4496
BSplineCommon & uniform_refine(int numRefine=1, int dim=-1)
Returns the B-spline object with uniformly refined knot and coefficient vectors.
Definition bspline.hpp:3801
auto plot(const std::initializer_list< utils::TensorArray< BSplineCore::parDim_ > > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:5953
auto abs_diff(const BSplineCommon &other, int dim=-1) const
Computes the absolute difference between two compatible B-spline objects.
Definition bspline.hpp:3907
auto to(Options< real_t > options) const
Returns a copy of the B-spline object with settings from options.
Definition bspline.hpp:3824
Abstract patch function base class.
Definition patch.hpp:50
The Options class handles the automated determination of dtype from the template argument and the sel...
Definition options.hpp:104
Spline base class.
Definition bspline.hpp:3577
SplineCore base class.
Definition bspline.hpp:126
Full qualified name descriptor.
Definition fqn.hpp:22
Concept to identify template parameters that are derived from iganet::SplineCore_.
Definition bspline.hpp:137
Concept to identify template parameters that are derived from iganet::Spline_.
Definition bspline.hpp:3582
Definition bspline.hpp:119
Definition bspline.hpp:112
Container utility functions.
Full qualified name utility functions.
Integer sequence utility function.
Integer power utility function.
Linear algebra utility functions.
auto kron(T0 &&t0, T1 &&t1)
Computes the Kronecker-product between two or more tensors.
Definition linalg.hpp:209
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:56
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:78
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:220
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:372
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:45
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:188
Forward declaration of BlockTensor.
Definition blocktensor.hpp:43
deriv
Enumerator for specifying the derivative of B-spline evaluation.
Definition bspline.hpp:72
constexpr auto operator+(deriv lhs, deriv rhs)
Adds two enumerators for specifying the derivative of B-spline evaluation.
Definition bspline.hpp:89
bool is_verbose(std::ostream &os)
Definition core.hpp:831
std::ostream & operator<<(std::ostream &os, const MemoryDebugger< id > &obj)
Print (as string) a memory debugger object.
Definition memory.hpp:125
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:102
init
Enumerator for specifying the initialization of B-spline coefficients.
Definition bspline.hpp:55
torch::serialize::InputArchive & operator>>(torch::serialize::InputArchive &archive, UniformBSplineCore< real_t, GeoDim, Degrees... > &obj)
De-serializes a B-spline object.
Definition bspline.hpp:3051
@ none
Definition boundary.hpp:38
short int short_t
Definition core.hpp:74
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:156
VSlice utility functions.