41#define GENERATE_EXPR_SEQ (curl)(div)(grad)(hess)(jac)(lapl)
47#define GENERATE_IEXPR_SEQ (icurl)(idiv)(igrad)(ihess)(ijac)(ilapl)
50using namespace literals;
51using utils::operator+;
204 public BSplinePatch<real_t, GeoDim, sizeof...(Degrees)> {
219 static constexpr const std::array<short_t, parDim_>
degrees_ = {Degrees...};
256 std::make_signed<short_t>::type degree_elevate = 0>
257 using derived_type = BSpline<real_t, GeoDim, (Degrees + degree_elevate)...>;
261 template <std::make_signed<short_t>::type degree_elevate = 0>
272 template <
typename other_t>
277 inline torch::Device
device() const noexcept
override {
287 inline torch::Dtype
dtype() const noexcept
override {
292 inline torch::Layout
layout() const noexcept
override {
312 inline static constexpr bool is_uniform() noexcept {
return true; }
403 throw std::runtime_error(
"Invalid number of coefficients");
410 .to(
options.requires_grad(
false))
411 .requires_grad_(
options.requires_grad());
444 template <
typename other_t>
457 .to(
options.requires_grad(
false))
458 .requires_grad_(
options.requires_grad());
464 .to(
options.requires_grad(
false))
465 .requires_grad_(
options.requires_grad());
481 inline static constexpr const std::array<short_t, parDim_> &
537 inline const std::array<int64_t, parDim_> &
nknots() const noexcept {
631 inline const std::array<int64_t, parDim_> &
ncoeffs() const noexcept {
650 template <std::size_t... Is>
651 inline torch::Tensor
as_tensor_(std::index_sequence<Is...>)
const noexcept {
652 return torch::cat({
coeffs_[Is]...});
659 inline torch::Tensor
as_tensor() const noexcept
override {
660 return as_tensor_(std::make_index_sequence<geoDim_>{});
667 template <std::size_t... Is>
670 const torch::Tensor &tensor)
noexcept {
686 return from_tensor_(std::make_index_sequence<geoDim_>{}, tensor);
729 auto idx_base = torch::arange(count,
options_.requires_grad(
false).template dtype<int64_t>()).unsqueeze(1);
732 auto offsets = torch::arange(1,
degrees_[j] + 1,
options_.requires_grad(
false).template dtype<int64_t>()).unsqueeze(0);
735 auto indices = idx_base + offset + offsets;
738 auto gathered =
knots_[j].index_select(0, indices.flatten()).view({count,
degrees_[j]});
741 auto greville_ = gathered.mean(1);
781 const torch::Tensor &coeff_indices, int64_t numeval,
782 torch::IntArrayRef sizes)
const override {
790 coeffs(i).index_select(0, coeff_indices).view({-1, numeval}))
797 const torch::Tensor &coeff_indices, int64_t numeval,
798 torch::IntArrayRef sizes)
const override {
813 return torch::matmul(
coeffs(i)
814 .index_select(0, coeff_indices)
815 .view({numeval, -1,
degrees_[0] + 1}),
816 basfunc[0].view({numeval, -1, 1}));
818 return torch::matmul(
819 (eval_(i, dim - 1)).view({numeval, -1,
degrees_[dim] + 1}),
820 basfunc[dim].view({numeval, -1, 1}));
825 result.set(i, (eval_(i,
parDim_ - 1)).view(sizes));
881 template <deriv deriv = deriv::func,
bool memory_optimized = false>
882 inline auto eval(
const torch::Tensor &xi)
const {
886 throw std::runtime_error(
"Invalid parametric dimension");
889 template <deriv deriv = deriv::func,
bool memory_optimized = false>
910 template <deriv deriv = deriv::func,
bool memory_optimized = false>
913 return eval<deriv, memory_optimized>(
914 xi, knot_indices, find_coeff_indices<memory_optimized>(knot_indices));
935 template <deriv deriv = deriv::func,
bool memory_optimized = false>
938 const torch::Tensor &coeff_indices)
const {
947 result.set(i, torch::zeros_like(
coeffs_[i]));
955 assert(xi[i].sizes() == knot_indices[i].sizes());
957 assert(xi[0].sizes() == xi[i].sizes());
959 if constexpr (memory_optimized) {
963 throw std::runtime_error(
964 "Memory-optimized evaluation requires single-valued coefficient");
968 eval_basfunc<deriv, memory_optimized>(xi, knot_indices);
975 return torch::matmul(
977 .index_select(0, coeff_indices)
978 .view({xi[0].numel(), -1,
degrees_[0] + 1}),
979 basfunc[0].view({xi[0].numel(), -1, 1}));
981 return torch::matmul(
983 .view({xi[0].numel(), -1,
degrees_[dim] + 1}),
984 basfunc[dim].view({xi[0].numel(), -1, 1}));
989 result.set(i, (eval_(i,
parDim_ - 1)).view(xi[0].sizes()));
998 auto basfunc = eval_basfunc<deriv, memory_optimized>(xi, knot_indices);
1000 if (
coeffs(0).dim() > 1) {
1002 auto sizes = xi[0].sizes() + (-1_i64);
1006 .index_select(0, coeff_indices)
1007 .view({-1, xi[0].numel(),
1015 .index_select(0, coeff_indices)
1016 .view({-1, xi[0].numel()}))
1017 .view(xi[0].sizes()));
1044 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1076 template <
bool memory_optimized = false>
1079 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1081 return find_coeff_indices<memory_optimized>(
1085 template <
bool memory_optimized = false>
1088 using utils::operator-;
1091 return torch::zeros_like(
coeffs_[0]).to(torch::kInt64);
1092 else if constexpr (
parDim_ == 1)
1093 return utils::VSlice<memory_optimized>(indices[0].flatten(), -
degrees_[0],
1096 return utils::VSlice<memory_optimized>(
1098 utils::make_array<int64_t>(-
degrees_),
1099 utils::make_array<int64_t, parDim_>(1),
1114 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1118 return torch::ones_like(
coeffs_[0]);
1120 return torch::zeros_like(
coeffs_[0]);
1125 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1129 return torch::ones_like(
coeffs_[0]);
1131 return torch::zeros_like(
coeffs_[0]);
1149 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1151 const torch::Tensor &knot_indices)
const {
1154 return torch::ones_like(
coeffs_[0]);
1156 return torch::zeros_like(
coeffs_[0]);
1158 return eval_basfunc<deriv, memory_optimized>(
1162 template <deriv deriv = deriv::func,
bool memory_optimized = false>
1169 return torch::ones_like(
coeffs_[0]);
1171 return torch::zeros_like(
coeffs_[0]);
1177 assert(xi[i].sizes() == knot_indices[i].sizes());
1179 assert(xi[0].sizes() == xi[i].sizes());
1181 if constexpr (memory_optimized) {
1185 this]<std::size_t... Is>(std::index_sequence<Is...>) {
1193 xi[Is].flatten(), knot_indices[Is].flatten())
1194 .transpose(0, 1))...};
1197 return basfunc_(std::make_index_sequence<parDim_>{});
1204 return eval_prefactor<degrees_[0], (short_t)deriv % 10>() *
1205 eval_basfunc_univariate<degrees_[0], 0, (short_t)deriv % 10>(
1206 xi[0].flatten(), knot_indices[0].flatten());
1211 auto basfunc_ = [&,
this]<std::size_t... Is>(
1212 std::index_sequence<Is...>) {
1222 10>(xi[Is].flatten(),
1223 knot_indices[Is].flatten())...);
1237 std::array<real_t, geoDim_>(
const std::array<real_t, parDim_> &)>
1239 static_assert(
parDim_ <= 4,
"Unsupported parametric dimension");
1243 auto c = transformation(std::array<real_t, parDim_>{});
1245 coeffs_[d].detach()[0] = c[d];
1249 else if constexpr (
parDim_ == 1) {
1250#pragma omp parallel for
1251 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1252 auto c = transformation(
1253 std::array<real_t, parDim_>{i / real_t(
ncoeffs_[0] - 1)});
1255 coeffs_[d].detach()[i] = c[d];
1260 else if constexpr (
parDim_ == 2) {
1261#pragma omp parallel for collapse(2)
1262 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1263 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1264 auto c = transformation(std::array<real_t, parDim_>{
1273 else if constexpr (
parDim_ == 3) {
1274#pragma omp parallel for collapse(3)
1275 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1276 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1277 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1278 auto c = transformation(std::array<real_t, parDim_>{
1290 else if constexpr (
parDim_ == 4) {
1291#pragma omp parallel for collapse(4)
1292 for (int64_t l = 0; l <
ncoeffs_[3]; ++l) {
1293 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1294 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1295 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1296 auto c = transformation(std::array<real_t, parDim_>{
1309 throw std::runtime_error(
"Unsupported parametric dimension");
1315 template <std::
size_t N>
1318 std::array<real_t, N>(
const std::array<real_t, parDim_> &)>
1320 std::array<short_t, N> dims) {
1321 static_assert(
parDim_ <= 4,
"Unsupported parametric dimension");
1325 auto c = transformation(std::array<real_t, parDim_>{});
1326 for (std::size_t d = 0; d < N; ++d)
1327 coeffs_[dims[d]].detach()[0] = c[d];
1331 else if constexpr (
parDim_ == 1) {
1332#pragma omp parallel for
1333 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1334 auto c = transformation(
1335 std::array<real_t, parDim_>{i / real_t(
ncoeffs_[0] - 1)});
1336 for (std::size_t d = 0; d < N; ++d)
1337 coeffs_[dims[d]].detach()[i] = c[d];
1342 else if constexpr (
parDim_ == 2) {
1343#pragma omp parallel for collapse(2)
1344 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1345 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1346 auto c = transformation(std::array<real_t, parDim_>{
1348 for (std::size_t d = 0; d < N; ++d)
1355 else if constexpr (
parDim_ == 3) {
1356#pragma omp parallel for collapse(3)
1357 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1358 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1359 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1360 auto c = transformation(std::array<real_t, parDim_>{
1363 for (std::size_t d = 0; d < N; ++d)
1372 else if constexpr (
parDim_ == 4) {
1373#pragma omp parallel for collapse(4)
1374 for (int64_t l = 0; l <
ncoeffs_[3]; ++l) {
1375 for (int64_t k = 0; k <
ncoeffs_[2]; ++k) {
1376 for (int64_t j = 0; j <
ncoeffs_[1]; ++j) {
1377 for (int64_t i = 0; i <
ncoeffs_[0]; ++i) {
1378 auto c = transformation(std::array<real_t, parDim_>{
1381 for (std::size_t d = 0; d < N; ++d)
1391 throw std::runtime_error(
"Unsupported parametric dimension");
1398 nlohmann::json json;
1412 return ::iganet::utils::to_json<real_t, 1>(
knots_);
1417 auto coeffs_json = nlohmann::json::array();
1419 auto [coeffs_cpu, coeffs_accessor] =
1420 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
1422 auto json = nlohmann::json::array();
1425 json.push_back(coeffs_accessor[0]);
1430 json.push_back(coeffs_accessor[i]);
1433 coeffs_json.push_back(json);
1441 if (json[
"geoDim"].get<short_t>() !=
geoDim_)
1442 throw std::runtime_error(
1443 "JSON object provides incompatible geometric dimensions");
1445 if (json[
"parDim"].get<short_t>() !=
parDim_)
1446 throw std::runtime_error(
1447 "JSON object provides incompatible parametric dimensions");
1449 if (json[
"degrees"].get<std::array<short_t, parDim_>>() !=
degrees_)
1450 throw std::runtime_error(
"JSON object provides incompatible degrees");
1452 nknots_ = json[
"nknots"].get<std::array<int64_t, parDim_>>();
1453 ncoeffs_ = json[
"ncoeffs"].get<std::array<int64_t, parDim_>>();
1459 auto kv = json[
"knots"].get<std::array<std::vector<real_t>,
parDim_>>();
1464 auto c = json[
"coeffs"].get<std::array<std::vector<real_t>,
geoDim_>>();
1473 inline pugi::xml_document
to_xml(
int id = 0, std::string label =
"",
1474 int index = -1)
const {
1475 pugi::xml_document doc;
1476 pugi::xml_node root = doc.append_child(
"xml");
1477 to_xml(root,
id, label, index);
1483 inline pugi::xml_node &
to_xml(pugi::xml_node &root,
int id = 0,
1484 std::string label =
"",
int index = -1)
const {
1486 pugi::xml_node geo = root.append_child(
"Geometry");
1490 geo.append_attribute(
"type") =
"Point";
1493 geo.append_attribute(
"id") = id;
1496 geo.append_attribute(
"index") = index;
1499 geo.append_attribute(
"label") = label.c_str();
1503 else if constexpr (
parDim_ == 1) {
1504 geo.append_attribute(
"type") =
"BSpline";
1507 geo.append_attribute(
"id") = id;
1510 geo.append_attribute(
"index") = index;
1513 geo.append_attribute(
"label") = label.c_str();
1516 pugi::xml_node basis = geo.append_child(
"Basis");
1517 basis.append_attribute(
"type") =
"BSplineBasis";
1520 pugi::xml_node
knots = basis.append_child(
"KnotVector");
1523 std::stringstream ss;
1524 auto [knots_cpu, knots_accessor] =
1525 utils::to_tensorAccessor<real_t, 1>(
knots_[0], torch::kCPU);
1526 for (int64_t i = 0; i <
nknots_[0]; ++i)
1527 ss << std::to_string(knots_accessor[i])
1528 << (i <
nknots_[0] - 1 ?
" " :
"");
1529 knots.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1534 geo.append_attribute(
"type") =
1535 std::string(
"TensorBSpline").append(std::to_string(
parDim_)).c_str();
1538 geo.append_attribute(
"id") = id;
1541 geo.append_attribute(
"index") = index;
1544 geo.append_attribute(
"label") = label.c_str();
1547 pugi::xml_node bases = geo.append_child(
"Basis");
1548 bases.append_attribute(
"type") = std::string(
"TensorBSplineBasis")
1549 .append(std::to_string(
parDim_))
1553 pugi::xml_node basis = bases.append_child(
"Basis");
1554 basis.append_attribute(
"type") =
"BSplineBasis";
1555 basis.append_attribute(
"index") = index;
1558 pugi::xml_node
knots = basis.append_child(
"KnotVector");
1561 std::stringstream ss;
1562 auto [knots_cpu, knots_accessor] =
1563 utils::to_tensorAccessor<real_t, 1>(
knots_[index], torch::kCPU);
1564 for (int64_t i = 0; i <
nknots_[index]; ++i)
1565 ss << std::to_string(knots_accessor[i])
1566 << (i <
nknots_[index] - 1 ?
" " :
"");
1567 knots.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1573 pugi::xml_node coefs = geo.append_child(
"coefs");
1574 coefs.append_attribute(
"geoDim") =
geoDim_;
1576 auto [coeffs_cpu, coeffs_accessors] =
1577 utils::to_tensorAccessor<real_t, 1>(
coeffs_, torch::kCPU);
1578 std::stringstream ss;
1582 ss << std::to_string(coeffs_accessors[g][0]) <<
" ";
1587 ss << std::to_string(coeffs_accessors[g][i]) <<
" ";
1590 coefs.append_child(pugi::node_pcdata).set_value(ss.str().c_str());
1597 std::string label =
"",
int index = -1) {
1598 return from_xml(doc.child(
"xml"),
id, label, index);
1603 std::string label =
"",
int index = -1) {
1605 std::array<bool, std::max(
parDim_,
short_t{1})> nknots_found{
false},
1606 ncoeffs_found{
false};
1609 for (pugi::xml_node geo : root.children(
"Geometry")) {
1615 if (geo.attribute(
"type").value() == std::string(
"Point") &&
1616 (
id >= 0 ? geo.attribute(
"id").as_int() == id :
true) &&
1617 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
1618 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
1620 nknots_found[0] =
true;
1621 ncoeffs_found[0] =
true;
1628 else if constexpr (
parDim_ == 1) {
1631 if (geo.attribute(
"type").value() == std::string(
"BSpline") &&
1632 (
id >= 0 ? geo.attribute(
"id").as_int() == id :
true) &&
1633 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
1634 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
1637 if (pugi::xml_node basis = geo.child(
"Basis");
1638 basis.attribute(
"type").value() == std::string(
"BSplineBasis")) {
1641 if (pugi::xml_node
knots = basis.child(
"KnotVector");
1644 std::vector<real_t> kv;
1645 std::string values = std::regex_replace(
1646 knots.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
1647 for (
auto value = strtok(&values[0],
" "); value != NULL;
1648 value = strtok(NULL,
" "))
1649 kv.push_back(
static_cast<real_t
>(std::stod(value)));
1655 nknots_found[0] =
true;
1656 ncoeffs_found[0] =
true;
1671 if (geo.attribute(
"type").value() ==
1672 std::string(
"TensorBSpline").append(std::to_string(
parDim_)) &&
1673 (
id >= 0 ? geo.attribute(
"id").as_int() ==
id :
true) &&
1674 (index >= 0 ? geo.attribute(
"index").as_int() == index :
true) &&
1675 (!label.empty() ? geo.attribute(
"label").value() == label :
true)) {
1678 if (pugi::xml_node bases = geo.child(
"Basis");
1679 bases.attribute(
"type").value() ==
1680 std::string(
"TensorBSplineBasis")
1681 .append(std::to_string(
parDim_))) {
1684 for (pugi::xml_node basis : bases.children(
"Basis")) {
1687 if (basis.attribute(
"type").value() ==
1688 std::string(
"BSplineBasis")) {
1690 short_t index = basis.attribute(
"index").as_int();
1693 if (pugi::xml_node
knots = basis.child(
"KnotVector");
1696 std::vector<real_t> kv;
1697 std::string values = std::regex_replace(
1698 knots.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
1700 for (
auto value = strtok(&values[0],
" "); value != NULL;
1701 value = strtok(NULL,
" "))
1702 kv.push_back(
static_cast<real_t
>(std::stod(value)));
1708 nknots_found[index] =
true;
1709 ncoeffs_found[index] =
true;
1725 if (std::any_of(std::begin(nknots_found), std::end(nknots_found),
1726 [](
bool i) {
return !i; }))
1727 throw std::runtime_error(
1728 "XML object is not compatible with B-spline object");
1740 if (pugi::xml_node coefs = geo.child(
"coefs")) {
1742 std::string values = std::regex_replace(
1743 coefs.text().get(), std::regex(
"[\t\r\n\a]+| +"),
" ");
1744 auto coeffs_accessors = utils::to_tensorAccessor<real_t, 1>(
coeffs_);
1747 auto value = strtok(&values[0],
" ");
1751 throw std::runtime_error(
1752 "XML object does not provide enough coefficients");
1754 coeffs_accessors[g][0] =
static_cast<real_t
>(std::stod(value));
1755 value = strtok(NULL,
" ");
1759 throw std::runtime_error(
1760 "XML object provides too many coefficients");
1763 auto value = strtok(&values[0],
" ");
1768 throw std::runtime_error(
1769 "XML object does not provide enough coefficients");
1771 coeffs_accessors[g][i] =
static_cast<real_t
>(std::stod(value));
1772 value = strtok(NULL,
" ");
1776 throw std::runtime_error(
1777 "XML object provides too many coefficients");
1785 if (nknots_found[0] && ncoeffs_found[0])
1787 }
else if (std::all_of(std::begin(nknots_found), std::end(nknots_found),
1788 [](
bool i) {
return i; }) &&
1789 std::all_of(std::begin(ncoeffs_found),
1790 std::end(ncoeffs_found),
1791 [](
bool i) {
return i; }))
1795 throw std::runtime_error(
1796 "XML object is not compatible with B-spline object");
1800 throw std::runtime_error(
"XML object does not provide coefficients");
1804 throw std::runtime_error(
"XML object does not provide geometry with given "
1805 "id, index, and/or label");
1810 inline void load(
const std::string &filename,
1811 const std::string &key =
"bspline") {
1812 torch::serialize::InputArchive archive;
1813 archive.load_from(filename);
1818 inline torch::serialize::InputArchive &
1819 read(torch::serialize::InputArchive &archive,
1820 const std::string &key =
"bspline") {
1821 torch::Tensor tensor;
1823 archive.read(key +
".parDim", tensor);
1824 if (tensor.item<int64_t>() !=
parDim_)
1825 throw std::runtime_error(
"parDim mismatch");
1827 archive.read(key +
".geoDim", tensor);
1828 if (tensor.item<int64_t>() !=
geoDim_)
1829 throw std::runtime_error(
"geoDim mismatch");
1832 archive.read(key +
".degree[" + std::to_string(i) +
"]", tensor);
1833 if (tensor.item<int64_t>() !=
degrees_[i])
1834 throw std::runtime_error(
"degrees mismatch");
1838 archive.read(key +
".nknots[" + std::to_string(i) +
"]", tensor);
1839 nknots_[i] = tensor.item<int64_t>();
1843 archive.read(key +
".knots[" + std::to_string(i) +
"]",
knots_[i]);
1846 archive.read(key +
".ncoeffs[" + std::to_string(i) +
"]", tensor);
1847 ncoeffs_[i] = tensor.item<int64_t>();
1851 archive.read(key +
".coeffs[" + std::to_string(i) +
"]",
coeffs_[i]);
1857 inline void save(
const std::string &filename,
1858 const std::string &key =
"bspline")
const {
1859 torch::serialize::OutputArchive archive;
1860 write(archive, key).save_to(filename);
1864 inline torch::serialize::OutputArchive &
1865 write(torch::serialize::OutputArchive &archive,
1866 const std::string &key =
"bspline")
const {
1867 archive.write(key +
".parDim", torch::full({1},
parDim_));
1868 archive.write(key +
".geoDim", torch::full({1},
geoDim_));
1871 archive.write(key +
".degree[" + std::to_string(i) +
"]",
1875 archive.write(key +
".nknots[" + std::to_string(i) +
"]",
1876 torch::full({1},
nknots_[i]));
1879 archive.write(key +
".knots[" + std::to_string(i) +
"]",
knots_[i]);
1882 archive.write(key +
".ncoeffs[" + std::to_string(i) +
"]",
1886 archive.write(key +
".coeffs[" + std::to_string(i) +
"]",
coeffs_[i]);
1893 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
1895 real_t rtol = real_t{1e-5}, real_t atol = real_t{1e-8})
const {
1896 if constexpr (!std::is_same<real_t, other_t>::value)
1916 result *= torch::allclose(
knots(i), other.
knots(i), rtol, atol);
1919 result *= torch::allclose(
coeffs(i), other.
coeffs(i), rtol, atol);
1925 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
1928 if constexpr (!std::is_same<real_t, other_t>::value)
1948 result *= torch::equal(
knots(i), other.
knots(i));
1957 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
1972 assert(numRefine > 0);
1973 assert(dim == -1 || (dim >= 0 && dim <
parDim_));
1979 for (
short_t refine = 0; refine < numRefine; ++refine) {
1996 std::vector<real_t> kv;
1999 for (int64_t j = 0; j <
degrees_[i]; ++j)
2000 kv.push_back(
static_cast<real_t
>(0));
2003 kv.push_back(
static_cast<real_t
>(j) /
2006 for (int64_t j = 0; j <
degrees_[i]; ++j)
2007 kv.push_back(
static_cast<real_t
>(1));
2018 knots_indices[i] =
knots[i].index(
2019 {torch::indexing::Slice(0,
knots[i].numel() -
degrees_[i] - 1)});
2044 if constexpr (
degree > terminal)
2045 return degree * eval_prefactor<degree - 1, deriv, terminal>();
2058 throw std::runtime_error(
2059 "Not enough coefficients to create open knot vector");
2066 auto inner = torch::empty({0},
options_);
2068 if (num_inner > 0) {
2069 inner = torch::arange(0, num_inner + 1,
options_);
2070 inner = inner /
static_cast<real_t
>(num_inner);
2073 knots_[i] = torch::cat({start, inner, end});
2125 coeffs_[i] = torch::kron(torch::linspace(
static_cast<real_t
>(0),
2126 static_cast<real_t
>(1),
2154 auto idx_base = torch::arange(count,
options_.requires_grad(
false).template dtype<int64_t>()).unsqueeze(1);
2157 auto offsets = torch::arange(1,
degrees_[j] + 1,
options_.requires_grad(
false).template dtype<int64_t>()).unsqueeze(0);
2160 auto indices = idx_base + offsets;
2163 auto gathered =
knots_[j].index_select(0, indices.flatten()).view({count,
degrees_[j]});
2166 auto greville_ = gathered.mean(1);
2187 std::pow(10, i) * 0, std::pow(10, i) * (size - 1), size,
options_);
2193 throw std::runtime_error(
"Unsupported init option");
2204 assert(
knots[i].numel() == knot_indices[i].numel() +
degrees_[i] + 1);
2208 auto basfunc = update_coeffs_univariate<degrees_[0], 0>(
2209 knots[0].flatten(), knot_indices[0].flatten());
2216 .index_select(0, coeff_indices)
2217 .view({-1, knot_indices[0].numel()}))
2218 .view(knot_indices[0].sizes());
2223 auto basfunc_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
2224 if constexpr (
sizeof...(Is) == 1)
2226 knots[Is].flatten(), knot_indices[Is].flatten()),
2230 knots[Is].flatten(), knot_indices[Is].flatten())...);
2240 for (
short_t i = start_index; i <= stop_index; ++i)
2241 result *= array[i].numel();
2250 .repeat_interleave(prod_(knot_indices, 0, i - 1), 0)
2251 .repeat(prod_(knot_indices, i + 1,
parDim_ - 1));
2258 .index_select(0, coeff_indices)
2259 .view({-1, knot_indices_[0].numel()}))
2260 .view(knot_indices_[0].sizes());
2369 template <
short_t degree,
short_t dim,
short_t deriv>
2371 const torch::Tensor &knot_indices)
const {
2372 assert(xi.sizes() == knot_indices.sizes());
2378 torch::Tensor b = torch::ones({xi.numel()},
options_);
2393 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2394 .to(::iganet::dtype<real_t>());
2400 auto w = torch::div(xi.repeat(k) - t1 - mask, t21 - mask);
2403 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2406 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2420 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2421 .to(::iganet::dtype<real_t>());
2427 auto w = torch::div(torch::ones_like(t21,
options_) - mask, t21 - mask);
2430 b = torch::cat({torch::mul(-w, b), torch::zeros_like(xi,
options_)},
2432 torch::cat({torch::zeros_like(xi,
options_), torch::mul(w, b)}, 0);
2435 return b.view({
degree + 1, xi.numel()});
2445 template <
short_t degree,
short_t dim>
2448 const torch::Tensor &knot_indices)
const {
2451 torch::Tensor b = torch::ones({knot_indices.numel()},
options_);
2466 auto mask = (t21 < std::numeric_limits<real_t>::epsilon())
2467 .to(::iganet::dtype<real_t>());
2473 auto w = torch::div(
2474 knots.index({torch::indexing::Slice(k, knot_indices.numel() + k)})
2480 b = torch::cat({torch::mul(torch::ones_like(w,
options_) - w, b),
2481 torch::zeros_like(knot_indices,
options_)},
2484 {torch::zeros_like(knot_indices,
options_), torch::mul(w, b)}, 0);
2487 return b.view({
degree + 1, knot_indices.numel()});
2496#ifdef IGANET_WITH_GISMO
2501 auto [coeffs_cpu, coeffs_accessor] =
2502 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2503 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
2505 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
2508 std::array<gismo::gsKnotVector<real_t>,
parDim_> kv;
2511 auto [knots_cpu, knots_accessor] =
2512 utils::to_tensorAccessor<real_t, 1>(
knots_[i], torch::kCPU);
2513 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
2514 kv[i] = gismo::gsKnotVector<real_t>(
degrees_[i], knots_cpu_ptr,
2515 knots_cpu_ptr + knots_cpu.size(0));
2520 return gismo::gsBSpline<real_t>(gismo::give(kv[0]), gismo::give(coefs));
2522 }
else if constexpr (
parDim_ == 2) {
2524 return gismo::gsTensorBSpline<parDim_, real_t>(
2525 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(coefs));
2527 }
else if constexpr (
parDim_ == 3) {
2529 return gismo::gsTensorBSpline<parDim_, real_t>(
2530 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(kv[2]),
2531 gismo::give(coefs));
2533 }
else if constexpr (
parDim_ == 4) {
2535 return gismo::gsTensorBSpline<parDim_, real_t>(
2536 gismo::give(kv[0]), gismo::give(kv[1]), gismo::give(kv[2]),
2537 gismo::give(kv[3]), gismo::give(coefs));
2540 throw std::runtime_error(
"Invalid parametric dimension");
2543 throw std::runtime_error(
2544 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
2548#ifdef IGANET_WITH_GISMO
2551 gismo::gsBSpline<real_t> &
to_gismo(gismo::gsBSpline<real_t> &bspline,
2552 bool updateKnotVector =
true,
2553 bool updateCoeffs =
true)
const {
2555 if (updateKnotVector) {
2559 if (bspline.degree(0) !=
degrees_[0])
2560 throw std::runtime_error(
"Degrees mismatch");
2562 auto [knots_cpu, knots_accessor] =
2563 utils::to_tensorAccessor<real_t, 1>(
knots_[0], torch::kCPU);
2564 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
2566 gismo::gsKnotVector<real_t> kv(
degrees_[0], knots_cpu_ptr,
2567 knots_cpu_ptr + knots_cpu.size(0));
2569 bspline.knots(0).swap(kv);
2572 throw std::runtime_error(
"Invalid parametric dimension");
2578 auto [coeffs_cpu, coeffs_accessor] =
2579 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2580 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
2581 bspline.coefs().col(g) =
2582 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
2590 gismo::gsTensorBSpline<parDim_, real_t> &
2591 to_gismo(gismo::gsTensorBSpline<parDim_, real_t> &bspline,
2592 bool updateKnotVector =
true,
bool updateCoeffs =
true)
const {
2594 if (updateKnotVector) {
2598 assert(bspline.degree(i) ==
degrees_[i]);
2601 auto [knots_cpu, knots_accessor] =
2602 utils::to_tensorAccessor<real_t, 1>(
knots_[i], torch::kCPU);
2603 auto knots_cpu_ptr = knots_cpu.template data_ptr<real_t>();
2605 gismo::gsKnotVector<real_t> kv(
degrees_[i], knots_cpu_ptr,
2606 knots_cpu_ptr + knots_cpu.size(0));
2607 bspline.knots(i).swap(kv);
2614 auto [coeffs_cpu, coeffs_accessor] =
2615 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2616 auto coeffs_cpu_ptr = coeffs_cpu.template data_ptr<real_t>();
2617 bspline.coefs().col(g) =
2618 gismo::gsAsConstVector<real_t>(coeffs_cpu_ptr, coeffs_cpu.size(0));
2627 template <
typename BSpline>
2628 BSpline &
to_gismo(BSpline &bspline,
bool,
bool)
const {
2629 throw std::runtime_error(
2630 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
2636#ifdef IGANET_WITH_GISMO
2639 auto &
from_gismo(
const gismo::gsBSpline<real_t> &bspline,
2640 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
2642 if (updateKnotVector) {
2644 throw std::runtime_error(
2645 "Knot vectors can only be updated for Non-uniform B-splines");
2650 if (bspline.coefs().cols() !=
geoDim_)
2651 throw std::runtime_error(
"Geometric dimensions mismatch");
2654 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
2658 auto [coeffs_cpu, coeffs_accessor] =
2659 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2661 const real_t *coeffs_ptr = bspline.coefs().col(g).data();
2663 for (int64_t i = 0; i <
ncoeffs_[g]; ++i)
2664 coeffs_accessor[i] = coeffs_ptr[i];
2674 auto &
from_gismo(
const gismo::gsTensorBSpline<parDim_, real_t> &bspline,
2675 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
2677 if (updateKnotVector) {
2679 throw std::runtime_error(
2680 "Knot vectors can only be updated for Non-uniform B-splines");
2685 if (bspline.coefs().cols() !=
geoDim_)
2686 throw std::runtime_error(
"Geometric dimensions mismatch");
2689 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
2693 auto [coeffs_cpu, coeffs_accessor] =
2694 utils::to_tensorAccessor<real_t, 1>(
coeffs_[g], torch::kCPU);
2696 const real_t *coeffs_ptr = bspline.coefs().col(g).data();
2698 for (int64_t i = 0; i <
ncoeffs_[g]; ++i)
2699 coeffs_accessor[i] = coeffs_ptr[i];
2710 template <
typename BSpline>
auto &
from_gismo(BSpline &bspline,
bool,
bool) {
2711 throw std::runtime_error(
2712 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
2721inline torch::serialize::OutputArchive &
2722operator<<(torch::serialize::OutputArchive &archive,
2724 return obj.
write(archive);
2729inline torch::serialize::InputArchive &
2732 return obj.
read(archive);
2757 template <
template <
typename,
short_t,
short_t...>
class BSpline,
2758 std::make_signed<short_t>::type degree_elevate = 0>
2759 using derived_type = BSpline<real_t, GeoDim, (Degrees + degree_elevate)...>;
2763 template <std::make_signed<short_t>::type degree_elevate = 0>
2770 template <
typename other_t,
short_t GeoDim_,
short_t... Degrees_>
2776 template <
typename other_t>
2831 .to(
options.requires_grad(
false))
2841 const std::array<std::vector<typename Base::value_type>,
Base::parDim_>
2847 throw std::runtime_error(
"Knot vector is too short for an open knot "
2848 "vector (n+p+1 > 2*(p+1))");
2863 template <deriv deriv = deriv::func,
bool memory_optimized = false>
2864 inline auto eval(
const torch::Tensor &xi)
const {
2868 template <deriv deriv = deriv::func,
bool memory_optimized = false>
2879 return Base::template eval<deriv, memory_optimized>(
2883 template <deriv deriv = deriv::func,
bool memory_optimized = false>
2896 return Base::template eval<deriv, memory_optimized>(xi, knot_indices);
2899 template <deriv deriv = deriv::func,
bool memory_optimized = false>
2902 const torch::Tensor &coeff_indices)
const {
2912 return Base::template eval<deriv, memory_optimized>(xi, knot_indices,
2932 return torch::zeros_like(
Base::coeffs_[0]).to(torch::kInt64);
2942 auto nnz =
Base::knots_[i].repeat({xi[i].numel(), 1}) >
2943 xi[i].flatten().view({-1, 1});
2945 torch::remainder(std::get<1>(((nnz.cumsum(1) == 1) & nnz).max(1)) - 1,
2947 .view(xi[i].sizes());
2961 assert(numRefine > 0);
2969 auto [kv_cpu, kv_accessor] =
2970 utils::to_tensorAccessor<typename Base::value_type, 1>(
2973 std::vector<typename Base::value_type> kv;
2975 kv.push_back(kv_accessor[0]);
2977 for (int64_t j = 1; j < kv_accessor.size(0); ++j) {
2979 if ((dim == -1 || dim == i) && (kv_accessor[j - 1] < kv_accessor[j]))
2980 for (
short_t refine = 1; refine < (2 << (numRefine - 1)); ++refine)
2981 kv.push_back(kv_accessor[j - 1] +
2984 2 << (numRefine - 1)) *
2985 (kv_accessor[j] - kv_accessor[j - 1]));
2987 kv.push_back(kv_accessor[j]);
3001 knots_indices[i] =
knots[i].index({torch::indexing::Slice(
3045 knots_indices[i] =
knots_[i].index({torch::indexing::Slice(
3070 assert(numReduce > 0);
3078 auto [kv_cpu, kv_accessor] =
3079 utils::to_tensorAccessor<typename Base::value_type, 1>(
3082 std::vector<typename Base::value_type> kv;
3084 kv.push_back(kv_accessor[0]);
3086 for (int64_t j = 1; j < kv_accessor.size(0); ++j) {
3088 if ((dim == -1 || dim == i) && (kv_accessor[j - 1] < kv_accessor[j]) &&
3089 (kv_accessor[j] < kv_accessor[kv_accessor.size(0) - 1]))
3090 for (
short_t reduce = 0; reduce < numReduce; ++reduce)
3091 kv.push_back(kv_accessor[j]);
3093 kv.push_back(kv_accessor[j]);
3107 knots_indices[i] =
knots[i].index({torch::indexing::Slice(
3128#ifdef IGANET_WITH_GISMO
3131 auto &
from_gismo(
const gismo::gsBSpline<typename Base::value_type> &bspline,
3132 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3134 if (updateKnotVector) {
3139 throw std::runtime_error(
"Degrees mismatch");
3142 throw std::runtime_error(
"Knot vector dimensions mismatch");
3144 auto [knots0_cpu, knots0_accessor] =
3145 utils::to_tensorAccessor<typename Base::value_type, 1>(
3149 bspline.knots(0).asMatrix().data();
3152 knots0_accessor[i] = knots0_ptr[i];
3157 throw std::runtime_error(
"Invalid parametric dimension");
3163 throw std::runtime_error(
"Geometric dimensions mismatch");
3166 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3170 auto [coeffs_cpu, coeffs_accessor] =
3171 utils::to_tensorAccessor<typename Base::value_type, 1>(
3175 bspline.coefs().row(g).data();
3178 coeffs_accessor[i] = coeffs_ptr[i];
3189 const gismo::gsTensorBSpline<Base::parDim_, typename Base::value_type>
3191 bool updateCoeffs =
true,
bool updateKnotVector =
false) {
3193 if (updateKnotVector) {
3197 throw std::runtime_error(
"Degrees mismatch");
3200 throw std::runtime_error(
"Knot vector dimensions mismatch");
3202 auto [knots_cpu, knots_accessor] =
3203 utils::to_tensorAccessor<typename Base::value_type, 1>(
3207 bspline.knots(i).asMatrix().data();
3210 knots_accessor[i] = knots_ptr[i];
3219 throw std::runtime_error(
"Geometric dimensions mismatch");
3222 throw std::runtime_error(
"Coefficient vector dimensions mismatch");
3226 auto [coeffs_cpu, coeffs_accessor] =
3227 utils::to_tensorAccessor<typename Base::value_type, 1>(
3231 bspline.coefs().row(g).data();
3234 coeffs_accessor[i] = coeffs_ptr[i];
3245 template <
typename BSpline>
auto &
from_gismo(BSpline &bspline,
bool,
bool) {
3246 throw std::runtime_error(
3247 "This functions must be compiled with -DIGANET_WITH_GISMO turned on");
3258 template<
typename T>
3262 template<
typename T>
3264 std::is_base_of_v<UniformSplineCore_, T> && !std::is_base_of_v<NonUniformSplineCore_, T>;
3267 template<
typename T>
3269 std::is_base_of_v<NonUniformSplineCore_, T>;
3284 template <
typename BSplineCore>
3291 using BSplineCore::BSplineCore;
3299 std::make_signed<short_t>::type degree_elevate = 0>
3305 template <std::make_signed<short_t>::type degree_elevate = 0>
3315 real_t, GeoDim, Degrees...>>;
3319 template <
typename other_t>
3324 using Ptr = std::shared_ptr<BSplineCommon>;
3327 using uPtr = std::unique_ptr<BSplineCommon>;
3335 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3336 BSplineCore::coeffs_[i] = other.coeffs(i).
clone();
3345 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3346 BSplineCore::coeffs_[i] = coeffs[i].
clone();
3348 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3349 BSplineCore::coeffs_[i] = coeffs[i];
3359 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3360 BSplineCore::coeffs_[i] = std::move(coeffs[i]);
3397 make_unique(
const std::array<std::vector<typename BSplineCore::value_type>,
3398 BSplineCore::parDim_> &kv,
3406 make_unique(
const std::array<std::vector<typename BSplineCore::value_type>,
3407 BSplineCore::parDim_> &kv,
3450 make_shared(
const std::array<std::vector<typename BSplineCore::value_type>,
3451 BSplineCore::parDim_> &kv,
3459 make_shared(
const std::array<std::vector<typename BSplineCore::value_type>,
3460 BSplineCore::parDim_> &kv,
3476 BSplineCore::uniform_refine(numRefine, dim);
3484 result.nknots_ = BSplineCore::nknots_;
3485 result.ncoeffs_ = BSplineCore::ncoeffs_;
3486 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
3488 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3489 result.knots_[i] = BSplineCore::knots_[i].
clone();
3491 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3492 result.coeffs_[i] = BSplineCore::coeffs_[i].
clone();
3502 result.nknots_ = BSplineCore::nknots_;
3503 result.ncoeffs_ = BSplineCore::ncoeffs_;
3504 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
3506 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3507 result.knots_[i] = BSplineCore::knots_[i].
to(options);
3509 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3510 result.coeffs_[i] = BSplineCore::coeffs_[i].
to(options);
3516 inline auto to(torch::Device device)
const {
3519 result.nknots_ = BSplineCore::nknots_;
3520 result.ncoeffs_ = BSplineCore::ncoeffs_;
3521 result.ncoeffs_reverse_ = BSplineCore::ncoeffs_reverse_;
3523 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3524 result.knots_[i] = BSplineCore::knots_[i].
to(device);
3526 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3527 result.coeffs_[i] = BSplineCore::coeffs_[i].
to(device);
3533 template <
typename real_t>
inline auto to()
const {
3534 return to(BSplineCore::options_.
template dtype<real_t>());
3545 bool compatible(
true);
3547 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3548 compatible *= (BSplineCore::nknots(i) == other.nknots(i));
3550 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3551 compatible *= (BSplineCore::ncoeffs(i) == other.ncoeffs(i));
3554 throw std::runtime_error(
"B-splines are not compatible");
3557 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3558 BSplineCore::coeffs(i) -= other.coeffs(i);
3560 BSplineCore::coeffs(dim) -= other.coeffs(dim);
3573 bool compatible(
true);
3575 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3576 compatible *= (BSplineCore::nknots(i) == other.nknots(i));
3578 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3579 compatible *= (BSplineCore::ncoeffs(i) == other.ncoeffs(i));
3582 throw std::runtime_error(
"B-splines are not compatible");
3585 for (
short_t i = 0; i < BSplineCore::geoDim_; ++i)
3586 BSplineCore::coeffs(i) =
3587 torch::abs(BSplineCore::coeffs(i) - other.coeffs(i));
3589 BSplineCore::coeffs(dim) =
3590 torch::abs(BSplineCore::coeffs(dim) - other.coeffs(dim));
3596 inline auto scale(
typename BSplineCore::value_type s,
int dim = -1) {
3598 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
3599 BSplineCore::coeffs(i) *= s;
3601 BSplineCore::coeffs(dim) *= s;
3607 scale(std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
3608 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
3609 BSplineCore::coeffs(i) *= v[i];
3615 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
3616 for (
int i = 0; i < BSplineCore::geoDim(); ++i)
3617 BSplineCore::coeffs(i) += v[i];
3622 inline auto rotate(
typename BSplineCore::value_type angle) {
3624 static_assert(BSplineCore::geoDim() == 2,
3625 "Rotation about one angle is only available in 2D");
3628 coeffs[0] = std::cos(angle) * BSplineCore::coeffs(0) -
3629 std::sin(angle) * BSplineCore::coeffs(1);
3630 coeffs[1] = std::sin(angle) * BSplineCore::coeffs(0) +
3631 std::cos(angle) * BSplineCore::coeffs(1);
3633 BSplineCore::coeffs().swap(coeffs);
3638 inline auto rotate(std::array<typename BSplineCore::value_type, 3> angle) {
3640 static_assert(BSplineCore::geoDim() == 3,
3641 "Rotation about two angles is only available in 3D");
3645 std::cos(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(0) +
3646 (std::sin(angle[0]) * std::sin(angle[1]) * std::cos(angle[2]) -
3647 std::cos(angle[0]) * std::sin(angle[2])) *
3648 BSplineCore::coeffs(1) +
3649 (std::cos(angle[0]) * std::sin(angle[1]) * std::cos(angle[2]) +
3650 std::sin(angle[0]) * std::sin(angle[2])) *
3651 BSplineCore::coeffs(2);
3654 std::cos(angle[1]) * std::sin(angle[2]) * BSplineCore::coeffs(0) +
3655 (std::sin(angle[0]) * std::sin(angle[1]) * std::sin(angle[2]) +
3656 std::cos(angle[0]) * std::cos(angle[2])) *
3657 BSplineCore::coeffs(1) +
3658 (std::cos(angle[0]) * std::sin(angle[1]) * std::sin(angle[2]) -
3659 std::sin(angle[0]) * std::cos(angle[2])) *
3660 BSplineCore::coeffs(2);
3663 -std::sin(angle[1]) * BSplineCore::coeffs(0) +
3664 std::sin(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(1) +
3665 std::cos(angle[0]) * std::cos(angle[1]) * BSplineCore::coeffs(2);
3667 BSplineCore::coeffs().swap(coeffs);
3675 auto min_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
3676 return torch::stack({BSplineCore::coeffs(Is).min()...});
3680 auto max_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
3681 return torch::stack({BSplineCore::coeffs(Is).max()...});
3684 std::pair<torch::Tensor, torch::Tensor> bbox;
3685 bbox.first = min_(std::make_index_sequence<BSplineCore::geoDim_>{});
3686 bbox.second = max_(std::make_index_sequence<BSplineCore::geoDim_>{});
3697 template <
bool memory_optimized = false>
3698 inline auto nv(
const torch::Tensor &xi)
const {
3702 template <
bool memory_optimized = false>
3704 return nv<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
3716 template <
bool memory_optimized = false>
3720 return nv<memory_optimized>(
3722 BSplineCore::template find_coeff_indices<memory_optimized>(
3737 template <
bool memory_optimized = false>
3740 const torch::Tensor &coeff_indices)
const {
3742 if constexpr (BSplineCore::parDim_ == 1 &&
3743 BSplineCore::geoDim_ == 2) {
3745 auto eval_ = BSplineCore::template eval<deriv::dx, memory_optimized>(xi, knot_indices, coeff_indices);
3748 else if constexpr (BSplineCore::parDim_ == 1 &&
3749 BSplineCore::geoDim_ == 3) {
3751 auto t_ = BSplineCore::template eval<deriv::dx, memory_optimized>(xi, knot_indices, coeff_indices).normalize();
3752 auto a_ = BSplineCore::template eval<deriv::dx^2, memory_optimized>(xi, knot_indices, coeff_indices);
3753 auto n_ = a_ - a_.dot(t_) * t_;
3755 t_(0,0), t_(0,1), t_(0,2));
3757 else if constexpr (BSplineCore::parDim_ == 2 &&
3758 BSplineCore::geoDim_ == 3) {
3760 auto jac_ = jac<memory_optimized>(xi, knot_indices, coeff_indices);
3762 jac_(2,0) * jac_(0,1) - jac_(0,0) * jac_(2,1),
3763 jac_(0,0) * jac_(1,1) - jac_(1,0) * jac_(0,1));
3766 throw std::runtime_error(
"Unsupported parametric/geometric dimension");
3794 template <
bool memory_optimized = false>
3795 inline auto curl(
const torch::Tensor &xi)
const {
3799 template <
bool memory_optimized = false>
3801 return curl<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
3829 template <
bool memory_optimized = false>
3833 return curl<memory_optimized>(
3835 BSplineCore::template find_coeff_indices<memory_optimized>(
3865 template <
bool memory_optimized = false>
3868 const torch::Tensor &coeff_indices)
const {
3870 static_assert(BSplineCore::parDim_ == BSplineCore::geoDim_,
3871 "curl(.) requires that parametric and geometric dimension "
3875 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
3876 assert(xi[i].sizes() == knot_indices[i].sizes());
3877 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
3878 assert(xi[0].sizes() == xi[i].sizes());
3880 if constexpr (BSplineCore::parDim_ == 2)
3888 *BSplineCore::template eval<deriv::dx, memory_optimized>(
3889 xi, knot_indices, coeff_indices)[1] -
3890 *BSplineCore::template eval<deriv::dy, memory_optimized>(
3891 xi, knot_indices, coeff_indices)[0]);
3893 else if constexpr (BSplineCore::parDim_ == 3)
3899 *BSplineCore::template eval<deriv::dy, memory_optimized>(
3900 xi, knot_indices, coeff_indices)[2] -
3901 *BSplineCore::template eval<deriv::dz, memory_optimized>(
3902 xi, knot_indices, coeff_indices)[1],
3903 *BSplineCore::template eval<deriv::dz, memory_optimized>(
3904 xi, knot_indices, coeff_indices)[0] +
3905 *BSplineCore::template eval<deriv::dx, memory_optimized>(
3906 xi, knot_indices, coeff_indices)[2],
3907 *BSplineCore::template eval<deriv::dx, memory_optimized>(
3908 xi, knot_indices, coeff_indices)[1] +
3909 *BSplineCore::template eval<deriv::dy, memory_optimized>(
3910 xi, knot_indices, coeff_indices)[0]);
3913 throw std::runtime_error(
"Unsupported parametric/geometric dimension");
3939 template <
bool memory_optimized = false,
typename Geometry>
3940 inline auto icurl(
const Geometry &G,
const torch::Tensor &xi)
const {
3941 if constexpr (BSplineCore::parDim_ == 0)
3943 torch::zeros_like(BSplineCore::coeffs_[0])};
3948 template <
bool memory_optimized = false,
typename Geometry>
3951 if constexpr (BSplineCore::parDim_ == 0)
3953 torch::zeros_like(BSplineCore::coeffs_[0])};
3955 return icurl<memory_optimized, Geometry>(
3956 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
3983 template <
bool memory_optimized = false,
typename Geometry>
3988 if constexpr (BSplineCore::parDim_ == 0)
3990 torch::zeros_like(BSplineCore::coeffs_[0])};
3992 return icurl<memory_optimized, Geometry>(
3993 G, xi, knot_indices,
3994 BSplineCore::template find_coeff_indices<memory_optimized>(
3997 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4030 template <
bool memory_optimized = false,
typename Geometry>
4034 const torch::Tensor &coeff_indices,
4036 const torch::Tensor &coeff_indices_G)
const {
4038 if constexpr (BSplineCore::parDim_ == 0)
4040 torch::zeros_like(BSplineCore::coeffs_[0])};
4043 det[0] = std::make_shared<torch::Tensor>(torch::reciprocal(
4044 G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G)
4047 return det * (curl<memory_optimized>(xi, knot_indices, coeff_indices) *
4048 G.template jac<memory_optimized>(xi, knot_indices_G,
4074 template <
bool memory_optimized = false>
4075 inline auto div(
const torch::Tensor &xi)
const {
4076 if constexpr (BSplineCore::parDim_ == 0)
4078 torch::zeros_like(BSplineCore::coeffs_[0])};
4082 template <
bool memory_optimized = false>
4084 if constexpr (BSplineCore::parDim_ == 0)
4086 torch::zeros_like(BSplineCore::coeffs_[0])};
4087 return div<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4113 template <
bool memory_optimized = false>
4117 if constexpr (BSplineCore::parDim_ == 0)
4119 torch::zeros_like(BSplineCore::coeffs_[0])};
4120 return div<memory_optimized>(
4122 BSplineCore::template find_coeff_indices<memory_optimized>(
4151 template <
bool memory_optimized = false>
4154 const torch::Tensor &coeff_indices)
const {
4156 static_assert(BSplineCore::parDim_ == BSplineCore::geoDim_,
4157 "div(.) requires parDim == geoDim");
4159 if constexpr (BSplineCore::parDim_ == 0)
4161 torch::zeros_like(BSplineCore::coeffs_[0])};
4166 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4167 assert(xi[i].sizes() == knot_indices[i].sizes());
4168 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4169 assert(xi[0].sizes() == xi[i].sizes());
4172 auto div_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4174 (*BSplineCore::template eval<
4176 xi, knot_indices, coeff_indices)[Is] +
4180 return div_(std::make_index_sequence<BSplineCore::parDim_>{});
4206 template <
bool memory_optimized = false,
typename Geometry>
4207 inline auto idiv(
const Geometry &G,
const torch::Tensor &xi) {
4208 if constexpr (BSplineCore::parDim_ == 0)
4210 torch::zeros_like(BSplineCore::coeffs_[0])};
4215 template <
bool memory_optimized = false,
typename Geometry>
4216 inline auto idiv(
const Geometry &G,
4218 if constexpr (BSplineCore::parDim_ == 0)
4220 torch::zeros_like(BSplineCore::coeffs_[0])};
4222 return idiv<memory_optimized, Geometry>(
4223 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4252 template <
bool memory_optimized = false,
typename Geometry>
4257 if constexpr (BSplineCore::parDim_ == 0)
4259 torch::zeros_like(BSplineCore::coeffs_[0])};
4261 return idiv<memory_optimized, Geometry>(
4262 G, xi, knot_indices,
4263 BSplineCore::template find_coeff_indices<memory_optimized>(
4266 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4300 template <
bool memory_optimized = false,
typename Geometry>
4301 inline auto idiv(
const Geometry &G,
4304 const torch::Tensor &coeff_indices,
4306 const torch::Tensor &coeff_indices_G)
const {
4307 if constexpr (BSplineCore::parDim_ == 0)
4309 torch::zeros_like(BSplineCore::coeffs_[0])};
4311 return ijac<memory_optimized, Geometry>(G, xi, knot_indices,
4312 coeff_indices, knot_indices_G,
4337 template <
bool memory_optimized = false>
4338 inline auto grad(
const torch::Tensor &xi)
const {
4340 static_assert(BSplineCore::geoDim_ == 1,
4341 "grad(.) requires 1D variable, use jac(.) instead");
4343 if constexpr (BSplineCore::parDim_ == 0)
4345 torch::zeros_like(BSplineCore::coeffs_[0])};
4350 template <
bool memory_optimized = false>
4353 static_assert(BSplineCore::geoDim_ == 1,
4354 "grad(.) requires 1D variable, use jac(.) instead");
4356 if constexpr (BSplineCore::parDim_ == 0)
4358 torch::zeros_like(BSplineCore::coeffs_[0])};
4360 return grad<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4385 template <
bool memory_optimized = false>
4390 static_assert(BSplineCore::geoDim_ == 1,
4391 "grad(.) requires 1D variable, use jac(.) instead");
4393 if constexpr (BSplineCore::parDim_ == 0)
4395 torch::zeros_like(BSplineCore::coeffs_[0])};
4397 return grad<memory_optimized>(
4399 BSplineCore::template find_coeff_indices<memory_optimized>(
4427 template <
bool memory_optimized = false>
4430 const torch::Tensor &coeff_indices)
const {
4432 static_assert(BSplineCore::geoDim_ == 1,
4433 "grad(.) requires 1D variable, use jac(.) instead");
4435 if constexpr (BSplineCore::parDim_ == 0)
4437 torch::zeros_like(BSplineCore::coeffs_[0])};
4441 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4442 assert(xi[i].sizes() == knot_indices[i].sizes());
4443 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4444 assert(xi[0].sizes() == xi[i].sizes());
4447 auto grad_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4450 memory_optimized>(xi, knot_indices,
4454 return grad_(std::make_index_sequence<BSplineCore::parDim_>{});
4479 template <
bool memory_optimized = false,
typename Geometry>
4480 inline auto igrad(
const Geometry &G,
const torch::Tensor &xi)
const {
4481 if constexpr (BSplineCore::parDim_ == 0)
4483 torch::zeros_like(BSplineCore::coeffs_[0])};
4488 template <
bool memory_optimized = false,
typename Geometry>
4491 if constexpr (BSplineCore::parDim_ == 0)
4493 torch::zeros_like(BSplineCore::coeffs_[0])};
4495 return igrad<memory_optimized, Geometry>(
4496 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4523 template <
bool memory_optimized = false,
typename Geometry>
4528 if constexpr (BSplineCore::parDim_ == 0)
4530 torch::zeros_like(BSplineCore::coeffs_[0])};
4532 return igrad<memory_optimized, Geometry>(
4533 G, xi, knot_indices,
4534 BSplineCore::template find_coeff_indices<memory_optimized>(
4537 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4570 template <
bool memory_optimized = false,
typename Geometry>
4574 const torch::Tensor &coeff_indices,
4576 const torch::Tensor &coeff_indices_G)
const {
4577 if constexpr (BSplineCore::parDim_ == 0)
4579 torch::zeros_like(BSplineCore::coeffs_[0])};
4581 return grad<memory_optimized>(xi, knot_indices, coeff_indices) *
4582 G.template jac<memory_optimized>(xi, knot_indices_G,
4623 template <
bool memory_optimized = false>
4624 inline auto hess(
const torch::Tensor &xi)
const {
4625 if constexpr (BSplineCore::parDim_ == 0)
4627 torch::zeros_like(BSplineCore::coeffs_[0])};
4632 template <
bool memory_optimized = false>
4634 if constexpr (BSplineCore::parDim_ == 0)
4636 torch::zeros_like(BSplineCore::coeffs_[0])};
4638 return hess<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
4678 template <
bool memory_optimized = false>
4682 if constexpr (BSplineCore::parDim_ == 0)
4684 torch::zeros_like(BSplineCore::coeffs_[0])};
4686 return hess<memory_optimized>(
4688 BSplineCore::template find_coeff_indices<memory_optimized>(
4730 template <
bool memory_optimized = false>
4733 const torch::Tensor &coeff_indices)
const {
4735 if constexpr (BSplineCore::parDim_ == 0)
4737 torch::zeros_like(BSplineCore::coeffs_[0])};
4741 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4742 assert(xi[i].sizes() == knot_indices[i].sizes());
4743 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
4744 assert(xi[0].sizes() == xi[i].sizes());
4747 auto hess_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
4749 BSplineCore::geoDim_, BSplineCore::parDim_>{
4750 BSplineCore::template eval<
4752 Is / BSplineCore::parDim_>::value +
4754 Is % BSplineCore::parDim_>::value,
4755 memory_optimized>(xi, knot_indices, coeff_indices)...}
4759 return hess_(std::make_index_sequence<BSplineCore::parDim_ *
4760 BSplineCore::parDim_>{});
4791 template <
bool memory_optimized = false,
typename Geometry>
4792 inline auto ihess(
const Geometry &G,
const torch::Tensor &xi)
const {
4793 if constexpr (BSplineCore::parDim_ == 0)
4795 torch::zeros_like(BSplineCore::coeffs_[0])};
4800 template <
bool memory_optimized = false,
typename Geometry>
4803 if constexpr (BSplineCore::parDim_ == 0)
4805 torch::zeros_like(BSplineCore::coeffs_[0])};
4807 return ihess<memory_optimized, Geometry>(
4808 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
4841 template <
bool memory_optimized = false,
typename Geometry>
4846 if constexpr (BSplineCore::parDim_ == 0)
4848 torch::zeros_like(BSplineCore::coeffs_[0])};
4850 return ihess<memory_optimized, Geometry>(
4851 G, xi, knot_indices,
4852 BSplineCore::template find_coeff_indices<memory_optimized>(
4855 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
4893 template <
bool memory_optimized = false,
typename Geometry>
4897 const torch::Tensor &coeff_indices,
4899 const torch::Tensor &coeff_indices_G)
const {
4901 if constexpr (BSplineCore::parDim_ == 0)
4903 torch::zeros_like(BSplineCore::coeffs_[0])};
4907 auto hessG = G.template hess<memory_optimized>(xi, knot_indices_G,
4909 auto ijacG = ijac<memory_optimized>(G, xi, knot_indices, coeff_indices,
4910 knot_indices_G, coeff_indices_G);
4912 for (
short_t component = 0; component < BSplineCore::geoDim_; ++component) {
4913 auto hess_component = hess<memory_optimized>(xi, knot_indices, coeff_indices).slice(component);
4915 for (
short_t k = 0; k < hessG.slices(); ++k) {
4916 hess_component -= ijacG(component, k) * hessG.slice(k);
4919 auto jacInv = G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G).ginv();
4920 auto hessu_component = jacInv.tr() * hess_component * jacInv;
4922 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
4923 for (
short_t j = 0; j < BSplineCore::parDim_; ++j)
4924 hessu.set(i, j, component, hessu_component(i, j));
4963 template <
bool memory_optimized = false>
4964 inline auto jac(
const torch::Tensor &xi)
const {
4965 if constexpr (BSplineCore::parDim_ == 0)
4967 torch::zeros_like(BSplineCore::coeffs_[0])};
4972 template <
bool memory_optimized = false>
4974 if constexpr (BSplineCore::parDim_ == 0)
4976 torch::zeros_like(BSplineCore::coeffs_[0])};
4978 return jac<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5013 template <
bool memory_optimized = false>
5017 if constexpr (BSplineCore::parDim_ == 0)
5019 torch::zeros_like(BSplineCore::coeffs_[0])};
5021 return jac<memory_optimized>(
5023 BSplineCore::template find_coeff_indices<memory_optimized>(
5066 template <
bool memory_optimized = false>
5069 const torch::Tensor &coeff_indices)
const {
5071 if constexpr (BSplineCore::parDim_ == 0)
5073 torch::zeros_like(BSplineCore::coeffs_[0])};
5077 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5078 assert(xi[i].sizes() == knot_indices[i].sizes());
5079 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5080 assert(xi[0].sizes() == xi[i].sizes());
5083 auto jac_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5085 BSplineCore::geoDim_>{
5087 memory_optimized>(xi, knot_indices,
5092 return jac_(std::make_index_sequence<BSplineCore::parDim_>{});
5117 template <
bool memory_optimized = false,
typename Geometry>
5118 inline auto ijac(
const Geometry &G,
const torch::Tensor &xi)
const {
5119 if constexpr (BSplineCore::parDim_ == 0)
5121 torch::zeros_like(BSplineCore::coeffs_[0])};
5126 template <
bool memory_optimized = false,
typename Geometry>
5127 inline auto ijac(
const Geometry &G,
5129 if constexpr (BSplineCore::parDim_ == 0)
5131 torch::zeros_like(BSplineCore::coeffs_[0])};
5133 return ijac<memory_optimized, Geometry>(
5134 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5162 template <
bool memory_optimized = false,
typename Geometry>
5167 if constexpr (BSplineCore::parDim_ == 0)
5169 torch::zeros_like(BSplineCore::coeffs_[0])};
5171 return ijac<memory_optimized, Geometry>(
5172 G, xi, knot_indices,
5173 BSplineCore::template find_coeff_indices<memory_optimized>(
5176 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5209 template <
bool memory_optimized = false,
typename Geometry>
5210 inline auto ijac(
const Geometry &G,
5213 const torch::Tensor &coeff_indices,
5215 const torch::Tensor &coeff_indices_G)
const {
5216 if constexpr (BSplineCore::parDim_ == 0)
5218 torch::zeros_like(BSplineCore::coeffs_[0])};
5220 return jac<memory_optimized>(xi, knot_indices, coeff_indices) *
5221 G.template jac<memory_optimized>(xi, knot_indices_G,
5246 template <
bool memory_optimized = false>
5247 inline auto lapl(
const torch::Tensor &xi)
const {
5248 if constexpr (BSplineCore::parDim_ == 0)
5250 torch::zeros_like(BSplineCore::coeffs_[0])};
5255 template <
bool memory_optimized = false>
5257 if constexpr (BSplineCore::parDim_ == 0)
5259 torch::zeros_like(BSplineCore::coeffs_[0])};
5261 return lapl<memory_optimized>(xi, BSplineCore::find_knot_indices(xi));
5286 template <
bool memory_optimized = false>
5290 if constexpr (BSplineCore::parDim_ == 0)
5292 torch::zeros_like(BSplineCore::coeffs_[0])};
5294 return lapl<memory_optimized>(
5296 BSplineCore::template find_coeff_indices<memory_optimized>(
5324 template <
bool memory_optimized = false>
5327 const torch::Tensor &coeff_indices)
const {
5329 if constexpr (BSplineCore::parDim_ == 0)
5331 torch::zeros_like(BSplineCore::coeffs_[0])};
5335 for (
short_t i = 0; i < BSplineCore::parDim_; ++i)
5336 assert(xi[i].sizes() == knot_indices[i].sizes());
5337 for (
short_t i = 1; i < BSplineCore::parDim_; ++i)
5338 assert(xi[0].sizes() == xi[i].sizes());
5341 auto lapl_ = [&,
this]<std::size_t... Is>(std::index_sequence<Is...>) {
5343 (BSplineCore::template eval<
5345 memory_optimized>(xi, knot_indices, coeff_indices) +
5350 return lapl_(std::make_index_sequence<BSplineCore::parDim_>{});
5382 template <
bool memory_optimized = false,
typename Geometry>
5383 auto ilapl(
const Geometry &G,
const torch::Tensor &xi)
const {
5384 if constexpr (BSplineCore::parDim_ == 0)
5386 torch::zeros_like(BSplineCore::coeffs_[0])};
5391 template <
bool memory_optimized = false,
typename Geometry>
5394 if constexpr (BSplineCore::parDim_ == 0)
5396 torch::zeros_like(BSplineCore::coeffs_[0])};
5398 return ilapl<memory_optimized, Geometry>(
5399 G, xi, BSplineCore::find_knot_indices(xi), G.find_knot_indices(xi));
5433 template <
bool memory_optimized = false,
typename Geometry>
5438 if constexpr (BSplineCore::parDim_ == 0)
5440 torch::zeros_like(BSplineCore::coeffs_[0])};
5442 return ilapl<memory_optimized, Geometry>(
5443 G, xi, knot_indices,
5444 BSplineCore::template find_coeff_indices<memory_optimized>(
5447 G.template find_coeff_indices<memory_optimized>(knot_indices_G));
5487 template <
bool memory_optimized = false,
typename Geometry>
5491 const torch::Tensor &coeff_indices,
5493 const torch::Tensor &coeff_indices_G)
const {
5495 if constexpr (BSplineCore::parDim_ == 0)
5497 torch::zeros_like(BSplineCore::coeffs_[0])};
5501 hess<memory_optimized>(xi, knot_indices, coeff_indices).slice(0);
5505 igrad<memory_optimized>(G, xi, knot_indices, coeff_indices,
5506 knot_indices_G, coeff_indices_G);
5507 auto hessG = G.template hess<memory_optimized>(xi, knot_indices_G,
5509 assert(igradG.cols() == hessG.slices());
5510 for (
short_t k = 0; k < hessG.slices(); ++k)
5511 hessu -= igradG(0, k) * hessG.slice(k);
5515 G.template jac<memory_optimized>(xi, knot_indices_G, coeff_indices_G)
5518 return (jacInv.tr() * hessu * jacInv).trace();
5527#ifdef IGANET_WITH_MATPLOT
5528 template <
typename Backend = matplot::backend::gnuplot>
5530 template <
typename Backend =
void>
5532 inline auto plot(
const nlohmann::json &json = {})
const {
5533 return plot<Backend>(*
this, json);
5543#ifdef IGANET_WITH_MATPLOT
5544 template <
typename Backend = matplot::backend::gnuplot>
5546 template <
typename Backend =
void>
5549 const nlohmann::json &json = {})
const {
5551 return plot<Backend>(*
this, xi, json);
5561#ifdef IGANET_WITH_MATPLOT
5562 template <
typename Backend = matplot::backend::gnuplot>
5564 template <
typename Backend =
void>
5568 const nlohmann::json &json = {})
const {
5570 return plot<Backend>(*
this, xi, json);
5580#ifdef IGANET_WITH_MATPLOT
5581 template <
typename Backend = matplot::backend::gnuplot,
5582 typename BSplineCoreColor>
5584 template <
typename Backend =
void,
typename BSplineCoreColor>
5587 const nlohmann::json &json = {})
const {
5588#ifdef IGANET_WITH_MATPLOT
5589 static_assert(BSplineCore::parDim() == BSplineCoreColor::parDim(),
5590 "Parametric dimensions must match");
5592 if ((
void *)
this != (
void *)&color && BSplineCoreColor::geoDim() > 1)
5593 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
5595 if constexpr (BSplineCore::parDim() == 1 && BSplineCore::geoDim() == 1) {
5601 int64_t res0 = BSplineCore::ncoeffs(0);
5602 if (json.contains(
"res0"))
5603 res0 = json[
"res0"].get<int64_t>();
5606 auto f = matplot::figure<Backend>(
true);
5607 auto ax = f->current_axes();
5611 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
5614 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5615 Coords(0), torch::kCPU);
5616 auto XAccessor = std::get<1>(Coords_cpu);
5618 auto [Coords_cpu, XAccessor] =
5619 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5620 Coords(0), torch::kCPU);
5623 matplot::vector_1d Xfine(res0, 0.0);
5624 matplot::vector_1d Yfine(res0, 0.0);
5626#pragma omp parallel for simd
5627 for (int64_t i = 0; i < res0; ++i)
5628 Xfine[i] = XAccessor[i];
5631 if ((
void *)
this != (
void *)&color) {
5632 if constexpr (BSplineCoreColor::geoDim_ == 1) {
5636 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
5640 1>(Color(0), torch::kCPU);
5641 auto CAccessor = std::get<1>(Color_cpu);
5643 auto [Color_cpu, CAccessor] =
5645 1>(Color(0), torch::kCPU);
5648 matplot::vector_1d Cfine(res0, 0.0);
5650#pragma omp parallel for simd
5651 for (int64_t i = 0; i < res0; ++i)
5652 Cfine[i] = CAccessor[i];
5654 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
5655 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
5657 auto Cmap = matplot::colormap();
5659 auto a = Cmap.size() / (Cmax - Cmin);
5663 ax->hold(matplot::on);
5664 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
5665 ax->plot({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]})
5667 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
5668 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
5669 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
5670 ax->hold(matplot::off);
5671 matplot::colorbar(ax);
5673 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
5676 ax->plot(Xfine, Yfine,
"b-")->line_width(2);
5680 if (json.contains(
"cnet"))
5681 cnet = json[
"cnet"].get<
bool>();
5687 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5688 BSplineCore::coeffs(0), torch::kCPU);
5689 auto xAccessor = std::get<1>(coeffs_cpu);
5691 auto [coeffs_cpu, xAccessor] =
5692 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5693 BSplineCore::coeffs(0), torch::kCPU);
5695 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
5696 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
5698#pragma omp parallel for simd
5699 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
5700 X[i] = xAccessor[i];
5704 ax->hold(matplot::on);
5705 ax->plot(X, Y,
".k-")->line_width(1);
5706 ax->hold(matplot::off);
5710 if (json.contains(
"title"))
5711 ax->title(json[
"title"].get<std::string>());
5713 ax->title(
"BSpline: [0,1] -> R");
5716 if (json.contains(
"xlabel"))
5717 ax->xlabel(json[
"xlabel"].get<std::string>());
5722 if (json.contains(
"ylabel"))
5723 ax->ylabel(json[
"ylabel"].get<std::string>());
5730 else if constexpr (BSplineCore::parDim_ == 1 && BSplineCore::geoDim_ == 2) {
5736 int64_t res0 = BSplineCore::ncoeffs(0);
5737 if (json.contains(
"res0"))
5738 res0 = json[
"res0"].get<int64_t>();
5741 auto f = matplot::figure<Backend>(
true);
5742 auto ax = f->current_axes();
5746 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
5749 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5750 Coords, torch::kCPU);
5751 auto XAccessor = std::get<1>(Coords_cpu)[0];
5752 auto YAccessor = std::get<1>(Coords_cpu)[1];
5754 auto [Coords0_cpu, XAccessor] =
5755 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5756 Coords(0), torch::kCPU);
5757 auto [Coords1_cpu, YAccessor] =
5758 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5759 Coords(1), torch::kCPU);
5762 matplot::vector_1d Xfine(res0, 0.0);
5763 matplot::vector_1d Yfine(res0, 0.0);
5765#pragma omp parallel for simd
5766 for (int64_t i = 0; i < res0; ++i) {
5767 Xfine[i] = XAccessor[i];
5768 Yfine[i] = YAccessor[i];
5772 if ((
void *)
this != (
void *)&color) {
5773 if constexpr (BSplineCoreColor::geoDim() == 1) {
5777 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
5781 1>(Color(0), torch::kCPU);
5782 auto CAccessor = std::get<1>(Color_cpu);
5784 auto [Color_cpu, CAccessor] =
5786 1>(Color(0), torch::kCPU);
5789 matplot::vector_1d Cfine(res0, 0.0);
5791#pragma omp parallel for simd
5792 for (int64_t i = 0; i < res0; ++i) {
5793 Cfine[i] = CAccessor[i];
5796 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
5797 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
5799 auto Cmap = matplot::colormap();
5801 auto a = Cmap.size() / (Cmax - Cmin);
5805 ax->hold(matplot::on);
5806 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
5807 ax->plot({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]})
5809 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
5810 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
5811 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
5812 ax->hold(matplot::off);
5813 matplot::colorbar(ax);
5815 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
5818 ax->plot(Xfine, Yfine,
"b-")->line_width(2);
5822 if (json.contains(
"cnet"))
5823 cnet = json[
"cnet"].get<
bool>();
5829 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5830 BSplineCore::coeffs(), torch::kCPU);
5831 auto xAccessor = std::get<1>(coeffs_cpu)[0];
5832 auto yAccessor = std::get<1>(coeffs_cpu)[1];
5834 auto [coeffs0_cpu, xAccessor] =
5835 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5836 BSplineCore::coeffs(0), torch::kCPU);
5837 auto [coeffs1_cpu, yAccessor] =
5838 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5839 BSplineCore::coeffs(1), torch::kCPU);
5842 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
5843 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
5845#pragma omp parallel for simd
5846 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
5847 X[i] = xAccessor[i];
5848 Y[i] = yAccessor[i];
5852 ax->hold(matplot::on);
5853 ax->plot(X, Y,
".k-")->line_width(1);
5854 ax->hold(matplot::off);
5858 if (json.contains(
"title"))
5859 ax->title(json[
"title"].get<std::string>());
5861 ax->title(
"BSpline: [0,1] -> R^2");
5864 if (json.contains(
"xlabel"))
5865 ax->xlabel(json[
"xlabel"].get<std::string>());
5870 if (json.contains(
"ylabel"))
5871 ax->ylabel(json[
"ylabel"].get<std::string>());
5878 else if constexpr (BSplineCore::parDim() == 1 &&
5879 BSplineCore::geoDim() == 3) {
5885 int64_t res0 = BSplineCore::ncoeffs(0);
5886 if (json.contains(
"res0"))
5887 res0 = json[
"res0"].get<int64_t>();
5890 auto f = matplot::figure<Backend>(
true);
5891 auto ax = f->current_axes();
5894 BSplineCore::eval(torch::linspace(0, 1, res0, BSplineCore::options_));
5897 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5898 Coords, torch::kCPU);
5899 auto XAccessor = std::get<1>(Coords_cpu)[0];
5900 auto YAccessor = std::get<1>(Coords_cpu)[1];
5901 auto ZAccessor = std::get<1>(Coords_cpu)[2];
5903 auto [Coords0_cpu, XAccessor] =
5904 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5905 Coords(0), torch::kCPU);
5906 auto [Coords1_cpu, YAccessor] =
5907 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5908 Coords(1), torch::kCPU);
5909 auto [Coords2_cpu, ZAccessor] =
5910 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5911 Coords(2), torch::kCPU);
5915 matplot::vector_1d Xfine(res0, 0.0);
5916 matplot::vector_1d Yfine(res0, 0.0);
5917 matplot::vector_1d Zfine(res0, 0.0);
5919#pragma omp parallel for simd
5920 for (int64_t i = 0; i < res0; ++i) {
5921 Xfine[i] = XAccessor[i];
5922 Yfine[i] = YAccessor[i];
5923 Zfine[i] = ZAccessor[i];
5927 if ((
void *)
this != (
void *)&color) {
5928 if constexpr (BSplineCoreColor::geoDim() == 1) {
5931 color.eval(torch::linspace(0, 1, res0, BSplineCore::options_));
5935 1>(Color(0), torch::kCPU);
5936 auto CAccessor = std::get<1>(Color_cpu);
5938 auto [Color_cpu, CAccessor] =
5940 1>(Color(0), torch::kCPU);
5944 matplot::vector_1d Cfine(matplot::vector_1d(res0, 0.0));
5946#pragma omp parallel for simd
5947 for (int64_t i = 0; i < res0; ++i) {
5948 Cfine[i] = CAccessor[i];
5951 auto Cmin = *std::min_element(Cfine.begin(), Cfine.end());
5952 auto Cmax = *std::max_element(Cfine.begin(), Cfine.end());
5954 auto Cmap = matplot::colormap();
5956 auto a = Cmap.size() / (Cmax - Cmin);
5960 ax->hold(matplot::on);
5961 for (std::size_t i = 0; i < Xfine.size() - 1; ++i)
5962 ax->plot3({Xfine[i], Xfine[i + 1]}, {Yfine[i], Yfine[i + 1]},
5963 {Zfine[i], Zfine[i + 1]})
5965 .color({Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][0],
5966 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][1],
5967 Cmap[a * (Cfine[i] + Cfine[i + 1]) / 2.0 - b][2]});
5968 ax->hold(matplot::off);
5969 matplot::colorbar(ax);
5971 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
5974 ax->plot3(Xfine, Yfine, Zfine,
"b-")->line_width(2);
5978 if (json.contains(
"cnet"))
5979 cnet = json[
"cnet"].get<
bool>();
5985 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5986 BSplineCore::coeffs(), torch::kCPU);
5987 auto xAccessor = std::get<1>(coeffs_cpu)[0];
5988 auto yAccessor = std::get<1>(coeffs_cpu)[1];
5989 auto zAccessor = std::get<1>(coeffs_cpu)[2];
5991 auto [coeffs0_cpu, xAccessor] =
5992 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5993 BSplineCore::coeffs(0), torch::kCPU);
5994 auto [coeffs1_cpu, yAccessor] =
5995 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5996 BSplineCore::coeffs(1), torch::kCPU);
5997 auto [coeffs2_cpu, zAccessor] =
5998 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
5999 BSplineCore::coeffs(2), torch::kCPU);
6002 matplot::vector_1d X(BSplineCore::ncoeffs(0), 0.0);
6003 matplot::vector_1d Y(BSplineCore::ncoeffs(0), 0.0);
6004 matplot::vector_1d Z(BSplineCore::ncoeffs(0), 0.0);
6006#pragma omp parallel for simd
6007 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i) {
6008 X[i] = xAccessor[i];
6009 Y[i] = yAccessor[i];
6010 Z[i] = zAccessor[i];
6014 ax->hold(matplot::on);
6015 ax->plot3(X, Y, Z,
".k-")->line_width(1);
6016 ax->hold(matplot::off);
6020 if (json.contains(
"title"))
6021 ax->title(json[
"title"].get<std::string>());
6023 ax->title(
"BSpline: [0,1] -> R^3");
6026 if (json.contains(
"xlabel"))
6027 ax->xlabel(json[
"xlabel"].get<std::string>());
6032 if (json.contains(
"ylabel"))
6033 ax->ylabel(json[
"ylabel"].get<std::string>());
6038 if (json.contains(
"zlabel"))
6039 ax->zlabel(json[
"zlabel"].get<std::string>());
6046 else if constexpr (BSplineCore::parDim() == 2 &&
6047 BSplineCore::geoDim() == 2) {
6053 int64_t res0 = BSplineCore::ncoeffs(0);
6054 int64_t res1 = BSplineCore::ncoeffs(1);
6055 if (json.contains(
"res0"))
6056 res0 = json[
"res0"].get<int64_t>();
6057 if (json.contains(
"res1"))
6058 res1 = json[
"res1"].get<int64_t>();
6061 auto f = matplot::figure<Backend>(
true);
6062 auto ax = f->current_axes();
6065 utils::TensorArray<2> meshgrid = utils::to_array<2>(
6066 torch::meshgrid({torch::linspace(0, 1, res0, BSplineCore::options_),
6067 torch::linspace(0, 1, res1, BSplineCore::options_)},
6069 auto Coords = BSplineCore::eval(meshgrid);
6072 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6073 Coords, torch::kCPU);
6074 auto XAccessor = std::get<1>(Coords_cpu)[0];
6075 auto YAccessor = std::get<1>(Coords_cpu)[1];
6077 auto [Coords0_cpu, XAccessor] =
6078 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6079 Coords(0), torch::kCPU);
6080 auto [Coords1_cpu, YAccessor] =
6081 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6082 Coords(1), torch::kCPU);
6085 matplot::vector_2d Xfine(res1, matplot::vector_1d(res0, 0.0));
6086 matplot::vector_2d Yfine(res1, matplot::vector_1d(res0, 0.0));
6087 matplot::vector_2d Zfine(res1, matplot::vector_1d(res0, 0.0));
6089#pragma omp parallel for simd collapse(2)
6090 for (int64_t i = 0; i < res0; ++i)
6091 for (int64_t j = 0; j < res1; ++j) {
6092 Xfine[j][i] = XAccessor[j][i];
6093 Yfine[j][i] = YAccessor[j][i];
6097 if ((
void *)
this != (
void *)&color) {
6098 if constexpr (BSplineCoreColor::geoDim() == 1) {
6101 auto Color = color.eval(meshgrid);
6104 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6105 2>(Color, torch::kCPU);
6106 auto CAccessor = std::get<1>(Color_cpu)[0];
6108 auto [Color0_cpu, CAccessor] =
6109 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6110 2>(Color(0), torch::kCPU);
6113 matplot::vector_2d Cfine(res1, matplot::vector_1d(res0, 0.0));
6115#pragma omp parallel for simd collapse(2)
6116 for (int64_t i = 0; i < res0; ++i)
6117 for (int64_t j = 0; j < res1; ++j)
6118 Cfine[j][i] = CAccessor[j][i];
6122 ax->mesh(Xfine, Yfine, Cfine)->hidden_3d(
false);
6123 matplot::colorbar(ax);
6125 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6129 matplot::colormap(std::vector<std::vector<double>>{{ 0.0, 0.0, 1.0 }});
6130 ax->mesh(Xfine, Yfine, Zfine)->hidden_3d(
false).line_width(2);
6134 if (json.contains(
"cnet"))
6135 cnet = json[
"cnet"].get<
bool>();
6141 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6142 BSplineCore::coeffs(), torch::kCPU);
6143 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6144 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6146 auto [coeffs0_cpu, xAccessor] =
6147 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6148 BSplineCore::coeffs(0), torch::kCPU);
6149 auto [coeffs1_cpu, yAccessor] =
6150 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6151 BSplineCore::coeffs(1), torch::kCPU);
6154 matplot::vector_2d X(BSplineCore::ncoeffs(1),
6155 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6156 matplot::vector_2d Y(BSplineCore::ncoeffs(1),
6157 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6158 matplot::vector_2d Z(BSplineCore::ncoeffs(1),
6159 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6161#pragma omp parallel for simd collapse(2)
6162 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i)
6163 for (int64_t j = 0; j < BSplineCore::ncoeffs(1); ++j) {
6164 X[j][i] = xAccessor[j * BSplineCore::ncoeffs(0) + i];
6165 Y[j][i] = yAccessor[j * BSplineCore::ncoeffs(0) + i];
6169 ax->hold(matplot::on);
6171 ->palette_map_at_surface(
true)
6174 for (std::size_t i = 0; i < X.size(); ++i)
6175 ax->scatter3(X[i], Y[i], Z[i],
"k.");
6176 ax->hold(matplot::off);
6180 if (json.contains(
"title"))
6181 ax->title(json[
"title"].get<std::string>());
6183 ax->title(
"BSpline: [0,1]^2 -> R^2");
6186 if (json.contains(
"xlabel"))
6187 ax->xlabel(json[
"xlabel"].get<std::string>());
6192 if (json.contains(
"ylabel"))
6193 ax->ylabel(json[
"ylabel"].get<std::string>());
6198 if (json.contains(
"zlabel"))
6199 ax->zlabel(json[
"zlabel"].get<std::string>());
6206 else if constexpr (BSplineCore::parDim() == 2 &&
6207 BSplineCore::geoDim() == 3) {
6213 int64_t res0 = BSplineCore::ncoeffs(0);
6214 int64_t res1 = BSplineCore::ncoeffs(1);
6215 if (json.contains(
"res0"))
6216 res0 = json[
"res0"].get<int64_t>();
6217 if (json.contains(
"res1"))
6218 res1 = json[
"res1"].get<int64_t>();
6221 auto f = matplot::figure<Backend>(
true);
6222 auto ax = f->current_axes();
6225 utils::TensorArray<2> meshgrid = utils::to_array<2>(
6226 torch::meshgrid({torch::linspace(0, 1, res0, BSplineCore::options_),
6227 torch::linspace(0, 1, res1, BSplineCore::options_)},
6229 auto Coords = BSplineCore::eval(meshgrid);
6232 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6233 Coords, torch::kCPU);
6234 auto XAccessor = std::get<1>(Coords_cpu)[0];
6235 auto YAccessor = std::get<1>(Coords_cpu)[1];
6236 auto ZAccessor = std::get<1>(Coords_cpu)[2];
6238 auto [Coords0_cpu, XAccessor] =
6239 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6240 Coords(0), torch::kCPU);
6241 auto [Coords1_cpu, YAccessor] =
6242 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6243 Coords(1), torch::kCPU);
6244 auto [Coords2_cpu, ZAccessor] =
6245 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 2>(
6246 Coords(2), torch::kCPU);
6249 matplot::vector_2d Xfine(res1, matplot::vector_1d(res0, 0.0));
6250 matplot::vector_2d Yfine(res1, matplot::vector_1d(res0, 0.0));
6251 matplot::vector_2d Zfine(res1, matplot::vector_1d(res0, 0.0));
6253#pragma omp parallel for simd collapse(2)
6254 for (int64_t i = 0; i < res0; ++i)
6255 for (int64_t j = 0; j < res1; ++j) {
6256 Xfine[j][i] = XAccessor[j][i];
6257 Yfine[j][i] = YAccessor[j][i];
6258 Zfine[j][i] = ZAccessor[j][i];
6262 if ((
void *)
this != (
void *)&color) {
6263 if constexpr (BSplineCoreColor::geoDim() == 1) {
6266 auto Color = color.eval(meshgrid);
6269 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6270 2>(Color, torch::kCPU);
6271 auto CAccessor = std::get<1>(Color_cpu)[0];
6273 auto [Color_cpu, CAccessor] =
6274 utils::to_tensorAccessor<
typename BSplineCoreColor::value_type,
6275 2>(Color(0), torch::kCPU);
6278 matplot::vector_2d Cfine(res1, matplot::vector_1d(res0, 0.0));
6280#pragma omp parallel for simd collapse(2)
6281 for (int64_t i = 0; i < res0; ++i)
6282 for (int64_t j = 0; j < res1; ++j) {
6283 Cfine[j][i] = CAccessor[j][i];
6287 ax->mesh(Xfine, Yfine, Zfine, Cfine)->hidden_3d(
false);
6288 matplot::colorbar(ax);
6290 throw std::runtime_error(
"BSpline for coloring must have geoDim=1");
6293 matplot::colormap(std::vector<std::vector<double>>{{ 0.0, 0.0, 1.0 }});
6294 ax->mesh(Xfine, Yfine, Zfine)->hidden_3d(
false).line_width(2);
6298 if (json.contains(
"cnet"))
6299 cnet = json[
"cnet"].get<
bool>();
6305 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6306 BSplineCore::coeffs(), torch::kCPU);
6307 auto xAccessor = std::get<1>(coeffs_cpu)[0];
6308 auto yAccessor = std::get<1>(coeffs_cpu)[1];
6309 auto zAccessor = std::get<1>(coeffs_cpu)[2];
6311 auto [coeffs0_cpu, xAccessor] =
6312 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6313 BSplineCore::coeffs(0), torch::kCPU);
6314 auto [coeffs1_cpu, yAccessor] =
6315 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6316 BSplineCore::coeffs(1), torch::kCPU);
6317 auto [coeffs2_cpu, zAccessor] =
6318 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6319 BSplineCore::coeffs(2), torch::kCPU);
6322 matplot::vector_2d X(BSplineCore::ncoeffs(1),
6323 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6324 matplot::vector_2d Y(BSplineCore::ncoeffs(1),
6325 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6326 matplot::vector_2d Z(BSplineCore::ncoeffs(1),
6327 matplot::vector_1d(BSplineCore::ncoeffs(0), 0.0));
6329#pragma omp parallel for simd collapse(2)
6330 for (int64_t i = 0; i < BSplineCore::ncoeffs(0); ++i)
6331 for (int64_t j = 0; j < BSplineCore::ncoeffs(1); ++j) {
6332 X[j][i] = xAccessor[j * BSplineCore::ncoeffs(0) + i];
6333 Y[j][i] = yAccessor[j * BSplineCore::ncoeffs(0) + i];
6334 Z[j][i] = zAccessor[j * BSplineCore::ncoeffs(0) + i];
6338 ax->hold(matplot::on);
6340 ->palette_map_at_surface(
true)
6343 for (std::size_t i = 0; i < X.size(); ++i)
6344 ax->scatter3(X[i], Y[i], Z[i],
"k.");
6345 ax->hold(matplot::off);
6349 if (json.contains(
"title"))
6350 ax->title(json[
"title"].get<std::string>());
6352 ax->title(
"BSpline: [0,1]^2 -> R^3");
6355 if (json.contains(
"xlabel"))
6356 ax->xlabel(json[
"xlabel"].get<std::string>());
6361 if (json.contains(
"ylabel"))
6362 ax->ylabel(json[
"ylabel"].get<std::string>());
6367 if (json.contains(
"zlabel"))
6368 ax->zlabel(json[
"zlabel"].get<std::string>());
6376 throw std::runtime_error(
6377 "Unsupported combination of parametric/geometric dimensions");
6379 throw std::runtime_error(
6380 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
6394#ifdef IGANET_WITH_MATPLOT
6395 template <
typename Backend = matplot::backend::gnuplot,
6396 typename BSplineCoreColor>
6398 template <
typename Backend =
void,
typename BSplineCoreColor>
6402 const nlohmann::json &json = {})
const {
6404#ifdef IGANET_WITH_MATPLOT
6405 auto f = plot<Backend>(color, json);
6406 auto ax = f->current_axes();
6410 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6412 auto xiAccessor = std::get<1>(xi_cpu);
6414 auto [xi_cpu, xiAccessor] =
6415 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6419 if constexpr (BSplineCore::parDim_ == 1) {
6420 matplot::vector_1d X(xi[0].size(0), 0.0);
6421 matplot::vector_1d Y(xi[0].size(0), 0.0);
6423#pragma omp parallel for simd
6424 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6425 X[i] = xiAccessor[0][i];
6428 ax->hold(matplot::on);
6429 ax->scatter(X, Y,
".");
6430 ax->hold(matplot::off);
6431 }
else if constexpr (BSplineCore::parDim_ == 2) {
6432 matplot::vector_1d X(xi[0].size(0), 0.0);
6433 matplot::vector_1d Y(xi[0].size(0), 0.0);
6434 matplot::vector_1d Z(xi[0].size(0), 0.0);
6436#pragma omp parallel for simd
6437 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6438 X[i] = xiAccessor[0][i];
6439 Y[i] = xiAccessor[1][i];
6442 ax->hold(matplot::on);
6443 ax->scatter3(X, Y, Z,
".");
6444 ax->hold(matplot::off);
6445 }
else if constexpr (BSplineCore::parDim_ == 3) {
6446 matplot::vector_1d X(xi[0].size(0), 0.0);
6447 matplot::vector_1d Y(xi[0].size(0), 0.0);
6448 matplot::vector_1d Z(xi[0].size(0), 0.0);
6450#pragma omp parallel for simd
6451 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6452 X[i] = xiAccessor[0][i];
6453 Y[i] = xiAccessor[1][i];
6454 Z[i] = xiAccessor[2][i];
6457 ax->hold(matplot::on);
6458 ax->scatter3(X, Y, Z,
".");
6459 ax->hold(matplot::off);
6461 throw std::runtime_error(
"Invalid parametric dimension");
6465 throw std::runtime_error(
6466 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
6480#ifdef IGANET_WITH_MATPLOT
6481 template <
typename Backend = matplot::backend::gnuplot,
6482 typename BSplineCoreColor>
6484 template <
typename Backend =
void,
typename BSplineCoreColor>
6489 const nlohmann::json &json = {})
const {
6491#ifdef IGANET_WITH_MATPLOT
6492 auto f = plot<Backend>(color, json);
6493 auto ax = f->current_axes();
6495 for (
const auto &xi : xi) {
6498 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6500 auto xiAccessor = std::get<1>(xi_cpu);
6502 auto [xi_cpu, xiAccessor] =
6503 utils::to_tensorAccessor<typename BSplineCoreColor::value_type, 1>(
6507 if constexpr (BSplineCore::parDim_ == 1) {
6508 matplot::vector_1d X(xi[0].size(0), 0.0);
6509 matplot::vector_1d Y(xi[0].size(0), 0.0);
6511#pragma omp parallel for simd
6512 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6513 X[i] = xiAccessor[0][i];
6516 ax->hold(matplot::on);
6517 ax->scatter(X, Y,
".");
6518 ax->hold(matplot::off);
6519 }
else if constexpr (BSplineCore::parDim_ == 2) {
6520 matplot::vector_1d X(xi[0].size(0), 0.0);
6521 matplot::vector_1d Y(xi[0].size(0), 0.0);
6522 matplot::vector_1d Z(xi[0].size(0), 0.0);
6524#pragma omp parallel for simd
6525 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6526 X[i] = xiAccessor[0][i];
6527 Y[i] = xiAccessor[1][i];
6530 ax->hold(matplot::on);
6531 ax->scatter3(X, Y, Z,
".");
6532 ax->hold(matplot::off);
6533 }
else if constexpr (BSplineCore::parDim_ == 3) {
6534 matplot::vector_1d X(xi[0].size(0), 0.0);
6535 matplot::vector_1d Y(xi[0].size(0), 0.0);
6536 matplot::vector_1d Z(xi[0].size(0), 0.0);
6538#pragma omp parallel for simd
6539 for (int64_t i = 0; i < xi[0].size(0); ++i) {
6540 X[i] = xiAccessor[0][i];
6541 Y[i] = xiAccessor[1][i];
6542 Z[i] = xiAccessor[2][i];
6545 ax->hold(matplot::on);
6546 ax->scatter3(X, Y, Z,
".");
6547 ax->hold(matplot::off);
6550 throw std::runtime_error(
"Invalid parametric dimension");
6554 throw std::runtime_error(
6555 "This functions must be compiled with -DIGANET_WITH_MATPLOT turned on");
6562 os << name() <<
"(\nparDim = " << BSplineCore::parDim()
6563 <<
", geoDim = " << BSplineCore::geoDim() <<
", degrees = ";
6565 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
6566 os << BSplineCore::degree(i) <<
"x";
6567 if (BSplineCore::parDim() > 0)
6568 os << BSplineCore::degree(BSplineCore::parDim() - 1);
6573 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
6574 os << BSplineCore::nknots(i) <<
"x";
6575 if (BSplineCore::parDim() > 0)
6576 os << BSplineCore::nknots(BSplineCore::parDim() - 1);
6580 os <<
", coeffs = ";
6581 for (
short_t i = 0; i < BSplineCore::parDim() - 1; ++i)
6582 os << BSplineCore::ncoeffs(i) <<
"x";
6583 if (BSplineCore::parDim() > 0)
6584 os << BSplineCore::ncoeffs(BSplineCore::parDim() - 1);
6588 os <<
", options = "
6589 <<
static_cast<torch::TensorOptions
>(BSplineCore::options_);
6593 for (
const torch::Tensor &knots : BSplineCore::knots()) {
6594 os << (knots.is_view() ?
"view/" :
"owns/");
6595 os << (knots.is_contiguous() ?
"cont " :
"non-cont ");
6597 if (BSplineCore::parDim() > 0)
6598 os <<
"] = " << BSplineCore::knots();
6602 os <<
"\ncoeffs [ ";
6603 for (
const torch::Tensor &coeffs : BSplineCore::coeffs()) {
6604 os << (coeffs.is_view() ?
"view/" :
"owns/");
6605 os << (coeffs.is_contiguous() ?
"cont " :
"non-cont ");
6607 if (BSplineCore::ncumcoeffs() > 0)
6608 os <<
"] = " << BSplineCore::coeffs_view();
6627 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6628 result.coeffs(i) += other.coeffs(i);
6645 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6646 result.coeffs(i) -= other.coeffs(i);
6657 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6658 result.coeffs(i) *= s;
6666 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
6671 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6672 result.coeffs(i) *= v[i];
6683 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6684 result.coeffs(i) /= s;
6692 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v)
6697 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6698 result.coeffs(i) /= v[i];
6711 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6712 BSplineCore::coeffs(i) += other.coeffs(i);
6725 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6726 BSplineCore::coeffs(i) -= other.coeffs(i);
6734 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6735 BSplineCore::coeffs(i) *= s;
6742 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
6744 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6745 BSplineCore::coeffs(i) *= v[i];
6753 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6754 BSplineCore::coeffs(i) /= s;
6761 std::array<
typename BSplineCore::value_type, BSplineCore::geoDim()> v) {
6763 for (
short_t i = 0; i < BSplineCore::geoDim(); ++i)
6764 BSplineCore::coeffs(i) /= v[i];
6771template <
typename real_t, short_t GeoDim, short_t... Degrees>
6777inline std::ostream &
6785template <
typename real_t, short_t GeoDim, short_t... Degrees>
6791inline std::ostream &
Compile-time block tensor.
B-spline (common high-level functionality)
Definition bspline.hpp:3288
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:5164
auto scale(typename BSplineCore::value_type s, int dim=-1)
Scales the B-spline object by a scalar.
Definition bspline.hpp:3596
auto translate(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Translates the B-spline object by a vector.
Definition bspline.hpp:3614
BSplineCommon operator/(typename BSplineCore::value_type s) const
Returns a new B-spline object whose coefficients are scaled by a scalar.
Definition bspline.hpp:6679
auto diff(const BSplineCommon &other, int dim=-1)
Computes the difference between two compatible B-spline objects.
Definition bspline.hpp:3543
auto scale(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the B-spline object by a vector.
Definition bspline.hpp:3607
BSplineCommon & operator*=(typename BSplineCore::value_type s)
Scales the coefficients by a scalar.
Definition bspline.hpp:6732
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:3985
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:4801
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:3866
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:3795
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:4895
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:5392
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:4792
BSplineCommon & operator*=(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the coefficients by a vector.
Definition bspline.hpp:6741
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:4216
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:4428
auto plot(const BSplineCommon< BSplineCoreColor > &color, const std::initializer_list< utils::TensorArray< BSplineCore::parDim_ > > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:6486
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:4083
auto rotate(std::array< typename BSplineCore::value_type, 3 > angle)
Rotates the B-spline object by three angles in 3d.
Definition bspline.hpp:3638
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:6665
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:3718
auto rotate(typename BSplineCore::value_type angle)
Rotates the B-spline object by an angle in 2d.
Definition bspline.hpp:3622
BSplineCommon & operator/=(std::array< typename BSplineCore::value_type, BSplineCore::geoDim()> v)
Scales the coefficients by a vector.
Definition bspline.hpp:6760
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:5325
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:5118
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:4032
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:5210
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:5015
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:5067
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:4633
std::unique_ptr< BSplineCommon > uPtr
Unique pointer for BSplineCommon.
Definition bspline.hpp:3327
BSplineCommon(const BSplineCommon &other, bool clone)
Copy/clone constructor.
Definition bspline.hpp:3333
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:4254
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:3372
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:5256
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:5435
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:3450
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:4338
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:4301
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:3800
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:4624
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:4489
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:3389
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:3738
auto abs_diff(const BSplineCommon &other, int dim=-1)
Computes the absolute difference between two compatible B-spline objects.
Definition bspline.hpp:3571
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:4572
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:4152
std::shared_ptr< BSplineCommon > Ptr
Shared pointer for BSplineCommon.
Definition bspline.hpp:3324
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:4731
auto plot(const BSplineCommon< BSplineCoreColor > &color, const utils::TensorArray< BSplineCore::parDim_ > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:6400
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:3380
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:3442
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:4680
auto plot(const utils::TensorArray< BSplineCore::parDim_ > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:5548
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:3406
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:3831
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:3366
BSplineCommon(BSplineCommon &&)=default
Move constructor.
auto plot(const nlohmann::json &json={}) const
Definition bspline.hpp:5532
BSplineCommon(BSplineCommon &&other, utils::TensorArray< BSplineCore::geoDim_ > &&coeffs)
Move constructor with external coefficients.
Definition bspline.hpp:3356
BSplineCommon & operator+=(const BSplineCommon &other)
Adds the coefficients of another B-spline object.
Definition bspline.hpp:6709
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:5383
auto plot(const BSplineCommon< BSplineCoreColor > &color, const nlohmann::json &json={}) const
Definition bspline.hpp:5586
auto to() const
Returns a copy of the B-spline object with real_t type.
Definition bspline.hpp:3533
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:3698
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:6623
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:3940
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:4843
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:4207
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:5127
auto boundingBox() const
Computes the bounding box of the B-spline object.
Definition bspline.hpp:3672
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:3425
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:4075
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:3949
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:4387
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:4480
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:4973
BSplineCommon & operator/=(typename BSplineCore::value_type s)
Scales the coefficients by a scalar.
Definition bspline.hpp:6751
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:3433
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:3397
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:3703
BSplineCommon & operator-=(const BSplineCommon &other)
Substracts the coefficients of another B-spline object.
Definition bspline.hpp:6723
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:6641
BSplineCommon(const BSplineCommon &other, const utils::TensorArray< BSplineCore::geoDim_ > &coeffs, bool clone=false)
Copy constructor with external coefficients.
Definition bspline.hpp:3340
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:5489
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:4351
auto to(torch::Device device) const
Returns a copy of the B-spline object with settings from device.
Definition bspline.hpp:3516
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:4525
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:5247
auto clone() const
Returns a clone of the B-spline object.
Definition bspline.hpp:3481
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:6691
virtual void pretty_print(std::ostream &os=Log(log::info)) const noexcept override
Returns a string representation of the BSplineCommon object.
Definition bspline.hpp:6561
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:3419
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:4964
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:3459
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:5288
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:4115
BSplineCommon & uniform_refine(int numRefine=1, int dim=-1)
Returns the B-spline object with uniformly refined knot and coefficient vectors.
Definition bspline.hpp:3475
auto plot(const std::initializer_list< utils::TensorArray< BSplineCore::parDim_ > > &xi, const nlohmann::json &json={}) const
Definition bspline.hpp:5566
auto to(Options< real_t > options) const
Returns a copy of the B-spline object with settings from options.
Definition bspline.hpp:3498
BSplineCommon operator*(typename BSplineCore::value_type s) const
Returns a new B-spline object whose coefficients are scaled by a scalar.
Definition bspline.hpp:6653
Abstract patch function base class.
Definition patch.hpp:25
The Options class handles the automated determination of dtype from the template argument and the sel...
Definition options.hpp:107
Spline base class.
Definition bspline.hpp:3255
SplineCore base class.
Definition bspline.hpp:108
Full qualified name descriptor.
Definition fqn.hpp:26
Concept to identify template parameters that are derived from iganet::SplineCore_.
Definition bspline.hpp:118
Concept to identify template parameters that are derived from iganet::Spline_.
Definition bspline.hpp:3259
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:210
std::array< torch::Tensor, N > TensorArray
Definition tensorarray.hpp:28
auto to_tensor(const std::array< T, N > &array, torch::IntArrayRef sizes=torch::IntArrayRef{-1}, const iganet::Options< T > &options=iganet::Options< T >{})
Converts an std::array to torch::Tensor.
Definition container.hpp:60
auto kronproduct(T0 &&t0, T1 &&t1)
Computes the directional Kronecker-product between two tensors along the given dimension.
Definition linalg.hpp:61
TensorArray< 1 > TensorArray1
Definition tensorarray.hpp:31
auto to_tensorAccessor(const torch::Tensor &tensor)
Converts a torch::Tensor object to a torch::TensorAccessor object.
Definition tensorarray.hpp:80
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:221
constexpr std::array< T, N - M > remove_from_back(std::array< T, N > array)
Derives an std::array object from a given std::array object dropping the last M entries.
Definition container.hpp:376
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:47
auto dotproduct(T0 &&t0, T1 &&t1)
Computes the directional dot-product between two tensors with summation along the given dimension.
Definition linalg.hpp:37
auto to_ArrayRef(const std::array< T, N > &array)
Converts an std::array<int64_t, N> to a at::IntArrayRef object.
Definition container.hpp:192
Forward declaration of BlockTensor.
Definition blocktensor.hpp:46
Definition boundary.hpp:22
deriv
Enumerator for specifying the derivative of B-spline evaluation.
Definition bspline.hpp:74
constexpr auto operator+(deriv lhs, deriv rhs)
Adds two enumerators for specifying the derivative of B-spline evaluation.
Definition bspline.hpp:91
bool is_verbose(std::ostream &os)
Definition core.hpp:831
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:103
struct iganet::@0 Log
Logger.
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:2730
@ none
Definition boundary.hpp:38
short int short_t
Definition core.hpp:74
std::ostream & operator<<(std::ostream &os, const Boundary< Spline > &obj)
Print (as string) a Boundary object.
Definition boundary.hpp:1963
Serialization utility functions.
Serialization prototype.
Definition serialize.hpp:31
Computes the power of integer E to the N at compile time.
Definition integer_pow.hpp:22
Reverse index sequence.
Definition index_sequence.hpp:37
TensorArray utility functions.
#define TENSORARRAY_FORALL(obj, func,...)
Definition tensorarray.hpp:159
VSlice utility functions.