diff --git a/.gitignore b/.gitignore index c3b8541ec..3e7daa3c2 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,9 @@ joss/paper.pdf *.egg-info _skbuild dist + +*.png +*.jpg +*.svg +!joss/img/*.png +!joss/img/*.svg diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 516cf313f..832a56c04 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -26,7 +26,7 @@ add_feature_info(BUILD_SHARED_LIBS BUILD_SHARED_LIBS "Build Basix with shared li option(DOWNLOAD_XTENSOR_LIBS "Download xtl and xtensor. Requires git." OFF) add_feature_info(DOWNLOAD_XTENSOR_LIBS DOWNLOAD_XTENSOR_LIBS "Download xtl and xtensor. Requires git.") -# Enable xtensor with target-specific optimization, i.e. -march=native +# Enable xtensor with target-specific optimization, ie -march=native option(XTENSOR_OPTIMIZE "Enable xtensor target-specific optimization" OFF) add_feature_info(XTENSOR_OPTIMIZE XTENSOR_OPTIMIZE "Enable architecture-specific optimizations as defined by xtensor.") diff --git a/cpp/basix/finite-element.h b/cpp/basix/finite-element.h index 19ea6bbfd..90bddf530 100644 --- a/cpp/basix/finite-element.h +++ b/cpp/basix/finite-element.h @@ -245,7 +245,7 @@ class FiniteElement FiniteElement& operator=(FiniteElement&& element) = default; /// Check if two elements are the same - /// @note This operator compares the element properties, e.g. family, + /// @note This operator compares the element properties, eg family, /// degree, etc, and not computed numerical data /// @return True if elements are the same bool operator==(const FiniteElement& e) const; @@ -275,7 +275,7 @@ class FiniteElement /// @return The basis functions (and derivatives). The shape is /// (derivative, point, basis fn index, value index). /// - The first index is the derivative, with higher derivatives are - /// stored in triangular (2D) or tetrahedral (3D) ordering, i.e. for + /// stored in triangular (2D) or tetrahedral (3D) ordering, ie for /// the (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), /// (0,2), (3,0)... The function basix::indexing::idx can be used to find the /// appropriate derivative. @@ -299,7 +299,7 @@ class FiniteElement /// value_size). The function `FiniteElement::tabulate_shape` can be /// used to get the required shape. /// - The first index is the derivative, with higher derivatives are - /// stored in triangular (2D) or tetrahedral (3D) ordering, i.e. for + /// stored in triangular (2D) or tetrahedral (3D) ordering, ie for /// the (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), /// (0,2), (3,0)... The function basix::indexing::idx can be used to /// find the appropriate derivative. @@ -321,7 +321,7 @@ class FiniteElement /// @return Polynomial degree int degree() const; - /// The element value tensor shape, e.g. returning {} for scalars, {3} + /// The element value tensor shape, eg returning {} for scalars, {3} /// for vectors in 3D, {2, 2} for a rank-2 tensor in 2D. /// @return Value shape const std::vector& value_shape() const; @@ -693,7 +693,7 @@ class FiniteElement void apply_inverse_dof_transformation_to_transpose( const xtl::span& data, int block_size, std::uint32_t cell_info) const; - /// Return the interpolation points, i.e. the coordinates on the + /// Return the interpolation points, ie the coordinates on the /// reference element where a function need to be evaluated in order /// to interpolate it in the finite element space. /// @return Array of coordinate with shape `(num_points, tdim)` @@ -895,7 +895,7 @@ class FiniteElement // Shape function coefficient of expansion sets on cell. If shape // function is given by @f$\psi_i = \sum_{k} \phi_{k} - // \alpha^{i}_{k}@f$, then _coeffs(i, j) = @f$\alpha^i_k@f$. i.e., + // \alpha^{i}_{k}@f$, then _coeffs(i, j) = @f$\alpha^i_k@f$. ie // _coeffs.row(i) are the expansion coefficients for shape function i // (@f$\psi_{i}@f$). xt::xtensor _coeffs; diff --git a/cpp/basix/lattice.h b/cpp/basix/lattice.h index d25b84a52..6998a7656 100644 --- a/cpp/basix/lattice.h +++ b/cpp/basix/lattice.h @@ -67,7 +67,7 @@ enum class simplex_method /// optionally including the outer surface points /// /// For a given celltype, this creates a set of points on a regular grid -/// which covers the cell, e.g. for a quadrilateral, with n=2, the points are: +/// which covers the cell, eg for a quadrilateral, with n=2, the points are: /// [0,0],[0.5,0],[1,0],[0,0.5],[0.5,0.5],[1,0.5],[0,1],[0.5,1],[1,1] /// If the parameter exterior is set to false, the points lying on the external /// boundary are omitted, in this case for a quadrilateral with n=2, the points diff --git a/cpp/basix/loguru.cpp b/cpp/basix/loguru.cpp index 8f1749cd1..e107f9229 100644 --- a/cpp/basix/loguru.cpp +++ b/cpp/basix/loguru.cpp @@ -989,7 +989,7 @@ bool add_file(const char* path_in, FileMode mode, Verbosity verbosity) Any logging message with a verbosity lower or equal to the given verbosity will be included. - This works for Unix like systems (i.e. Linux/Mac) + This works for Unix like systems (ie Linux/Mac) There is no current implementation for Windows (as I don't know the equivalent calls or have a way to test them). If you know please add and send a pull request. @@ -2026,7 +2026,7 @@ EcEntryBase::~EcEntryBase() { get_thread_ec_head_ref() = _previous; } Text ec_to_text(const char* value) { // Add quotes around the string to make it obvious where it begin and ends. - // This is great for detecting erroneous leading or trailing spaces in e.g. an + // This is great for detecting erroneous leading or trailing spaces in eg an // identifier. auto str = "\"" + std::string(value) + "\""; return Text{STRDUP(str.c_str())}; diff --git a/cpp/basix/loguru.hpp b/cpp/basix/loguru.hpp index 1744310bb..9c8a9b892 100644 --- a/cpp/basix/loguru.hpp +++ b/cpp/basix/loguru.hpp @@ -392,7 +392,7 @@ struct Message /* Everything with a verbosity equal or greater than g_stderr_verbosity will be written to stderr. You can set this in code or via the -v argument. Set to loguru::Verbosity_OFF to write nothing to stderr. -Default is 0, i.e. only log ERROR, WARNING and INFO are written to stderr. +Default is 0, ie only log ERROR, WARNING and INFO are written to stderr. */ LOGURU_EXPORT extern Verbosity g_stderr_verbosity; LOGURU_EXPORT extern bool g_colorlogtostderr; // True by default. @@ -530,7 +530,7 @@ void init(int& argc, char* argv[], const Options& options = {}); LOGURU_EXPORT void shutdown(); -// What ~ will be replaced with, e.g. "/home/your_user_name/" +// What ~ will be replaced with, eg "/home/your_user_name/" LOGURU_EXPORT const char* home_dir(); @@ -553,11 +553,11 @@ const char* current_dir(); LOGURU_EXPORT const char* filename(const char* path); -// e.g. "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/" +// eg "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/" LOGURU_EXPORT bool create_directories(const char* file_path_const); -// Writes date and time with millisecond precision, e.g. "20151017_161503.123" +// Writes date and time with millisecond precision, eg "20151017_161503.123" LOGURU_EXPORT void write_date_time(char* buff, unsigned buff_size); @@ -565,7 +565,7 @@ void write_date_time(char* buff, unsigned buff_size); LOGURU_EXPORT Text errno_as_text(); -/* Given a prefix of e.g. "~/loguru/" this might return +/* Given a prefix of eg "~/loguru/" this might return "/home/your_username/loguru/app_name/20151017_161503.123.log" where "app_name" is a sanitized version of argv[0]. @@ -1132,14 +1132,14 @@ some.cpp: namespace loguru { Text ec_to_text(MySmallType small_value) { - // Called only when needed, i.e. on a crash. + // Called only when needed, ie on a crash. std::string str = small_value.as_string(); // Format 'small_value' here somehow. return Text{STRDUP(str.c_str())}; } Text ec_to_text(const MyBigType* big_value) { - // Called only when needed, i.e. on a crash. + // Called only when needed, ie on a crash. std::string str = big_value->as_string(); // Format 'big_value' here somehow. return Text{STRDUP(str.c_str())}; } diff --git a/cpp/basix/polyset.cpp b/cpp/basix/polyset.cpp index f9670a6c2..621f27ca4 100644 --- a/cpp/basix/polyset.cpp +++ b/cpp/basix/polyset.cpp @@ -133,38 +133,38 @@ void tabulate_polyset_triangle_derivs(xt::xtensor& P, std::size_t n, { for (std::size_t p = 1; p <= n; ++p) { - auto p0 = xt::view(P, idx(kx, ky), xt::all(), idx(p, 0)); + auto p0 = xt::view(P, idx(kx, ky), xt::all(), idx(0, p)); const double a = static_cast(2 * p - 1) / static_cast(p); p0 = ((x0 * 2.0 - 1.0) + 0.5 * (x1 * 2.0 - 1.0) + 0.5) - * xt::view(P, idx(kx, ky), xt::all(), idx(p - 1, 0)) * a; + * xt::view(P, idx(kx, ky), xt::all(), idx(0, p - 1)) * a; if (kx > 0) { p0 += 2 * kx * a - * xt::view(P, idx(kx - 1, ky), xt::all(), idx(p - 1, 0)); + * xt::view(P, idx(kx - 1, ky), xt::all(), idx(0, p - 1)); } if (ky > 0) { - p0 += ky * a * xt::view(P, idx(kx, ky - 1), xt::all(), idx(p - 1, 0)); + p0 += ky * a * xt::view(P, idx(kx, ky - 1), xt::all(), idx(0, p - 1)); } if (p > 1) { // y^2 terms - p0 -= f3 * xt::view(P, idx(kx, ky), xt::all(), idx(p - 2, 0)) + p0 -= f3 * xt::view(P, idx(kx, ky), xt::all(), idx(0, p - 2)) * (a - 1.0); if (ky > 0) { p0 -= ky * ((x1 * 2.0 - 1.0) - 1.0) - * xt::view(P, idx(kx, ky - 1), xt::all(), idx(p - 2, 0)) + * xt::view(P, idx(kx, ky - 1), xt::all(), idx(0, p - 2)) * (a - 1.0); } if (ky > 1) { p0 -= ky * (ky - 1) - * xt::view(P, idx(kx, ky - 2), xt::all(), idx(p - 2, 0)) + * xt::view(P, idx(kx, ky - 2), xt::all(), idx(0, p - 2)) * (a - 1.0); } } @@ -172,27 +172,27 @@ void tabulate_polyset_triangle_derivs(xt::xtensor& P, std::size_t n, for (std::size_t p = 0; p < n; ++p) { - auto p0 = xt::view(P, idx(kx, ky), xt::all(), idx(p, 0)); - auto p1 = xt::view(P, idx(kx, ky), xt::all(), idx(p, 1)); + auto p0 = xt::view(P, idx(kx, ky), xt::all(), idx(0, p)); + auto p1 = xt::view(P, idx(kx, ky), xt::all(), idx(1, p)); p1 = p0 * ((x1 * 2.0 - 1.0) * (1.5 + p) + 0.5 + p); if (ky > 0) { p1 += 2 * ky * (1.5 + p) - * xt::view(P, idx(kx, ky - 1), xt::all(), idx(p, 0)); + * xt::view(P, idx(kx, ky - 1), xt::all(), idx(0, p)); } for (std::size_t q = 1; q < n - p; ++q) { const auto [a1, a2, a3] = jrc(2 * p + 1, q); - xt::view(P, idx(kx, ky), xt::all(), idx(p, q + 1)) - = xt::view(P, idx(kx, ky), xt::all(), idx(p, q)) + xt::view(P, idx(kx, ky), xt::all(), idx(q + 1, p)) + = xt::view(P, idx(kx, ky), xt::all(), idx(q, p)) * ((x1 * 2.0 - 1.0) * a1 + a2) - - xt::view(P, idx(kx, ky), xt::all(), idx(p, q - 1)) * a3; + - xt::view(P, idx(kx, ky), xt::all(), idx(q - 1, p)) * a3; if (ky > 0) { - xt::view(P, idx(kx, ky), xt::all(), idx(p, q + 1)) + xt::view(P, idx(kx, ky), xt::all(), idx(q + 1, p)) += 2 * ky * a1 - * xt::view(P, idx(kx, ky - 1), xt::all(), idx(p, q)); + * xt::view(P, idx(kx, ky - 1), xt::all(), idx(q, p)); } } } @@ -202,7 +202,7 @@ void tabulate_polyset_triangle_derivs(xt::xtensor& P, std::size_t n, // Normalisation for (std::size_t p = 0; p <= n; ++p) for (std::size_t q = 0; q <= n - p; ++q) - xt::view(P, xt::all(), xt::all(), idx(p, q)) + xt::view(P, xt::all(), xt::all(), idx(q, p)) *= std::sqrt((p + 0.5) * (p + q + 1)) * 2; } //----------------------------------------------------------------------------- @@ -245,42 +245,42 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { for (std::size_t p = 1; p <= n; ++p) { - auto p00 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, 0, 0)); + auto p00 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, 0, p)); double a = static_cast(2 * p - 1) / static_cast(p); p00 = ((x0 * 2.0 - 1.0) + 0.5 * ((x1 * 2.0 - 1.0) + (x2 * 2.0 - 1.0)) + 1.0) - * xt::view(P, idx(kx, ky, kz), xt::all(), idx(p - 1, 0, 0)) * a; + * xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, 0, p - 1)) * a; if (kx > 0) { p00 += 2 * kx * a * xt::view(P, idx(kx - 1, ky, kz), xt::all(), - idx(p - 1, 0, 0)); + idx(0, 0, p - 1)); } if (ky > 0) { p00 += ky * a * xt::view(P, idx(kx, ky - 1, kz), xt::all(), - idx(p - 1, 0, 0)); + idx(0, 0, p - 1)); } if (kz > 0) { p00 += kz * a * xt::view(P, idx(kx, ky, kz - 1), xt::all(), - idx(p - 1, 0, 0)); + idx(0, 0, p - 1)); } if (p > 1) { p00 -= f2 - * xt::view(P, idx(kx, ky, kz), xt::all(), idx(p - 2, 0, 0)) + * xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, 0, p - 2)) * (a - 1.0); if (ky > 0) { p00 -= ky * ((x1 * 2.0 - 1.0) + (x2 * 2.0 - 1.0)) * xt::view(P, idx(kx, ky - 1, kz), xt::all(), - idx(p - 2, 0, 0)) + idx(0, 0, p - 2)) * (a - 1.0); } @@ -288,7 +288,7 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { p00 -= ky * (ky - 1) * xt::view(P, idx(kx, ky - 2, kz), xt::all(), - idx(p - 2, 0, 0)) + idx(0, 0, p - 2)) * (a - 1.0); } @@ -296,7 +296,7 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { p00 -= kz * ((x1 * 2.0 - 1.0) + (x2 * 2.0 - 1.0)) * xt::view(P, idx(kx, ky, kz - 1), xt::all(), - idx(p - 2, 0, 0)) + idx(0, 0, p - 2)) * (a - 1.0); } @@ -304,7 +304,7 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { p00 -= kz * (kz - 1) * xt::view(P, idx(kx, ky, kz - 2), xt::all(), - idx(p - 2, 0, 0)) + idx(0, 0, p - 2)) * (a - 1.0); } @@ -312,7 +312,7 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { p00 -= 2.0 * ky * kz * xt::view(P, idx(kx, ky - 1, kz - 1), xt::all(), - idx(p - 2, 0, 0)) + idx(0, 0, p - 2)) * (a - 1.0); } } @@ -320,37 +320,37 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, for (std::size_t p = 0; p < n; ++p) { - auto p10 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, 1, 0)); - p10 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, 0, 0)) + auto p10 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, 1, p)); + p10 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, 0, p)) * ((1.0 + (x1 * 2.0 - 1.0)) * p + (2.0 + (x1 * 2.0 - 1.0) * 3.0 + (x2 * 2.0 - 1.0)) * 0.5); if (ky > 0) { p10 += 2 * ky - * xt::view(P, idx(kx, ky - 1, kz), xt::all(), idx(p, 0, 0)) + * xt::view(P, idx(kx, ky - 1, kz), xt::all(), idx(0, 0, p)) * (1.5 + p); } if (kz > 0) { p10 += kz - * xt::view(P, idx(kx, ky, kz - 1), xt::all(), idx(p, 0, 0)); + * xt::view(P, idx(kx, ky, kz - 1), xt::all(), idx(0, 0, p)); } for (std::size_t q = 1; q < n - p; ++q) { auto [aq, bq, cq] = jrc(2 * p + 1, q); auto pq1 - = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q + 1, 0)); - pq1 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, 0)) + = xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, q + 1, p)); + pq1 = xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, q, p)) * (f3 * aq + f4 * bq) - - xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q - 1, 0)) + - xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, q - 1, p)) * f5 * cq; if (ky > 0) { pq1 += 2 * ky - * xt::view(P, idx(kx, ky - 1, kz), xt::all(), idx(p, q, 0)) + * xt::view(P, idx(kx, ky - 1, kz), xt::all(), idx(0, q, p)) * aq; } @@ -358,11 +358,11 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { pq1 += kz * xt::view(P, idx(kx, ky, kz - 1), xt::all(), - idx(p, q, 0)) + idx(0, q, p)) * (aq - bq) + kz * (1.0 - (x2 * 2.0 - 1.0)) * xt::view(P, idx(kx, ky, kz - 1), xt::all(), - idx(p, q - 1, 0)) + idx(0, q - 1, p)) * cq; } @@ -371,7 +371,7 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, // Quadratic term in z pq1 -= kz * (kz - 1) * xt::view(P, idx(kx, ky, kz - 2), xt::all(), - idx(p, q - 1, 0)) + idx(0, q - 1, p)) * cq; } } @@ -381,13 +381,13 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { for (std::size_t q = 0; q < n - p; ++q) { - auto pq = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, 1)); - pq = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, 0)) + auto pq = xt::view(P, idx(kx, ky, kz), xt::all(), idx(1, q, p)); + pq = xt::view(P, idx(kx, ky, kz), xt::all(), idx(0, q, p)) * ((1.0 + p + q) + (x2 * 2.0 - 1.0) * (2.0 + p + q)); if (kz > 0) { pq += 2 * kz * (2.0 + p + q) - * xt::view(P, idx(kx, ky, kz - 1), xt::all(), idx(p, q, 0)); + * xt::view(P, idx(kx, ky, kz - 1), xt::all(), idx(0, q, p)); } } } @@ -399,17 +399,17 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, for (std::size_t r = 1; r < n - p - q; ++r) { auto [ar, br, cr] = jrc(2 * p + 2 * q + 2, r); - xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, r + 1)) - = xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, r)) + xt::view(P, idx(kx, ky, kz), xt::all(), idx(r + 1, q, p)) + = xt::view(P, idx(kx, ky, kz), xt::all(), idx(r, q, p)) * ((x2 * 2.0 - 1.0) * ar + br) - - xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, r - 1)) + - xt::view(P, idx(kx, ky, kz), xt::all(), idx(r - 1, q, p)) * cr; if (kz > 0) { - xt::view(P, idx(kx, ky, kz), xt::all(), idx(p, q, r + 1)) + xt::view(P, idx(kx, ky, kz), xt::all(), idx(r + 1, q, p)) += 2 * kz * ar * xt::view(P, idx(kx, ky, kz - 1), xt::all(), - idx(p, q, r)); + idx(r, q, p)); } } } @@ -425,7 +425,7 @@ void tabulate_polyset_tetrahedron_derivs(xt::xtensor& P, { for (std::size_t r = 0; r <= n - p - q; ++r) { - xt::view(P, xt::all(), xt::all(), idx(p, q, r)) + xt::view(P, xt::all(), xt::all(), idx(r, q, p)) *= std::sqrt(2 * (p + 0.5) * (p + q + 1.0) * (p + q + r + 1.5)) * 2; } } @@ -1027,9 +1027,8 @@ void tabulate_polyset_prism_derivs(xt::xtensor& P, std::size_t n, // Indexing for hexahedral basis functions auto prism_idx - = [n](std::size_t px, std::size_t py, std::size_t pz) -> std::size_t - { - return (n + 1) * (py * n + 1 - (py - 2) * (py - 1) / 2) + (n + 1) * px + pz; + = [n](std::size_t px, std::size_t py, std::size_t pz) -> std::size_t { + return (n + 1) * idx(py, px) + pz; }; // f3 = ((1 - y) / 2)^2 diff --git a/cpp/basix/polyset.h b/cpp/basix/polyset.h index 541e30e33..2b7923355 100644 --- a/cpp/basix/polyset.h +++ b/cpp/basix/polyset.h @@ -147,7 +147,7 @@ namespace basix::polyset /// /// - The first index is the derivative. The first entry is the basis /// itself. Derivatives are stored in triangular (2D) or tetrahedral -/// (3D) ordering, e.g. if `(p, q)` denotes `p` order dervative with +/// (3D) ordering, eg if `(p, q)` denotes `p` order dervative with /// repsect to `x` and `q` order derivative with respect to `y`, [0] -> /// (0, 0), [1] -> (1, 0), [2] -> (0, 1), [3] -> (2, 0), [4] -> (1, 1), /// [5] -> (0, 2), [6] -> (3, 0),... @@ -185,7 +185,7 @@ xt::xtensor tabulate(cell::type celltype, int d, int n, /// /// - The first index is the derivative. The first entry is the basis /// itself. Derivatives are stored in triangular (2D) or tetrahedral -/// (3D) ordering, e.g. if `(p, q)` denotes `p` order dervative with +/// (3D) ordering, eg if `(p, q)` denotes `p` order dervative with /// repsect to `x` and `q` order derivative with respect to `y`, [0] -> /// (0, 0), [1] -> (1, 0), [2] -> (0, 1), [3] -> (2, 0), [4] -> (1, 1), /// [5] -> (0, 2), [6] -> (3, 0),... diff --git a/demo/python/ccr_triangle_3.png b/demo/python/ccr_triangle_3.png deleted file mode 100644 index 2cb2c6307..000000000 Binary files a/demo/python/ccr_triangle_3.png and /dev/null differ diff --git a/demo/python/ccr_triangle_4.png b/demo/python/ccr_triangle_4.png deleted file mode 100644 index b94fe2b5b..000000000 Binary files a/demo/python/ccr_triangle_4.png and /dev/null differ diff --git a/demo/python/demo_custom_element.py b/demo/python/demo_custom_element.py index 141296720..0bcaa80cd 100644 --- a/demo/python/demo_custom_element.py +++ b/demo/python/demo_custom_element.py @@ -39,6 +39,10 @@ # .. math:: # 1,\; y,\; y^2,\; x,\; xy,\; xy^2,\; x^2,\; x^2y,\; x^2y^2 # +# The order in which the polynomials appear in the orthonormal polynomial sets for +# each cell are documented at +# https://docs.fenicsproject.org/basix/main/polyset-order.html. +# # As our polynomial space contains 1, :math:`y`, :math:`x` and :math:`xy`. The first # four rows of the matrix contain a single 1 for the four orthogonal polynomials with # these are their highest degree terms. diff --git a/doc/cpp/Doxyfile b/doc/cpp/Doxyfile index 09fa6e5ec..10af79025 100644 --- a/doc/cpp/Doxyfile +++ b/doc/cpp/Doxyfile @@ -198,7 +198,7 @@ JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# multi-line C++ special comment block (ie a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. @@ -314,7 +314,7 @@ TOC_INCLUDE_HEADINGS = 0 AUTOLINK_SUPPORT = YES -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# If you use STL classes (ie std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); @@ -385,7 +385,7 @@ INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, +# the documentation of the scope in which they are defined (ie file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). @@ -916,7 +916,7 @@ FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# producing the source files to browse (ie when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO @@ -1005,7 +1005,7 @@ SOURCE_TOOLTIPS = YES # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). +# tools must be available from the command line (ie in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. @@ -1413,7 +1413,7 @@ DISABLE_INDEX = NO # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the +# (ie any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at @@ -1484,7 +1484,7 @@ USE_MATHJAX = YES # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility), NativeMML (ie MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1813,7 +1813,7 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, +# file, ie a series of assignments. You only have to provide replacements, # missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the @@ -2392,7 +2392,7 @@ MAX_DOT_GRAPH_DEPTH = 0 # to support this out of the box. # # Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# badly anti-aliased labels on the edges of a graph (ie they become hard to # read). # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2400,7 +2400,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This +# files in one run (ie multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. # The default value is: NO. diff --git a/doc/web/make_html.py b/doc/web/make_html.py index e640c1618..0c8ceca59 100644 --- a/doc/web/make_html.py +++ b/doc/web/make_html.py @@ -30,12 +30,17 @@ def markdown(txt): # Convert code inside ```s (as these are not handled by the markdown library tsp = txt.split("```") out = "" - for t, code in zip(tsp[::2], tsp[1::2] + [""]): - code = code.strip().split("\n") - if code[0] in ["console", "bash", "python", "c", "cpp"]: - code = code[1:] - code = "\n".join(code) - out += f"{t}
{code}
" + code = False + for part in tsp: + if code: + part = part.strip().split("\n") + if part[0] in ["console", "bash", "python", "c", "cpp"]: + part = part[1:] + part = "\n".join(part) + out += f"
{part}
" + else: + out += part + code = not code return _markdown(out) @@ -218,6 +223,17 @@ def system(command): # Make demos os.system(f"rm {path('../../demo/python/*.py.rst')}") +os.system(f"rm {path('../../demo/python/*.png')}") +# If file saves matplotlib images, run the demo +for file in os.listdir(path("../../demo/python")): + if file.endswith(".py"): + with open(path(f"../../demo/python/{file}")) as f: + content = f.read() + if "savefig" in content: + here = os.getcwd() + os.chdir(path("../../demo/python")) + system(f"python3 {file}") + os.chdir(here) system(f"cd {path('../../demo/python')} && python3 convert_to_rst.py") system(f"mkdir {path('python/source/demo')}") system(f"cp {path('../../demo/python/*.rst')} {path('python/source/demo')}") diff --git a/doc/web/polyset-order.md b/doc/web/polyset-order.md new file mode 100644 index 000000000..0e61a6a77 --- /dev/null +++ b/doc/web/polyset-order.md @@ -0,0 +1,205 @@ +# The ordering of Basix's polynomial sets +When defining finite elements, Basix uses orthonormal polyomials to represent +a basis of the space spanned by the finite element functions. This page +details the order in which these polynomials are stored. + +In each example below, we give a list of the monomials that appear for the +first time in each term. The actual orthogonal polynomials will be linear +combinations of the monomial and all the previous monomials. For example, +the monomials + +\\(1\\), +\\(x\\), +\\(x^2\\), +\\(x^3\\), +... + +are given below for an interval. The orthonormal polynomials on an interval, +however, are + +\\(1\\), +\\(\\sqrt{3}(2x-1)\\), +\\(\\sqrt{5}(6x^2-6x+1)\\), +\\(\\sqrt{6}(20x^3 - 30x^2 + 12x - 1)\\), +... + + +### Interval +On an interval, Basix orders its polynomials by lowest degree first, ie + +\\(1\\), +\\(x\\), +\\(x^2\\), +\\(x^3\\), +... + +### Triangle +On a triangle, Basix orders its polynomials by degree (lowest first). +Polynomials of the same degree are ordered so that higher powers of \\(y\\) +appear first, ie + +\\(1\\), +\\(y\\), +\\(x\\), +\\(y^2\\), +\\(xy\\), +\\(x^2\\), +\\(y^3\\), +\\(x^2y\\), +\\(xy^2\\), +\\(x^3\\), +... + +### Tetrahedron +On a tetrahedron, Basix orders its polynomials by degree (lowest first). +Polynomials of the same degree are ordered so that higher powers of \\(z\\) +appear first then higher powers of \\(y\\), ie + +\\(1\\), +\\(z\\), +\\(y\\), +\\(x\\), +\\(z^2\\), +\\(yz\\), +\\(xz\\), +\\(y^2\\), +\\(xy\\), +\\(x^2\\), +\\(z^3\\), +\\(yz^2\\), +\\(xz^2\\), +\\(y^2z\\), +\\(xyz\\), +\\(x^2z\\), +\\(y^3\\), +\\(xy^2\\), +\\(x^2y\\), +\\(x^3\\), +... + +### Quadrilateral +On a quadrilateral, we take a tensor product of the polynomials on an +interval with \\(y\\) as the variable and those with \\(x\\) as the variable. +The resulting ordering has polynomials containing only \\(y\\) first +(low-to-high degree), then these polynomials times \\(x\\), then the same +polynomials times \\(x^2\\), and so on; ie + +\\(1\\), +\\(y\\), +\\(y^2\\), +\\(y^3\\), +\\(x\\), +\\(xy\\), +\\(xy^2\\), +\\(xy^3\\), +\\(x^2\\), +\\(x^2y\\), +\\(x^2y^2\\), +\\(x^2y^3\\), +\\(x^3\\), +\\(x^3y\\), +\\(x^3y^2\\), +\\(x^3y^3\\) + +### Hexahedron +On a hexahedron, we take a tensor product of the polynomials on an interval +with \\(z\\) as the varibles and the polynomials on a quadrilateral with +\\((x,y)\\) as the variables. The resulting ordering has polynomials containing +only \\(z\\) first (low-to-high degree) then these polynomials times \\(y\\), +then the same polynomials times \\(y^2\\), then \\(y^3\\) and so on (following +the order on a quadrilateral); ie + +\\(1\\), +\\(z\\), +\\(z^2\\), +\\(z^3\\), +\\(y\\), +\\(yz\\), +\\(yz^2\\), +\\(yz^3\\), +\\(y^2\\), +\\(y^2z\\), +\\(y^2z^2\\), +\\(y^2z^3\\), +\\(y^3\\), +\\(y^3z\\), +\\(y^3z^2\\), +\\(y^3z^3\\), +\\(x\\), +\\(xz\\), +\\(xz^2\\), +\\(xz^3\\), +\\(xy\\), +\\(xyz\\), +\\(xyz^2\\), +\\(xyz^3\\), +\\(xy^2\\), +\\(xy^2z\\), +\\(xy^2z^2\\), +\\(xy^2z^3\\), +\\(xy^3\\), +\\(xy^3z\\), +\\(xy^3z^2\\), +\\(xy^3z^3\\), +\\(x^2\\), +\\(x^2z\\), +\\(x^2z^2\\), +\\(x^2z^3\\), +\\(x^2y\\), +\\(x^2yz\\), +\\(x^2yz^2\\), +\\(x^2yz^3\\), +\\(x^2y^2\\), +\\(x^2y^2z\\), +\\(x^2y^2z^2\\), +\\(x^2y^2z^3\\), +\\(x^2y^3\\), +\\(x^2y^3z\\), +\\(x^2y^3z^2\\), +\\(x^2y^3z^3\\), +\\(x^3\\), +\\(x^3z\\), +\\(x^3z^2\\), +\\(x^3z^3\\), +\\(x^3y\\), +\\(x^3yz\\), +\\(x^3yz^2\\), +\\(x^3yz^3\\), +\\(x^3y^2\\), +\\(x^3y^2z\\), +\\(x^3y^2z^2\\), +\\(x^3y^2z^3\\), +\\(x^3y^3\\) +\\(x^3y^3z\\), +\\(x^3y^3z^2\\), +\\(x^3y^3z^3\\) + +### Prism +On a hexahedron, we take a tensor product of the polynomials on an interval +with \\(z\\) as the varibles and the polynomials on a triangle with +\\((x,y)\\) as the variables. The resulting ordering has polynomials containing +only \\(z\\) first (low-to-high degree) then these polynomials times \\(y\\), +then the same polynomials times \\(x\\), then \\(y^2\\) and so on (following +the order on a triangle); ie + +\\(1\\), +\\(z\\), +\\(z^2\\), +\\(y\\), +\\(yz\\), +\\(yz^2\\), +\\(x\\), +\\(xz\\), +\\(xz^2\\), +\\(y^2\\), +\\(y^2z\\), +\\(y^2z^2\\), +\\(xy\\), +\\(xyz\\), +\\(xyz^2\\), +\\(x^2\\), +\\(x^2z\\), +\\(x^2z^2\\), +... + +### Pyramid diff --git a/python/docs.h b/python/docs.h index 15e484237..1b8a82a52 100644 --- a/python/docs.h +++ b/python/docs.h @@ -74,7 +74,7 @@ Create a lattice of points on a reference cell optionally including the outer surface points For a given celltype, this creates a set of points on a regular grid -which covers the cell, e.g. for a quadrilateral, with n=2, the points are: +which covers the cell, eg for a quadrilateral, with n=2, the points are: [0,0],[0.5,0],[1,0],[0,0.5],[0.5,0.5],[1,0.5],[0,1],[0.5,1],[1,1] If the parameter exterior is set to false, the points lying on the external boundary are omitted, in this case for a quadrilateral with n=2, the points @@ -105,7 +105,7 @@ Create a lattice of points on a reference cell optionally including the outer surface points For a given celltype, this creates a set of points on a regular grid -which covers the cell, e.g. for a quadrilateral, with n=2, the points are: +which covers the cell, eg for a quadrilateral, with n=2, the points are: [0,0],[0.5,0],[1,0],[0,0.5],[0.5,0.5],[1,0.5],[0,1],[0.5,1],[1,1] If the parameter exterior is set to false, the points lying on the external boundary are omitted, in this case for a quadrilateral with n=2, the points @@ -239,7 +239,7 @@ numpy.ndarray[numpy.float64] The basis functions (and derivatives). The shape is (derivative, point, basis fn index, value index). - The first index is the derivative, with higher derivatives are - stored in triangular (2D) or tetrahedral (3D) ordering, i.e. for + stored in triangular (2D) or tetrahedral (3D) ordering, ie for the (x,y) derivatives in 2D: (0,0), (1,0), (0,1), (2,0), (1,1), (0,2), (3,0)... The function basix::indexing::idx can be used to find the appropriate derivative. @@ -762,7 +762,7 @@ numpy.ndarray[numpy.float64] - The first index is the derivative. The first entry is the basis itself. Derivatives are stored in triangular (2D) or tetrahedral - (3D) ordering, e.g. if `(p, q)` denotes `p` order dervative with + (3D) ordering, eg if `(p, q)` denotes `p` order dervative with repsect to `x` and `q` order derivative with respect to `y`, [0] -> (0, 0), [1] -> (1, 0), [2] -> (0, 1), [3] -> (2, 0), [4] -> (1, 1), [5] -> (0, 2), [6] -> (3, 0),... diff --git a/test/test_cmake/CMakeLists.txt b/test/test_cmake/CMakeLists.txt index 7f6979a1e..47e4183dd 100644 --- a/test/test_cmake/CMakeLists.txt +++ b/test/test_cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -# Example of CMakeLists.txt for downstream C++ project (e.g. DOLFINx) +# Example of CMakeLists.txt for downstream C++ project (eg DOLFINx) # Used to check correctly installed package during wheel creation.: cmake_minimum_required(VERSION 3.16) diff --git a/test/test_polynomials.py b/test/test_polynomials.py index 50e552cd6..5196f1ed8 100644 --- a/test/test_polynomials.py +++ b/test/test_polynomials.py @@ -5,6 +5,12 @@ import basix import pytest import numpy +import sympy + +one = sympy.Integer(1) +x = sympy.Symbol("x") +y = sympy.Symbol("y") +z = sympy.Symbol("z") @pytest.mark.parametrize("degree", range(6)) @@ -23,3 +29,67 @@ def test_legendre(cell_type, degree): matrix[i, j] = sum(col_i * col_j * weights) assert numpy.allclose(matrix, numpy.identity(polys.shape[1])) + + +def evaluate(function, pt): + if len(pt) == 1: + return function.subs(x, pt[0]) + elif len(pt) == 2: + return function.subs(x, pt[0]).subs(y, pt[1]) + elif len(pt) == 3: + return function.subs(x, pt[0]).subs(y, pt[1]).subs(z, pt[2]) + + +# TODO: pyramid +@pytest.mark.parametrize("cell_type, functions, degree", [ + [basix.CellType.interval, [one, x], 1], + [basix.CellType.interval, [one, x, x**2], 2], + [basix.CellType.interval, [one, x, x**2, x**3], 3], + [basix.CellType.triangle, [one, y, x], 1], + [basix.CellType.triangle, [one, y, x, y**2, x * y, x**2], 2], + [basix.CellType.triangle, [one, y, x, y**2, x * y, x**2, y**3, x*y**2, x**2*y, x**3], 3], + [basix.CellType.tetrahedron, [one], 0], + [basix.CellType.tetrahedron, [one, z, y, x], 1], + [basix.CellType.tetrahedron, [one, z, y, x, z**2, y * z, x * z, y**2, x * y, x**2], 2], + [basix.CellType.tetrahedron, [one, z, y, x, z**2, y * z, x * z, y**2, x * y, x**2, + z**3, y * z**2, x * z**2, y**2 * z, x * y * z, x**2 * z, + y**3, x * y**2, x**2 * y, x**3], 3], + [basix.CellType.quadrilateral, [one, y, x, x * y], 1], + [basix.CellType.quadrilateral, [one, y, y**2, x, x * y, x * y**2, x**2, x**2 * y, x**2 * y**2], 2], + [basix.CellType.hexahedron, [one, z, y, y * z, x, x * z, x * y, x * y * z], 1], + [basix.CellType.hexahedron, [i * j * k for i in [one, x, x**2] + for j in [one, y, y**2] for k in [one, z, z**2]], 2], + [basix.CellType.prism, [one, z, y, y * z, x, x * z], 1], + [basix.CellType.prism, [one, z, z**2, y, y * z, y * z**2, x, x * z, x * z**2, + y**2, y**2 * z, y**2 * z**2, x * y, x * y * z, x * y * z**2, + x**2, x**2 * z, x**2 * z**2], 2], +]) +def test_order(cell_type, functions, degree): + points, weights = basix.make_quadrature(cell_type, 2 * degree) + polys = basix.tabulate_polynomials(basix.PolynomialType.legendre, cell_type, degree, points) + + assert len(functions) == polys.shape[1] + + eval_points = basix.create_lattice(cell_type, 10, basix.LatticeType.equispaced, True) + eval_polys = basix.tabulate_polynomials(basix.PolynomialType.legendre, cell_type, degree, eval_points) + + for n, function in enumerate(functions): + expected_eval = [float(evaluate(function, i)) for i in eval_points] + + # Using n polynomials + # The monomial should NOT be exactly represented using this many + coeffs = [] + values = numpy.array([evaluate(function, i) for i in points]) + for p in range(n): + coeffs.append(sum(values * polys[:, p] * weights)) + actual_eval = [float(sum(coeffs * p[:n])) for p in eval_polys] + assert not numpy.allclose(expected_eval, actual_eval) + + # Using n+1 polynomials + # The monomial should be exactly represented using this many + coeffs = [] + values = numpy.array([evaluate(function, i) for i in points]) + for p in range(n + 1): + coeffs.append(sum(values * polys[:, p] * weights)) + actual_eval = [float(sum(coeffs * p[:n+1])) for p in eval_polys] + assert numpy.allclose(expected_eval, actual_eval)