diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml index 3ec65e0b3..9d3fb9cc5 100644 --- a/.JuliaFormatter.toml +++ b/.JuliaFormatter.toml @@ -10,9 +10,9 @@ conditional_to_if = true # Add whitespace to type definitions. whitespace_typedefs = true # Enables formatting for code blocks -format_docstrings = false +format_docstrings = true # Enables formatting for markdown -format_markdown = false +format_markdown = true # Stops pipes from being converted to function calls pipe_to_function_call = false # Since the `formart_markdown` can break markdown blocks in the docs, it is best to ignore diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 102392831..72ab62605 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,24 +8,31 @@ assignees: '' --- ### First checks -1. Make sure the bug is from the `Mantis` code and not other modifications. -2. Check the open issues to see if someone else has already spotted the same bug. + + 1. Make sure the bug is from the `Mantis` code and not other modifications. + 2. Check the open issues to see if someone else has already spotted the same bug. ### Expected behaviour + A clear and concise description of what you expected to happen. ### Observed behaviour + Prints of the output you obtain. ### Describe the bug + A clear and concise description of what the bug is. ### To Reproduce -1. Indicate the `Mantis` release used. -2. Provide the minimal but necessary code to encounter the error. + + 1. Indicate the `Mantis` release used. + 2. Provide the minimal but necessary code to encounter the error. ### Screenshots + If applicable, add screenshots to help explain your problem. ### Additional context + Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 550553dbc..cd120ccd3 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -8,13 +8,17 @@ assignees: '' --- ### Is your feature request related to a problem? Please describe. + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] ### Describe the solution you'd like + A clear and concise description of what you want to happen. ### Describe alternatives you've considered + A clear and concise description of any alternative solutions or features you've considered. ### Additional context + Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/JuliaFormatter.yml b/.github/workflows/JuliaFormatter.yml new file mode 100644 index 000000000..10fe54710 --- /dev/null +++ b/.github/workflows/JuliaFormatter.yml @@ -0,0 +1,23 @@ +name: Validate Formatting + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + validate-formatting: + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: read + + steps: + - uses: actions/checkout@v7 + - uses: julia-actions/setup-julia@v3 + - uses: julia-actions/cache@v3 + - name: Install jlfmt and check formatting + run: | + export PATH="$HOME/.julia/bin:$PATH" + julia -e 'using Pkg; Pkg.Registry.add("General"); Pkg.Registry.update(); Pkg.Apps.add(["JuliaFormatter"])' + jlfmt --check -v ./ diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index 635c761d9..c30e71cf5 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -1,34 +1,36 @@ # Acknowledgments -## Founders +## Founders -- Diogo C. Cabanas -- Joey Dekker -- Artur Palha -- Deepesh Toshniwal + - Diogo C. Cabanas + - Joey Dekker + - Artur Palha + - Deepesh Toshniwal ## Current Maintainers -- Diogo C. Cabanas -- Joey Dekker -- Artur Palha -- Deepesh Toshniwal + - Diogo C. Cabanas + - Joey Dekker + - Artur Palha + - Deepesh Toshniwal All current maintainers are part of TU Delft's Numerical Analysis' department, and therefore would like to thank the support the university provides. Diogo C. Cabanas is supported by FCT - Fundação para a Ciência e Tecnologia, I.P., with -project reference 2023.00238.BD and DOI identifier https://doi.org/10.54499/2023.00238.BD. +project reference 2023.00238.BD and DOI identifier https://doi.org/10.54499/2023.00238.BD. The research of Deepesh Toshniwal was partially supported (2021-2025) by project number 202.150 awarded through the Veni research programme by the Dutch Research Council (NWO). # Previous Contributors + The following TU Delft 'Computer Science and Engineering'-minor students created the first implementation of the `TimeIntegrators`-module in Q2/3 of AY2024/2025: -- Daisy de Blom, -- Wiktor Cupiał, -- Pelle Mutsaers, -- Pablo Raichs Fernandez, -- Teun Schuurs, -- Aleksandra Taneva. + + - Daisy de Blom, + - Wiktor Cupiał, + - Pelle Mutsaers, + - Pablo Raichs Fernandez, + - Teun Schuurs, + - Aleksandra Taneva. diff --git a/README.md b/README.md index 256a1ba06..89c0fe7d2 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,13 @@ and adaptively-refinable spaces (e.g., hierarchical B-splines). `Mantis` is free, open-source, and available under the [EUPL licence](https://github.com/MantisFEM/Mantis.jl/blob/main/LICENSE). - ## Authors + The `Mantis` package was created by -- Diogo C. Cabanas, -- Joey Dekker, -- Artur Palha, -- Deepesh Toshniwal, - + + - Diogo C. Cabanas, + - Joey Dekker, + - Artur Palha, + - Deepesh Toshniwal, + from TU Delft's Institute of Applied Mathematics (DIAM). diff --git a/docs/make.jl b/docs/make.jl index b2954c9ab..dd6c2f072 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -36,25 +36,24 @@ Design = [ joinpath("Design", "DesignIntroduction.md"), "Philosophy" => [joinpath("Design", "Philosophy", "WhyMantis.md")], "Theory" => joinpath.("Design", "Theory", ["FEM.md", "DifferentialForms.md"]), - "Modules" => - joinpath.( - "Design", - "Modules", - [ - "Analysis.md", - "Assemblers.md", - "Forms.md", - "FunctionSpaces.md", - "GeneralHelpers.md", - "Hierarchy.md", - "Geometry.md", - "Mesh.md", - "Plot.md", - "Points.md", - "Quadrature.md", - "TimeIntegrators.md", - ], - ), + "Modules" => joinpath.( + "Design", + "Modules", + [ + "Analysis.md", + "Assemblers.md", + "Forms.md", + "FunctionSpaces.md", + "GeneralHelpers.md", + "Hierarchy.md", + "Geometry.md", + "Mesh.md", + "Plot.md", + "Points.md", + "Quadrature.md", + "TimeIntegrators.md", + ], + ), ] Support = [ @@ -119,14 +118,14 @@ makedocs(; pages=Pages, plugins=[bib], format=DocumenterVitepress.MarkdownVitepress(; - repo="github.com/MantisFEM/Mantis.jl", devbranch = "dev", devurl = "dev" + repo="github.com/MantisFEM/Mantis.jl", devbranch="dev", devurl="dev" ), ) DocumenterVitepress.deploydocs(; repo="github.com/MantisFEM/Mantis.jl", target="build", # this is where Vitepress stores its output devbranch="dev", - devurl = "dev", + devurl="dev", branch="gh-pages", push_preview=true, ) diff --git a/docs/src/BibliographicInformation/HowToCite.md b/docs/src/BibliographicInformation/HowToCite.md index d9f207323..b74f98477 100644 --- a/docs/src/BibliographicInformation/HowToCite.md +++ b/docs/src/BibliographicInformation/HowToCite.md @@ -2,6 +2,7 @@ Thank you for using `Mantis`. Please consider including a citation using Zenodo [Cabanas2026](@cite): + ```@bibliography Pages = ["HowToCite.md"] Canonical = false diff --git a/docs/src/Design/DesignIntroduction.md b/docs/src/Design/DesignIntroduction.md index ae0e0de2a..78a8659a2 100644 --- a/docs/src/Design/DesignIntroduction.md +++ b/docs/src/Design/DesignIntroduction.md @@ -1,7 +1,8 @@ # [Introduction](@id DesignIntroduction) -In this part of the documentation, we provide a detailed description of the ideas and design -behind `Mantis`. In the philosophy section, we describe the ideas and philosophy behind -`Mantis`, and we discuss why `Mantis` was created. Then, in the theory section, we briefly -recap some of the main theoretical ideas that are used within `Mantis`, such as the -[The Finite Element Method](@ref). Lastly, in the module section, we describe, per module, + +In this part of the documentation, we provide a detailed description of the ideas and design +behind `Mantis`. In the philosophy section, we describe the ideas and philosophy behind +`Mantis`, and we discuss why `Mantis` was created. Then, in the theory section, we briefly +recap some of the main theoretical ideas that are used within `Mantis`, such as the +[The Finite Element Method](@ref). Lastly, in the module section, we describe, per module, what is available in `Mantis`, its structure, and some of the design choices. diff --git a/docs/src/Design/Modules/Analysis.md b/docs/src/Design/Modules/Analysis.md index e5a9c458b..fba99c37d 100644 --- a/docs/src/Design/Modules/Analysis.md +++ b/docs/src/Design/Modules/Analysis.md @@ -1,6 +1,7 @@ # Analysis ## All docstrings from Mantis.Analysis + ```@autodocs Modules = [Mantis.Analysis] ``` diff --git a/docs/src/Design/Modules/Assemblers.md b/docs/src/Design/Modules/Assemblers.md index b4ab1fc45..c14d6b45a 100644 --- a/docs/src/Design/Modules/Assemblers.md +++ b/docs/src/Design/Modules/Assemblers.md @@ -1,6 +1,7 @@ # [Assemblers](@id DocAssemblyModule) ## All docstrings from Mantis.Assemblers + ```@autodocs Modules = [Mantis.Assemblers] ``` diff --git a/docs/src/Design/Modules/Forms.md b/docs/src/Design/Modules/Forms.md index dfc84776f..a5f8de368 100644 --- a/docs/src/Design/Modules/Forms.md +++ b/docs/src/Design/Modules/Forms.md @@ -1,6 +1,7 @@ ```@meta CurrentModule = Mantis.Forms ``` + # Forms As explained on the [differential form theory page](@ref TheoryForms), differential forms provide an elegant and general framework to deal with the discretisation of PDEs. @@ -8,75 +9,91 @@ One of the most distinguishing features of `Mantis` is its ability to work with The `Forms` module provides all the required tools to use differential forms in `Mantis`. ## What is a differential form in `Mantis`? -The top-level type within the `Forms` module is the `AbstractForm{manifold_dim, form_rank, expression_rank}` type. Every expression involving forms (see [Creating Forms](@ref FormsCreation)) and operations on forms (see [Operations on Forms](@ref FormsOperations)) will be an `AbstractForm`. -There are two exceptions to this rule. + +The top-level type within the `Forms` module is the `AbstractForm{manifold_dim, form_rank, expression_rank}` type. Every expression involving forms (see [Creating Forms](@ref FormsCreation)) and operations on forms (see [Operations on Forms](@ref FormsOperations)) will be an `AbstractForm`. +There are two exceptions to this rule. The first exception is an operation that returns a real value, such as an integral, see [Operators returning a real value](@ref FormsRealValuedOperators). The second exception is an operation that returns a vector, such as the sharp, see [Operators returning a vector](@ref FormsOperationsToVectors) + ```@docs AbstractForm ``` There are two aliases for `AbstractForm`, which are `AbstractFormField` and `AbstractFormSpace`. + ```@docs AbstractFormField AbstractFormSpace ``` -Every `AbstractForm` has three type parameters which say something about the form. +Every `AbstractForm` has three type parameters which say something about the form. You can always call the following three methods on any `AbstractForm` to get these type parameters. + ```@docs get_manifold_dim get_form_rank get_expression_rank ``` -The above abstract types are used in function signatures, but cannot be instantiated. +The above abstract types are used in function signatures, but cannot be instantiated. The concrete types that can be instantiated are discussed next. - ## [Creating Forms](@id FormsCreation) + You can create two main types of `Forms`: [FormSpaces](@ref FormsSpaces) and [FormFields](@ref FormsFields). ### [FormSpaces](@id FormsSpaces) -A `FormSpace` allows you to distinguish between functions and forms. + +A `FormSpace` allows you to distinguish between functions and forms. A `FormSpace` is build on top of a [FunctionSpaces.AbstractFESpace](@ref), which acts as its basis. However, it is the `FormSpace` that dictates the behaviour of the form. + ```@docs FormSpace ``` + As explained on the [differential form theory page](@ref TheoryForms), differential forms are more expressive than functions. By using a `FormSpace`, this expressiveness becomes available within your code. For example, if we start by creating a simple 2D [FunctionSpaces.BSplineSpace](@ref) using the helper [FunctionSpaces.create\_bspline\_space](@ref) (on a unit square with ``4 \times 4`` elements, degree ``3`` and regularity ``2``), + ```@repl CreatingFormSpaces using Mantis B = FunctionSpaces.create_bspline_space((0.0, 0.0), (1.0, 1.0), (4, 4), (3, 3), (2, 2)) ``` + we can use this function space to create two different spaces: one for a ``0``-form ``\Lambda^0_h`` and one for a ``2``-form ``\Lambda^2_h`` (a top form in 2D). + ```@repl CreatingFormSpaces Λ⁰ₕ = Forms.FormSpace(0, B, "0-form") Λ²ₕ = Forms.FormSpace(2, B, "2-form") ``` -These two forms have the same basis `B`, but have different transformation properties. + +These two forms have the same basis `B`, but have different transformation properties. This will result in the use of different pullbacks (see [How `FormSpaces` are evaluated](@ref FormsInternalEvaluateFormSpace) on how that is reflected in the implementation), and on the operations that you can apply to these forms (see [Operations on Forms](@ref FormsOperations)). ### [ConstantFormSpaces](@id FormsConstantSpaces) -Next to the conventional `FormSpace`, `Mantis` also provides a `ConstantFormSpace`. -A `ConstantFormSpace` can be instantiated as a ``0``- or `manifold_dim`-form (so a top form), and will always evaluate to ``1``. -This is often useful as Lagrange multiplier, where a `ConstantFormSpace` can act as the form basis for the real numbers ``\mathbb{R}``. + +Next to the conventional `FormSpace`, `Mantis` also provides a `ConstantFormSpace`. +A `ConstantFormSpace` can be instantiated as a ``0``- or `manifold_dim`-form (so a top form), and will always evaluate to ``1``. +This is often useful as Lagrange multiplier, where a `ConstantFormSpace` can act as the form basis for the real numbers ``\mathbb{R}``. Note that, compared to the [FormSpaces](@ref FormsSpaces), the `ConstantFormSpace` does not require a function space but only the geometry. + ```@docs ConstantFormSpace ``` ### [FormFields](@id FormsFields) -A `FormField` can be used to represent a differential form field (a combination of a basis with coefficients) or forms without an underlying basis. + +A `FormField` can be used to represent a differential form field (a combination of a basis with coefficients) or forms without an underlying basis. The former is, for example, useful to represent solution fields or right hand sides, while the latter can be used with analytical expressions to, for example, represent exact solutions or forcings. + ```@docs FormField AnalyticalFormField ``` Since a `FormField` has coefficients and an `AnalyticalFormField` has an analytical expression, you can inspect them using the following functions. + ```@docs get_coefficients get_num_coefficients @@ -84,70 +101,85 @@ get_expression ``` ## [Evaluating Forms](@id FormsEvaluateFormSpace) + As with any object in `Mantis`, evaluating a form is a matter of calling the `evaluate`-function: + ```@docs evaluate(::AbstractForm{manifold_dim}, ::Int, ::Points.AbstractPoints{manifold_dim}) where {manifold_dim} ``` - ### [Internals: How a `FormSpace` is evaluated](@id FormsInternalEvaluateFormSpace) + !!! note "Internal behaviour" + We explain how a `FormSpace` is evaluated. However, this is considered an implementational detail. The evaluation of a `FormSpace` happens in the canonical domain and is done in two steps. -Firstly, the underlying function space is evaluated. -This evaluation gives us the function values and the basis indices. -Secondly, the function space evaluation is pulled-back to the canonical domain. +Firstly, the underlying function space is evaluated. +This evaluation gives us the function values and the basis indices. +Secondly, the function space evaluation is pulled-back to the canonical domain. What this pullback looks like is dictated by the `form_rank`. The evaluation then returns the pulled-back values and the basis indices (the indices for the form are the same as for the function space). This behaviour is encoded using the following two internal functions. + ```@docs _evaluate_form_in_canonical_coordinates _pullback_to_canonical_coordinates ``` ### [Internals: How an `AnalyticalFormField` is evaluated](@id FormsInternalEvaluateAnalyticalFormField) + !!! note "Internal behaviour" + We explain how an `AnalyticalFormField` is evaluated. However, this is considered an implementational detail. -A user has to define the expression used in the `AnalyticalFormField` in the physical domain. However, in `Mantis`, forms are always evaluated in the canonical domain. This means that any `AnalyticalFormField` must always be pulled-back before the result can be used in other computations. -These pull-backs are determined by the `form_rank` of the `AnalyticalFormField`. +A user has to define the expression used in the `AnalyticalFormField` in the physical domain. However, in `Mantis`, forms are always evaluated in the canonical domain. This means that any `AnalyticalFormField` must always be pulled-back before the result can be used in other computations. +These pull-backs are determined by the `form_rank` of the `AnalyticalFormField`. + ```@docs _evaluate(::AnalyticalFormField{manifold_dim, 0}, ::Int, ::Points.AbstractPoints{manifold_dim}) where {manifold_dim} ``` - ## [Operations on Forms](@id FormsOperations) -Now that we know how to create forms, we can look into the operators that we can use on these form objects. + +Now that we know how to create forms, we can look into the operators that we can use on these form objects. We first look at operators that map forms to forms. ### [Exterior Derivative](@id FormsExteriorDerivative) -The exterior derivative is a generalised derivative, which maps `k`-forms to `k+1` forms, and is known by its alias `d`. + +The exterior derivative is a generalised derivative, which maps `k`-forms to `k+1` forms, and is known by its alias `d`. The exterior derivative is a metric-independent operation. In ``\mathbb{R}^3``, the exterior derivative embodies the well-known gradient (when applied to ``0``-forms), curl (when applied to ``1``-forms), and divergence (when applied to ``2``-forms). + ```@docs ExteriorDerivative d ``` ### [Wedge](@id FormsWedge) + The wedge-operator is a generalisation of products. It takes in two forms (say a `k`-form and an `l`-form) and produces another form (a `k+l`-form). + ```@docs Wedge ∧ ``` ### [Hodge](@id FormsHodge) + The Hodge-star operator is a metric-dependent operator, which maps `k`-forms to `manifold_dim-k` forms. + ```@docs Hodge ★ ``` ### [Codifferential](@id FormsCodifferential) -The codifferential, often denoted ``d^{\star}`` or ``\delta``, is a differential operator mapping ``k``-forms to ``k-1``-forms. -On manifolds without boundaries, it is the ``L^2``-adjoint of the exterior derivative. + +The codifferential, often denoted ``d^{\star}`` or ``\delta``, is a differential operator mapping ``k``-forms to ``k-1``-forms. +On manifolds without boundaries, it is the ``L^2``-adjoint of the exterior derivative. That is, ``(\alpha^{k-1}, \delta\beta^k) = (d\alpha^{k-1}, \beta^k)``, where ``(\cdot, \cdot)`` is an ``L^2`` inner-product. + ```@docs CoDifferential dstar @@ -155,82 +187,104 @@ dstar ``` ### [Algebraic](@id FormsAlgebraic) -The algebraic operators allow you to use operators like addition, subtraction, and multiplication by a scalar on any form. + +The algebraic operators allow you to use operators like addition, subtraction, and multiplication by a scalar on any form. These operations are implemented as `UnaryFormTransformation` or `BinaryFormTransformation`, depending on whether the operator is a unary or binary operator, respectively. + ```@docs UnaryFormTransformation BinaryFormTransformation ``` ## [Operators returning a vector](@id FormsOperationsToVectors) + Next to operators that map forms to forms, there are operators that map forms to vectors. At the moment, `Mantis` does not have a type for vectors like it does for forms. The result of the operators in this section are thus **not** a subtype of `AbstractForm`. ### [Sharp](@id FormsSharp) + The sharp operator takes a ``1``-form and returns the proxy vector field. + ```@docs Sharp ♯ ``` + The sharp operator also has its own evaluate function, which, like the [evaluate](@ref) on forms, evaluates in the canonical domain. + ```@docs evaluate(::Sharp{manifold_dim}, ::Int, ::Points.AbstractPoints{manifold_dim}) where {manifold_dim} ``` ### [Pushforward](@id FormsPushforward) -As explained above, the [Sharp](@ref) turns a ``1``-form into a vector field, but its evaluate still returns values in the canonical domain. -To get values in the physical domain, the vector has to be pushforwarded. + +As explained above, the [Sharp](@ref) turns a ``1``-form into a vector field, but its evaluate still returns values in the canonical domain. +To get values in the physical domain, the vector has to be pushforwarded. Note that this is not a structure in `Mantis`, just a function. + ```@docs evaluate_pushforward ``` + Because the [Sharp](@ref) and pushforward are often used in combination, there is a convenience function to call both operators directly. + ```@docs evaluate_sharp_pushforward ``` ## [Operators returning a real value](@id FormsRealValuedOperators) + Another main class of operators are operators that return a value. These are all grouped under the `AbstractRealValuedOperator`-type. + ```@docs AbstractRealValuedOperator ``` ### [Integrals](@id FormsIntegrals) + The most important `AbstractRealValuedOperator` is the integral. + ```@docs Integral ∫ ``` + The integral has its own evaluate function, which only takes the integral and an `element_id` as input, since the integral operator already stores a quadrature rule and thus the evaluation points. + ```@docs evaluate(::Integral{manifold_dim, F, Q}, ::Int) where {manifold_dim, form_rank, expression_rank, F <: AbstractForm{manifold_dim, form_rank, expression_rank}, Q <: Quadrature.AbstractGlobalQuadratureRule{manifold_dim}} ``` You can retrieve the underlying quadrature rule and the underlying number of evaluation elements (see the docs page of [Quadrature](@ref) for this terminology) with the following functions. + ```@docs get_quadrature_rule get_num_evaluation_elements ``` ### [Algebraic Operations on Integrals](@id FormsAlgebraicReals) + ```@docs UnaryOperatorTransformation BinaryOperatorTransformation ``` ## [Basic Operations](@id FormsBasicOperations) + Next to the operators described in the previous section, you can also interact and inspect form objects using the following methods. Every form in `Mantis` has a label. You can retrieve this label using the following function. + ```@docs get_label ``` Most forms or form operators in `Mantis` are structs that contain another form. -For example, the [`ExteriorDerivative`](@ref) stores the form to which it is applied. +For example, the [`ExteriorDerivative`](@ref) stores the form to which it is applied. To retrieve the underlying form, you can use one of the following functions. + ```@docs get_form get_forms @@ -239,20 +293,26 @@ get_form_space_tree Additionally, every form is defined on some geometry. While this geometry is not stored in every form explicitly, it can always be retrieved using the following getter. + ```@docs get_geometry ``` + It is also possible to immediately obtain the number of elements in the underlying geometry using the following method. + ```@docs get_num_elements ``` Most forms also have an underlying [FunctionSpaces.AbstractFESpace](@ref). To obtain this function space, use the following getter. + ```@docs get_fe_space ``` + It is also possible to directly obtain some useful information about the underlying function space using the following functions. + ```@docs get_estimated_nnz_per_elem get_max_local_dim @@ -262,9 +322,11 @@ get_num_basis ## [Helper Functions](@id FormsHelpers) ### [De Rham Complexes](@id FormsComplexes) -The De Rham complex (or any other complex for that matter) is an important construct which structure-preserving methods utilise. + +The De Rham complex (or any other complex for that matter) is an important construct which structure-preserving methods utilise. As such, there are some (well-)known sequences of [Form Spaces](@ref FormsSpaces) that form a finite-dimensional De Rham complex. `Mantis` provides some helper functions to easily create the spaces in such a complex. + ```@docs create_tensor_product_bspline_de_rham_complex create_curvilinear_tensor_product_bspline_de_rham_complex @@ -274,13 +336,16 @@ create_polar_spline_de_rham_complex ``` ### [Boundary Conditions](@id FormsBCs) + In `Mantis`, boundary conditions are set during assembly (see the [assembly page](@ref DocAssemblyModule) for the details), but the following functions can help in specifying boundary conditions. + ```@docs set_dirichlet_boundary_conditions trace_basis_idxs ``` ### [Other Helper Functions](@id FormsOtherHelpers) + ```@docs get_basis_index_combinations ``` diff --git a/docs/src/Design/Modules/FunctionSpaces.md b/docs/src/Design/Modules/FunctionSpaces.md index 9ec5ee300..23bd634ba 100644 --- a/docs/src/Design/Modules/FunctionSpaces.md +++ b/docs/src/Design/Modules/FunctionSpaces.md @@ -1,6 +1,7 @@ # FunctionSpaces ## All docstrings from Mantis.FunctionSpaces + ```@autodocs Modules = [Mantis.FunctionSpaces] ``` diff --git a/docs/src/Design/Modules/GeneralHelpers.md b/docs/src/Design/Modules/GeneralHelpers.md index af7656693..13207b839 100644 --- a/docs/src/Design/Modules/GeneralHelpers.md +++ b/docs/src/Design/Modules/GeneralHelpers.md @@ -1,7 +1,9 @@ # General Helpers + General utility functions that are used throughout Mantis. ## All docstrings from Mantis.GeneralHelpers + ```@autodocs Modules = [Mantis.GeneralHelpers] ``` diff --git a/docs/src/Design/Modules/Geometry.md b/docs/src/Design/Modules/Geometry.md index 0c510b903..3d59c2783 100644 --- a/docs/src/Design/Modules/Geometry.md +++ b/docs/src/Design/Modules/Geometry.md @@ -9,10 +9,10 @@ An ``(n, m)`` geometry ``\Phi`` is a collection of ``L`` mappings ``\Omega^{0} := [0, 1]^{n}`` into ``L`` ``n``-dimensional simply connected subdomains, ``\Omega^{1}_{i}`` with ``i = 1, \dots, L``, of ``\mathbb{R}^{m}``. Moreover, ``\bigcap_{i=1}^{L}\Omega^{1}_{i} = \emptyset`` and using Core: Argument -``\overline{\Omega}^{1}_{i} \cap \overline{\Omega}^{1}_{j} \subset \partial\Omega^{1}_{i} -\cup \partial\Omega^{1}_{j}`` with ``i,j = 1, \dots, L``. +``\overline{\Omega}^{1}_{i} \cap \overline{\Omega}^{1}_{j} \subset \partial\Omega^{1}_{i} \cup \partial\Omega^{1}_{j}`` with ``i,j = 1, \dots, L``. Note that + ```math \Phi_{i}(\xi_{1}, \dots, \xi_{n}) = (x_{1}, \dots, x_{m}), ``` @@ -22,25 +22,30 @@ element ``i``. ## Tensor Product Geometry -Given an ``(n_{1}, m_{1})`` geometry ``\Phi^{1}`` of ``L_{1}`` mappings and an ``(n_{2}, -m_{2})`` geometry ``\Phi^{2}`` of ``L_{2}`` mappings, i.e., +Given an ``(n_{1}, m_{1})`` geometry ``\Phi^{1}`` of ``L_{1}`` mappings and an ``(n_{2}, m_{2})`` geometry ``\Phi^{2}`` of ``L_{2}`` mappings, i.e., + ```math \Phi^{1}_{i}: [0, 1]^{n_{1}} \mapsto \Omega^{1}_{i} \subset \mathbb{R}^{m_{1}}, \quad i = 1, \dots, L_{1} ``` + and + ```math \Phi^{2}_{i}: [0, 1]^{n_{2}} \mapsto \Omega^{2}_{i} \subset \mathbb{R}^{m_{2}}, \quad i = 1, \dots, L_{2} ``` -the tensor product geometry ``\Phi := \Phi^{1}\otimes\Phi^{2}`` is an ``(n_{1} + n_{2}, -m_{1} + m_{2})`` geometry made up of a collection of ``L_{1}L_{2}`` mappings ``\Phi_{k}`` + +the tensor product geometry ``\Phi := \Phi^{1}\otimes\Phi^{2}`` is an ``(n_{1} + n_{2}, m_{1} + m_{2})`` geometry made up of a collection of ``L_{1}L_{2}`` mappings ``\Phi_{k}`` + ```math \Phi_{k = L_1(j - 1) + I}: [0, 1]^{n_{1}} \times [0, 1]^{n_{2}} \mapsto \Omega_{k} = \Omega^{1}_{i}\times\Omega^{2}_{j} \subset \mathbb{R}^{m_{1} + m_{2}}, \quad i = 1, \dots, L_{1}, \text{ and } j = 1, \dots, L{2}. ``` + Specifically, we have + ```math \Phi_{L_1(j - 1) + i, l}(\xi_{1}, \dots, \xi_{n}) := \left\{ \begin{array}{ll} \Phi^{1}_{i, l}(\xi_{1}, \dots, \xi_{n_{1}}), & \quad \text{if } l \leq n_{1}\\ \Phi^{2}_{j, l - @@ -50,10 +55,13 @@ n_{1}}(\xi_{n_{1} + 1}, \dots, \xi_{n_{1} + n_{2}}), & \quad \text{if } n_{1} < ``` The Jacobian of this geometry + ```math J^{k}_{l,v} := \frac{\partial \Phi_{k, l}}{\partial \xi_{v}} ``` + is given by + ```math \frac{\partial\Phi_{L_1(j - 1) + i, l}}{\partial\xi_{v}}(\xi_{1}, \dots, \xi_{n}) := \left\{ \begin{array}{ll} \frac{\partial\Phi^{1}_{i, l}}{\partial \xi_{v}}(\xi_{1}, \dots, @@ -67,46 +75,43 @@ is given by ### Evaluation Given the `NTuple` `ξ` of ``n`` `Vectors`, ``\boldsymbol{\xi}^{i}``, ``i=1, \dots, n``, each -containing ``m_{i}`` unidimensional coordinates ``\xi^{i}_{j}``, ``i = 1, \dots, n`` and `` -j = 1, \dots m_{i}``, the tensor product geometry is evaluated at the element `element_idx` -and at the ``\prod_{i=1}^{n}m_{i}`` tensor product points ``V_{k = j_{1} + \sum_{i=2}^{n} -(j_{i} - 1)\prod_{l=1}^{i-1}m_{l}} = (\xi^{1}_{j_{1}}, \dots, \xi^{n}_{j_{n}})``, with +containing ``m_{i}`` unidimensional coordinates ``\xi^{i}_{j}``, ``i = 1, \dots, n`` and ``j = 1, \dots m_{i}``, the tensor product geometry is evaluated at the element `element_idx` +and at the ``\prod_{i=1}^{n}m_{i}`` tensor product points ``V_{k = j_{1} + \sum_{i=2}^{n} (j_{i} - 1)\prod_{l=1}^{i-1}m_{l}} = (\xi^{1}_{j_{1}}, \dots, \xi^{n}_{j_{n}})``, with ``j_{i} = 1, \dots, m_{i}``. The output is a matrix, ``\boldsymbol{\mathsf{X}}`` of dimensions ``\left(\prod_{i=1}^{n}m_{i}\right) \times m`` (the number of tensor product points where the geometry is evaluated in element `element_idx`, and the dimension of the embedding space to where the canonical element is mapped into. Specifically: + ```math \boldsymbol{\mathsf{X}}_{k, l} = \Phi_{r, l}(\xi^{1}_{j_{1}}, \dots, \xi^{n}_{j_{n}}), ``` -where ``r =`` `element_idx`, and ``k = j_{1} + \sum_{i=2}^{n} (j_{i} - -1)\prod_{l=1}^{i-1}m_{l}``, as before. + +where ``r =`` `element_idx`, and ``k = j_{1} + \sum_{i=2}^{n} (j_{i} - 1)\prod_{l=1}^{i-1}m_{l}``, as before. ### Jacobian Given the `NTuple` `ξ` of ``n`` `Vectors`, ``\boldsymbol{\xi}^{i}``, ``i=1, \dots, n``, each -containing ``m_{i}`` unidimensional coordinates ``\xi^{i}_{j}``, ``i = 1, \dots, n`` and `` -j = 1, \dots m_{i}``, evaluates the Jacobian of the tensor product geometry at the element -`element_idx` and at the ``\prod_{i=1}^{n}m_{i}`` tensor product points ``V_{k = j_{1} + -\sum_{i=2}^{n} (j_{i} - 1)\prod_{l=1}^{i-1}m_{l}} = (\xi^{1}_{j_{1}}, \dots, -\xi^{n}_{j_{n}})``, with ``j_{i} = 1, \dots, m_{i}``. +containing ``m_{i}`` unidimensional coordinates ``\xi^{i}_{j}``, ``i = 1, \dots, n`` and ``j = 1, \dots m_{i}``, evaluates the Jacobian of the tensor product geometry at the element +`element_idx` and at the ``\prod_{i=1}^{n}m_{i}`` tensor product points ``V_{k = j_{1} + \sum_{i=2}^{n} (j_{i} - 1)\prod_{l=1}^{i-1}m_{l}} = (\xi^{1}_{j_{1}}, \dots, \xi^{n}_{j_{n}})``, with ``j_{i} = 1, \dots, m_{i}``. The output is a matrix, ``\boldsymbol{\mathsf{J}}`` of dimensions ``\left(\prod_{i=1}^{n}m_{i}\right) \times m \times n`` (the number of tensor product points where the geometry is evaluated in element `element_idx`, the dimension of the embedding space to where the canonical element is mapped into, and the dimension of the canonical element, which is the same as the dimension of the element's manifold). Specifically: + ```math \boldsymbol{\mathsf{J}}_{k, l, s} = \frac{\partial\Phi_{r, l}}{\partial\xi_{s}}(\xi^{1}_{j_{1}}, \dots, \xi^{n}_{j_{n}}), ``` -where ``r = \mathtt{element\_idx}``, and ``k = j_{1} + \sum_{i=2}^{n} (j_{i} - -1)\prod_{l=1}^{i-1}m_{l}``, as before. +where ``r = \mathtt{element\_idx}``, and ``k = j_{1} + \sum_{i=2}^{n} (j_{i} - 1)\prod_{l=1}^{i-1}m_{l}``, as before. ## All docstrings from Mantis.Geometry + ```@autodocs Modules = [Mantis.Geometry] ``` diff --git a/docs/src/Design/Modules/Hierarchy.md b/docs/src/Design/Modules/Hierarchy.md index 673dc569f..24226efb8 100644 --- a/docs/src/Design/Modules/Hierarchy.md +++ b/docs/src/Design/Modules/Hierarchy.md @@ -1,6 +1,7 @@ # Hierarchy ## All docstrings from Mantis.Hierarchy + ```@autodocs Modules = [Mantis.Hierarchy] ``` diff --git a/docs/src/Design/Modules/Mesh.md b/docs/src/Design/Modules/Mesh.md index 69bdd8574..b2c69902f 100644 --- a/docs/src/Design/Modules/Mesh.md +++ b/docs/src/Design/Modules/Mesh.md @@ -1,6 +1,7 @@ # Mesh ## All docstrings from Mantis.Mesh + ```@autodocs Modules = [Mantis.Mesh] ``` diff --git a/docs/src/Design/Modules/Plot.md b/docs/src/Design/Modules/Plot.md index 1ebba6a70..05e057af8 100644 --- a/docs/src/Design/Modules/Plot.md +++ b/docs/src/Design/Modules/Plot.md @@ -1,7 +1,9 @@ # Plot + Note that the `Plot` module does not actually plot itself, but instead writes to VTK files. ## All docstrings from Mantis.Plot + ```@autodocs Modules = [Mantis.Plot] ``` diff --git a/docs/src/Design/Modules/Points.md b/docs/src/Design/Modules/Points.md index f7723c5dc..0a0048daa 100644 --- a/docs/src/Design/Modules/Points.md +++ b/docs/src/Design/Modules/Points.md @@ -1,6 +1,7 @@ # Points ## All docstrings from Mantis.Points + ```@autodocs Modules = [Mantis.Points] ``` diff --git a/docs/src/Design/Modules/Quadrature.md b/docs/src/Design/Modules/Quadrature.md index 0f2feea93..6db64fb54 100644 --- a/docs/src/Design/Modules/Quadrature.md +++ b/docs/src/Design/Modules/Quadrature.md @@ -1,6 +1,7 @@ # Quadrature ## All docstrings from Mantis.Quadrature + ```@autodocs Modules = [Mantis.Quadrature] ``` diff --git a/docs/src/Design/Modules/TimeIntegrators.md b/docs/src/Design/Modules/TimeIntegrators.md index 5b7841cf1..180c11ed7 100644 --- a/docs/src/Design/Modules/TimeIntegrators.md +++ b/docs/src/Design/Modules/TimeIntegrators.md @@ -1,33 +1,42 @@ ```@meta CurrentModule = Mantis.TimeIntegrators ``` + # TimeIntegrators The time integration module implemented in `Mantis` is based on the framework developed by [Vos2011](@cite). -This framework allows for an easy implementation of a variety of explicit, implicit, and implicit-explicit (IMEX) time stepping schemes, and is based on the concept of general linear methods (GLMs). +This framework allows for an easy implementation of a variety of explicit, implicit, and implicit-explicit (IMEX) time stepping schemes, and is based on the concept of general linear methods (GLMs). See, for example, [Butcher2006](@cite), for more details. -These methods are applicable to both ODEs and PDEs, so that both are available in `Mantis`. +These methods are applicable to both ODEs and PDEs, so that both are available in `Mantis`. ## GLMs: Notation and Theory -General linear methods can be characterised as follows (see [Butcher2006](@cite), [Vos2011](@cite)). + +General linear methods can be characterised as follows (see [Butcher2006](@cite), [Vos2011](@cite)). Consider the initial value problem defined as the ODE + ```math \frac{d\mathbf{y}}{dt} = \mathbf{f}(\mathbf{y}), \quad \mathbf{y}(t_0) = \mathbf{y}_0\;, ``` -where ``\mathbf{f}: \mathbb{R}^N \to \mathbb{R}^N``. + +where ``\mathbf{f}: \mathbb{R}^N \to \mathbb{R}^N``. The ``n``-th (time) step of the GLM comprised of ``r`` (integrator) steps and ``s`` stages is then formulated as + ```math \begin{align} \mathbf{Y}_i &= \Delta t \sum_{j=1}^{s} a_{ij} \mathbf{F}_j + \sum_{j=1}^{r} u_{ij} \mathbf{y}_j^{n-1}, \quad i = 1, \dots, s\;, \\ \mathbf{y}_i^n &= \Delta t \sum_{j=1}^{s} b_{ij} \mathbf{F}_j + \sum_{j=1}^{r} v_{ij} \mathbf{y}_j^{n-1}, \quad i = 1, \dots, r\;, \end{align} ``` + where ``\mathbf{Y}_i`` are called the stage values and ``\mathbf{F}_i`` are called the stage derivatives. These two quantities are related by the differential equation + ```math \mathbf{F}_i = \mathbf{f}(\mathbf{Y}_i)\;. ``` + The above formulation can be cast into the following matrix form + ```math \begin{bmatrix} \mathbf{Y} \\ @@ -42,7 +51,9 @@ B \otimes I & V \otimes I \mathbf{y}^{n-1} \end{bmatrix} \;, ``` + which is often simplified (with some abuse of notation) to + ```math \begin{bmatrix} \mathbf{Y} \\ @@ -57,7 +68,9 @@ B & V \mathbf{y}^{n-1} \end{bmatrix} \;. ``` + Either way, the vectors ``\mathbf{y}`` (in/output approximations), ``\mathbf{Y}`` (stage values), and ``\mathbf{F}`` (stage derivatives) are defined as + ```math \mathbf{y}^{n-1} = \begin{bmatrix} y_1^{n-1} \\ @@ -84,66 +97,77 @@ F_2 \\ F_s \end{bmatrix}\;. ``` -It is important to note that the in/output vectors can contain more than just the solution. -The exact content depends on the specific method, but often includes previously computed -stage derivatives. This is particularly important when creating and/or initialising new + +It is important to note that the in/output vectors can contain more than just the solution. +The exact content depends on the specific method, but often includes previously computed +stage derivatives. This is particularly important when creating and/or initialising new methods. ### [GLMs: Characterising a GLM](@id TIGLMCharacter) -Any time integrator that fits in the above framework can thus be characterised by the four matrices ``A``, ``B``, ``U``, ``V``, and the layout of the in- and output vectors ``\mathbf{y}``. + +Any time integrator that fits in the above framework can thus be characterised by the four matrices ``A``, ``B``, ``U``, ``V``, and the layout of the in- and output vectors ``\mathbf{y}``. In addition, every time integrator will also need a vector ``C``, which keeps track of the time at which the stages are evaluated. In `Mantis`, ``A``, ``B``, ``U``, ``V``, and ``C`` are stored in the time integrator structs (see [this section below](@ref TIwhatareTIs)). The matrices have the following sizes: -| Matrix | Size | -| :-----------: | :-----------: | -| ``A`` | `num_stages` x `num_stages` | -| ``B`` | `num_steps` x `num_stages` | -| ``U`` | `num_stages` x `num_steps` | -| ``V`` | `num_steps` x `num_steps` | -| ``C`` | `num_stages` x `1` | - +| Matrix | Size | +|:------:|:---------------------------:| +| ``A`` | `num_stages` x `num_stages` | +| ``B`` | `num_steps` x `num_stages` | +| ``U`` | `num_stages` x `num_steps` | +| ``V`` | `num_steps` x `num_steps` | +| ``C`` | `num_stages` x `1` | ### GLMs: Extension to IMEX Schemes + The framework introduced by [Vos2011](@cite) extends the GLM idea to IMEX integrators. The ODE from the previous section is now split into + ```math \frac{d\mathbf{y}}{dt} = \mathbf{f}(\mathbf{y}) + \mathbf{g}(\mathbf{y}), \quad \mathbf{y}(t_0) = \mathbf{y}_0\;, ``` -where ``\mathbf{f}: \mathbb{R}^N \to \mathbb{R}^N`` and ``\mathbf{g}: \mathbb{R}^N \to \mathbb{R}^N``. + +where ``\mathbf{f}: \mathbb{R}^N \to \mathbb{R}^N`` and ``\mathbf{g}: \mathbb{R}^N \to \mathbb{R}^N``. The ``\mathbf{f}``-part represent the part of the ODE that is treated explicitly, while the ``\mathbf{g}``-part is treated implicitly. The ``n``-th (time) step of the IMEX-GLM comprised of ``r`` (integrator) steps and ``s`` stages is then formulated as + ```math \begin{align} \mathbf{Y}_i &= \Delta t \sum_{j=1}^{s} a^{IM}_{ij} \mathbf{G}_j + \Delta t \sum_{j=1}^{s} a^{EX}_{ij} \mathbf{F}_j + \sum_{j=1}^{r} u_{ij} \mathbf{y}_j^{n-1}, \quad i = 1, \dots, s\;, \\ \mathbf{y}_i^n &= \Delta t \sum_{j=1}^{s} b^{IM}_{ij} \mathbf{G}_j + \Delta t \sum_{j=1}^{s} b^{EX}_{ij} \mathbf{F}_j + \sum_{j=1}^{r} v_{ij} \mathbf{y}_j^{n-1}, \quad i = 1, \dots, r\;, \end{align} ``` + where ``\mathbf{Y}_i`` are called the stage values and ``\mathbf{F}_i`` and ``\mathbf{G}_i`` are called the (explicit and implicit) stage derivatives. These quantities are related by the differential equation + ```math \mathbf{F}_i = \mathbf{f}(\mathbf{Y}_i), \quad \mathbf{G}_i = \mathbf{g}(\mathbf{Y}_i)\;. ``` + The matrix form is obtained in the same way as in the previous section. -IMEX GLMs are characterised in the same way as described in [GLMs: Characterising a GLM](@ref TIGLMCharacter). +IMEX GLMs are characterised in the same way as described in [GLMs: Characterising a GLM](@ref TIGLMCharacter). The only difference is that an IMEX GLM will have two ``A`` and ``B`` matrices, and two ``C`` vectors : one for the explicit part and one for the implicit part. - ## [What are time integrators in `Mantis`?](@id TIwhatareTIs) -The top-level type within the `TimeIntegrators` module is the `AbstractTimeIntegrator{num_stages, num_steps}` type. + +The top-level type within the `TimeIntegrators` module is the `AbstractTimeIntegrator{num_stages, num_steps}` type. + ```@docs AbstractTimeIntegrator ``` Note that you can always obtain the number of stages and steps using the following functions. + ```@docs get_num_stages get_num_steps ``` -The `AbstractTimeIntegrator{num_stages, num_steps}` type has four concrete subtypes, each +The `AbstractTimeIntegrator{num_stages, num_steps}` type has four concrete subtypes, each representing a specific class of time integrators. + ```@docs Explicit DiagonallyImplicit @@ -154,24 +178,29 @@ IMEX The ``A``-matrix (see above) in the GLM framework dictates whether a scheme is implicit or not. When initialising one of the above structs, this is checked using the following function. You can also use this to check what to expect. + ```@docs check_implicit ``` Since all schemes store the order of the scheme, you can always retrieve that information using the following getter. + ```@docs get_order ``` ### [The solution objects](@id TIsolutions) -The time integrators themselves do not store the solution. -This is instead handled by the `TimeIntegrationSolution`-object. + +The time integrators themselves do not store the solution. +This is instead handled by the `TimeIntegrationSolution`-object. + ```@docs TimeIntegrationSolution ``` The `TimeIntegrationSolution` stores the information about the state of the time integration problem. You can inspect such objects through the following getters. + ```@docs get_num_variables get_solution @@ -182,7 +211,7 @@ get_remaining_startup_steps ::: details Internals: solution objects -We explain some internals related to the solution objects. +We explain some internals related to the solution objects. However, this is considered an implementation detail. Next to the information just mentioned, a `TimeIntegrationSolution` also stores the pre-allocated arrays and the solution object for the startup scheme (if present), which can be obtained with the following getters. @@ -198,7 +227,7 @@ get_temp_var > [!CAUTION] > Do not manually modify the pre-allocated arrays. -> +> > Modifying the pre-allocated arrays may lead to incorrect results or unexpected behaviour. The above getters are internally used to access the pre-allocated arrays and to overwrite their values. @@ -207,9 +236,11 @@ You should not need these functions unless you are extending the integrate funct ::: ## [Adding problem-specific information](@id TIprobleminfo) -To use the `TimeIntegrators`-module, you have to specify which problem you want to solve. -Information about the problem is collected in `TimeIntegrationOperators`. + +To use the `TimeIntegrators`-module, you have to specify which problem you want to solve. +Information about the problem is collected in `TimeIntegrationOperators`. See [GLMs: Notation and Theory](@ref) and [GLMs: Extension to IMEX Schemes](@ref) for the notation. + ```@docs TimeIntegrationOperators ``` @@ -230,10 +261,13 @@ define_imex_ode ``` ## [Time Integration](@id TIintegration) + Now that the scheme, solution object, and the problem are all defined, we can perform the actual time integration. ### [Time Integrate](@id TIintegrate) + The integration happens by calling one of the following two methods. + ```@docs time_integrate time_integrate! @@ -241,11 +275,12 @@ time_integrate! ::: details Internals: time integration -We explain how the time integration is performed. +We explain how the time integration is performed. However, this is considered an implementation detail. The integration functions from [Time Integrate](@ref TIintegrate) end up calling the following internal integrator. This integrator function is specialised for different integrators and encodes how the time stepping is actually performed. + ```@docs _time_integrate! ``` @@ -253,17 +288,20 @@ _time_integrate! ::: ### [Initialisation](@id TIsolutions) -All integrators must be initialised. -For multi-stage schemes, this is often just a matter of adding the initial condition. + +All integrators must be initialised. +For multi-stage schemes, this is often just a matter of adding the initial condition. For multi-step schemes, this requires a startup scheme and more computation. -To handle these different initialisation requirements, `Mantis` has a `TimeLevels` struct, as introduced in [Vos2011](@cite), to keep track of what needs to be initialised. +To handle these different initialisation requirements, `Mantis` has a `TimeLevels` struct, as introduced in [Vos2011](@cite), to keep track of what needs to be initialised. Every time integrator has a `TimeLevels` struct to define what information is needed from previous steps. + ```@docs TimeLevels ``` In general, the length of the arrays in the `TimeLevels` object is required. These lengths can be easily obtained using the following getters. + ```@docs get_num_step_values get_num_implicit_derivatives @@ -271,13 +309,14 @@ get_num_explicit_derivatives ``` The actual initialisation step(s) can be performed by calling the following function. + ```@docs initialise_scheme ``` ::: tip Other initialisation procedures -`Mantis` does not provide an exhaustive set of initialisation procedures. +`Mantis` does not provide an exhaustive set of initialisation procedures. Some GLM schemes may require a different quantity to be initialised than what the `initialise_scheme`-method provides. If this is the case, you can always perform the initialisation manually. See [Adding your own scheme](@ref TimeIntegratorsAddYourOwn) for the details. @@ -285,9 +324,11 @@ See [Adding your own scheme](@ref TimeIntegratorsAddYourOwn) for the details. ::: ## [Pre-implemented schemes](@id TIschemes) -`Mantis` provides a few pre-implemented schemes for convenience. + +`Mantis` provides a few pre-implemented schemes for convenience. ::: details Pre-implemented explicit integrators in the Runge-Kutta family. + ```@docs FORWARD_EULER EXPLICIT_MIDPOINT @@ -302,9 +343,11 @@ RK4 RK4_3_8 RALSTON4 ``` + ::: ::: details Pre-implemented (diagonally) implicit integrators in the Runge-Kutta family. + ```@docs BACKWARD_EULER RADAU_IA_1 @@ -318,18 +361,22 @@ DIRK4 GAUSS_LEGENDRE_4 GAUSS_LEGENDRE_6 ``` + ::: ::: details Pre-implemented explicit multi-step integrators. + ```@docs AB1 AB2 AB3 AB4 ``` + ::: ::: details Pre-implemented (diagonally) implicit multi-step integrators. + ```@docs AM0 AM1 @@ -341,9 +388,11 @@ BDF2 BDF3 BDF4 ``` + ::: ::: details Pre-implemented IMEX integrators. + ```@docs BACKWARD_FORWARD_EULER MIDPOINT_IMEX @@ -352,33 +401,41 @@ IMEX331 CNAB2 SSSS2 ``` + ::: You can, of course, always initialise a new scheme yourself (see the concrete types in [this section](@ref TIwhatareTIs) or [Adding your own scheme](@ref TimeIntegratorsAddYourOwn)). Next to the pre-implemented schemes, `Mantis` also provides the following convenience function to take a Butcher-Tableau and turn it into a GLM-based time integrator. + ```@docs butcher_tableau_to_glm ``` ## [Adding your own scheme](@id TimeIntegratorsAddYourOwn) + As an example of how to add your own time integrator, we look at how to implement an Almost Runge-Kutta (ARK) scheme. We use a specific scheme introduced in [Rattenbury2005](@cite). -This scheme requires a specialised initialisation, since it needs an estimate of the second derivative which is not accounted for in the available initialisations. +This scheme requires a specialised initialisation, since it needs an estimate of the second derivative which is not accounted for in the available initialisations. As a result, this scheme is not part of Mantis. ::: details Example: solving a simple ODE with an ARK scheme. Consider the ODE: + ```math \frac{dy}{dt} = \lambda y,\quad y(t=0) = 1.0 ``` -which has exact solution + +which has exact solution + ```math y(t) = \exp(\lambda t)\;. ``` + We can encode this in code as + ```@example arkexample using Mantis import StaticArrays @@ -399,13 +456,15 @@ function test_ode_explicit_func!(output, yn, t) end test_ode_explicit = TimeIntegrators.define_explicit_ode(test_ode_explicit_func!) ``` + ::: -The ARK3 scheme that we use here has a known GLM-representation (see [Rattenbury2005](@cite), page 50). -This method, however, requires its own initialisation, see [Rattenbury2005](@cite), pages 37-38. -This initialisation, can be easily implemented as shown below. -Note that the `TimeLevels` struct has more entries for the explicit forcing step. -No other changes are required. +The ARK3 scheme that we use here has a known GLM-representation (see [Rattenbury2005](@cite), page 50). +This method, however, requires its own initialisation, see [Rattenbury2005](@cite), pages 37-38. +This initialisation, can be easily implemented as shown below. +Note that the `TimeLevels` struct has more entries for the explicit forcing step. +No other changes are required. + ```@example arkexample const ARK3 = TimeIntegrators.Explicit( StaticArrays.SMatrix{3, 3}(0.0, 1/2, 0.0, 0.0, 0.0, 3/4, 0.0, 0.0, 0.0), # A @@ -421,7 +480,7 @@ const ARK3 = TimeIntegrators.Explicit( 3, ) -dt = 0.1 +dt = 0.1 yn = zeros(Float64, 1, 3) yn[:, 1] .= [y_0] yn[:, 2] .= lambda .* [y_0] .* dt diff --git a/docs/src/Design/Philosophy/WhyMantis.md b/docs/src/Design/Philosophy/WhyMantis.md index dbd4cb3fd..e01dc68d9 100644 --- a/docs/src/Design/Philosophy/WhyMantis.md +++ b/docs/src/Design/Philosophy/WhyMantis.md @@ -1,4 +1,4 @@ # Why `Mantis`? -As there are a variety of finite-element packages available in a variety of languages, the +As there are a variety of finite-element packages available in a variety of languages, the first questions that many people ask is 'Why another FEM package?'. diff --git a/docs/src/Examples/Introduction.md b/docs/src/Examples/Introduction.md index 29a52c539..7e85a4b69 100644 --- a/docs/src/Examples/Introduction.md +++ b/docs/src/Examples/Introduction.md @@ -1,8 +1,10 @@ # Examples Overview + This is the example section of Mantis. The examples show how `Mantis` can be used in practise. The following examples are available: + ```@contents Pages = filter!(s->s != "Introduction.md",readdir(pwd())) Depth = 1 diff --git a/docs/src/GettingStarted.md b/docs/src/GettingStarted.md index 7625fb8f6..796d83da1 100644 --- a/docs/src/GettingStarted.md +++ b/docs/src/GettingStarted.md @@ -4,6 +4,7 @@ Mantis.jl is a registered Julia package. As such, we can easily install `Mantis` using Julia's package manager `Pkg`, as shown below: + ```julia using Pkg diff --git a/docs/src/ReleaseHistory/v0-Acanthops/v0.1-brunneri.md b/docs/src/ReleaseHistory/v0-Acanthops/v0.1-brunneri.md index bc72d6831..96217ab5e 100644 --- a/docs/src/ReleaseHistory/v0-Acanthops/v0.1-brunneri.md +++ b/docs/src/ReleaseHistory/v0-Acanthops/v0.1-brunneri.md @@ -1,2 +1,3 @@ # v0.1 - Acanthops brunneri + The first publically available release for `Mantis`. diff --git a/docs/src/ReleaseHistory/v0-Acanthops/v0.2-centralis.md b/docs/src/ReleaseHistory/v0-Acanthops/v0.2-centralis.md index 7ec1dae8c..ec8bf2359 100644 --- a/docs/src/ReleaseHistory/v0-Acanthops/v0.2-centralis.md +++ b/docs/src/ReleaseHistory/v0-Acanthops/v0.2-centralis.md @@ -1,10 +1,12 @@ # v0.2 - Acanthops centralis ## New in `v0.2` -- The codifferential operator. -- Hessian computations for all geometry types. + + - The codifferential operator. + - Hessian computations for all geometry types. These additions allowed the biharmonic problem to be tackled. ## Patch Releases + The `v0.2`-release saw three patch releases. diff --git a/docs/src/ReleaseHistory/v0-Acanthops/v0.3-erosa.md b/docs/src/ReleaseHistory/v0-Acanthops/v0.3-erosa.md index cffa5b36c..483559405 100644 --- a/docs/src/ReleaseHistory/v0-Acanthops/v0.3-erosa.md +++ b/docs/src/ReleaseHistory/v0-Acanthops/v0.3-erosa.md @@ -2,5 +2,5 @@ ## New in `v0.3` -- `Mantis` is now distributed under the [EUPL license](https://github.com/MantisFEM/Mantis.jl?tab=License-1-ov-file) -- The `FunctionSpaces`-module now depends on the `Geometry`-module. + - `Mantis` is now distributed under the [EUPL license](https://github.com/MantisFEM/Mantis.jl?tab=License-1-ov-file) + - The `FunctionSpaces`-module now depends on the `Geometry`-module. diff --git a/docs/src/ReleaseHistory/v0-Acanthops/v0.4-falcata.md b/docs/src/ReleaseHistory/v0-Acanthops/v0.4-falcata.md index 62b58bd92..70791f576 100644 --- a/docs/src/ReleaseHistory/v0-Acanthops/v0.4-falcata.md +++ b/docs/src/ReleaseHistory/v0-Acanthops/v0.4-falcata.md @@ -4,5 +4,5 @@ The `v0.4`-release was the first release available from the public registry. ## New in `v0.4` -- A `ConstantFormSpace` structure. -- A `GLMakie` extension. + - A `ConstantFormSpace` structure. + - A `GLMakie` extension. diff --git a/docs/src/ReleaseHistory/v0-Acanthops/v0.5-godmani.md b/docs/src/ReleaseHistory/v0-Acanthops/v0.5-godmani.md index f3441f225..4058f31d8 100644 --- a/docs/src/ReleaseHistory/v0-Acanthops/v0.5-godmani.md +++ b/docs/src/ReleaseHistory/v0-Acanthops/v0.5-godmani.md @@ -2,5 +2,5 @@ ## New in `v0.5` -- The `CartesianPoints` structure now allows you to specify a different iteration order. -- `Mantis` now has a `Makie` extension, instead of a `GLMakie` extension. + - The `CartesianPoints` structure now allows you to specify a different iteration order. + - `Mantis` now has a `Makie` extension, instead of a `GLMakie` extension. diff --git a/docs/src/ReleaseHistory/v0-Acanthops/v0.6-onorei.md b/docs/src/ReleaseHistory/v0-Acanthops/v0.6-onorei.md index 192b039a6..9c5fbc04f 100644 --- a/docs/src/ReleaseHistory/v0-Acanthops/v0.6-onorei.md +++ b/docs/src/ReleaseHistory/v0-Acanthops/v0.6-onorei.md @@ -2,7 +2,7 @@ ## New in `v0.6` -- A new `Hierarchy` module for all things hierarchical. -- Lagrange and Edge polynomials on more general sets of nodes. -- Several bugfixes. -- Restructured and extended documentation. + - A new `Hierarchy` module for all things hierarchical. + - Lagrange and Edge polynomials on more general sets of nodes. + - Several bugfixes. + - Restructured and extended documentation. diff --git a/docs/src/Support/Contributing.md b/docs/src/Support/Contributing.md index 67c00f265..6fbdc5be1 100644 --- a/docs/src/Support/Contributing.md +++ b/docs/src/Support/Contributing.md @@ -22,21 +22,22 @@ wrote is indeed a *contribution*. Here is what we mean by that: > [!NOTE] Contribution > > A change to the source-code of `Mantis` that either: -> 1. Adds a new feature or extends the generality of existing features. -> 2. Fixes a bug. -> 3. Adds tests for existing features previously uncovered, or fixes current tests. -> 4. Increases performance, as in faster computation times, reduced memory usage, or -> better type-stability. -> 5. Refactors existing code to improve readability or clarity. -> 6. Extends the documentation, doc-strings, or corrects outdated information, or typos. +> +> 1. Adds a new feature or extends the generality of existing features. +> 2. Fixes a bug. +> 3. Adds tests for existing features previously uncovered, or fixes current tests. +> 4. Increases performance, as in faster computation times, reduced memory usage, or +> better type-stability. +> 5. Refactors existing code to improve readability or clarity. +> 6. Extends the documentation, doc-strings, or corrects outdated information, or typos. If what you have in mind does not fall into one of these categories, then it is likely one of the following: -1. A feature request, which you can read more about [here](./FeatureRequest.md). -1. A bug report, which you can read more about [here](./SubmitBugReport.md). -3. You need help getting `Mantis` up-and-running, which you can read more about - [here](./GettingHelp.md). + 1. A feature request, which you can read more about [here](./FeatureRequest.md). + 2. A bug report, which you can read more about [here](./SubmitBugReport.md). + 3. You need help getting `Mantis` up-and-running, which you can read more about + [here](./GettingHelp.md). ## Creating a pull-request @@ -46,131 +47,127 @@ others are specific for each case. ### General guidelines -1. The code in `Mantis` follows, for the most part, the [Blue style - guide](https://github.com/JuliaDiff/BlueStyle) for Julia, with minor modifications. - Please try as best as possible to adhere to the conventions of this guide. - We recommend you set up your language-server protocol (lsp) to automatically use the - `.JuliaFormatter.toml` file in the `Mantis` repository and format the code accordingly, - for example whenever you save a file. - This will help you be consistent. -2. Write informative commit messages. These don't need to explain every line of code, that's - what diffs are for, but they should explain the overall change implemented in the commit. - (For example, `feat: add new cool geometry` or `perf: reduce evaluate allocations`). - Have a look at [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for - more information. This is what we follow. -3. Make commits with a “single” purpose. This ties in nicely with the previous point. If you - have a single commit that adds a new feature, tests and documentation for it, and fix a - typo somewhere, what identifier do you give it? `feat:`, `test:`, `doc:`? Who knows! -4. When creating the pull-request, give a brief explanation what your intent is with the - contribution. This should be only one or two sentences; the commit messages should handle - the rest! Also, please add all relevant labels to make it immediately obvious what the - intent of the pull-request is. -5. One of the rules in our repository is that we require commits to be signed. If you are - already doing that, great. If not, you can read about why it is important, and how to set - it up [here](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work). + 1. The code in `Mantis` follows, for the most part, the [Blue style + guide](https://github.com/JuliaDiff/BlueStyle) for Julia, with minor modifications. + Please try as best as possible to adhere to the conventions of this guide. + We recommend you set up your language-server protocol (lsp) to automatically use the + `.JuliaFormatter.toml` file in the `Mantis` repository and format the code accordingly, + for example whenever you save a file. + This will help you be consistent. + 2. Write informative commit messages. These don't need to explain every line of code, that's + what diffs are for, but they should explain the overall change implemented in the commit. + (For example, `feat: add new cool geometry` or `perf: reduce evaluate allocations`). + Have a look at [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for + more information. This is what we follow. + 3. Make commits with a “single” purpose. This ties in nicely with the previous point. If you + have a single commit that adds a new feature, tests and documentation for it, and fix a + typo somewhere, what identifier do you give it? `feat:`, `test:`, `doc:`? Who knows! + 4. When creating the pull-request, give a brief explanation what your intent is with the + contribution. This should be only one or two sentences; the commit messages should handle + the rest! Also, please add all relevant labels to make it immediately obvious what the + intent of the pull-request is. + 5. One of the rules in our repository is that we require commits to be signed. If you are + already doing that, great. If not, you can read about why it is important, and how to set + it up [here](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work). ### Specific guidelines The following guidelines are specific to each type of contribution, so feel free to go straight to the part that concerns your contribution. -1. [New feature](@ref) -2. [Bug fix](@ref) -3. [Performance](@ref) -4. [Refactor](@ref) -5. [Tests](@ref) -6. [Documentation](@ref) + 1. [New feature](@ref) + 2. [Bug fix](@ref) + 3. [Performance](@ref) + 4. [Refactor](@ref) + 5. [Tests](@ref) + 6. [Documentation](@ref) #### New feature -1. Part of the commits in this case will be identified by `feat:`. It's okay to make a - single `feat: add foo feature` commit adding everything that is relevant to your new - feature, but consider breaking it apart into smaller `feat: add foo feature 1`, `feat: - add foo feature 2`, ..., commits if you think it will help comprehension. -2. Properly document your feature! Take the time to write informative descriptions of new - structures, methods, or whatever new things you add. This will make the whole review - process easier for everyone. Note that you don't need to write separate `doc:` commits - for your new feature, as the documentation we mention here is not an extension to the - current one, but an integral part of understanding your contribution; it should be - included in the `feat:` commits, so that the reviewer can immediately see what the intent - of the new feature is. -3. Properly test your feature! If you are adding a new feature, then you should also add - tests that validate it. These should be small unit tests that confirm your assumptions on - what the code you wrote does. They will also help other people have confidence that your - code serves its intended purpose, including the reviewers. Unlike in the previous point, - tests should be written in separate `test:` commits, as these are not essential to - understand your feature. Instead, they serve the separate goal of validating it. + 1. Part of the commits in this case will be identified by `feat:`. It's okay to make a + single `feat: add foo feature` commit adding everything that is relevant to your new + feature, but consider breaking it apart into smaller `feat: add foo feature 1`, `feat: add foo feature 2`, ..., commits if you think it will help comprehension. + 2. Properly document your feature! Take the time to write informative descriptions of new + structures, methods, or whatever new things you add. This will make the whole review + process easier for everyone. Note that you don't need to write separate `doc:` commits + for your new feature, as the documentation we mention here is not an extension to the + current one, but an integral part of understanding your contribution; it should be + included in the `feat:` commits, so that the reviewer can immediately see what the intent + of the new feature is. + 3. Properly test your feature! If you are adding a new feature, then you should also add + tests that validate it. These should be small unit tests that confirm your assumptions on + what the code you wrote does. They will also help other people have confidence that your + code serves its intended purpose, including the reviewers. Unlike in the previous point, + tests should be written in separate `test:` commits, as these are not essential to + understand your feature. Instead, they serve the separate goal of validating it. #### Bug fix -1. Hopefully your contribution here will be small, so it should in principle fit under a - single `fix:` commit. If by any chance you happen to fix several unrelated bugs please - separate them into different commits: `fix: add missing variable 1`, `fix: correct - parameter type 2`, for example. -2. If you are correcting a bug, that means the tests were incomplete and did not cover the - case where that bug occurs. As such, consider accompanying your `fix:` commit with a - `test:` commit that would have prevented that bug from going unnoticed. + 1. Hopefully your contribution here will be small, so it should in principle fit under a + single `fix:` commit. If by any chance you happen to fix several unrelated bugs please + separate them into different commits: `fix: add missing variable 1`, `fix: correct parameter type 2`, for example. + 2. If you are correcting a bug, that means the tests were incomplete and did not cover the + case where that bug occurs. As such, consider accompanying your `fix:` commit with a + `test:` commit that would have prevented that bug from going unnoticed. #### Performance -1. Commits for this contribution should be identified with `perf:`. Since it is expected - that the contribution provides some improvement, be that in terms of speed, memory, or - type-stability, please include some sort of comparison that quantifies and showcases - exactly what the contribution is. For example - ```julia - julia> @allocations old_foo() - 42 - julia> @allocations new_foo() # Reduces the number of allocations of `old_foo`. - 0 - ``` + 1. Commits for this contribution should be identified with `perf:`. Since it is expected + that the contribution provides some improvement, be that in terms of speed, memory, or + type-stability, please include some sort of comparison that quantifies and showcases + exactly what the contribution is. For example + ```julia + julia> @allocations old_foo() + 42 + + julia> @allocations new_foo() # Reduces the number of allocations of `old_foo`. + 0 + ``` #### Refactor -1. The purpose of this contribution is to improve the readability or clarity of some part of - the code. As such, please include a one-sentence description of what you think is - improved by your change. Commits of this type should be labelled with `refactor:`. + 1. The purpose of this contribution is to improve the readability or clarity of some part of + the code. As such, please include a one-sentence description of what you think is + improved by your change. Commits of this type should be labelled with `refactor:`. #### Tests -1. As you probably guessed, commits in this contribution should be given the identifier - `test:`. If you are contributing multiple tests, with distinct purposes, please try to - break the commits apart: `test: add test for foo1`, `test: extend tests for foo2`. -2. Make your tests as atomic as possible. Rather than testing directly the output of a - complicated method, whose arguments are themselves non-trivial, it's preferable to test - each constituent method individually, desirably with simple arguments. -3. If the previous point seems impossible to achieve, then it's likely that the code you are - trying to test is too convoluted and should be distilled into several simpler methods. - Maybe a [Refactor](@ref) contribution is also due. + 1. As you probably guessed, commits in this contribution should be given the identifier + `test:`. If you are contributing multiple tests, with distinct purposes, please try to + break the commits apart: `test: add test for foo1`, `test: extend tests for foo2`. + 2. Make your tests as atomic as possible. Rather than testing directly the output of a + complicated method, whose arguments are themselves non-trivial, it's preferable to test + each constituent method individually, desirably with simple arguments. + 3. If the previous point seems impossible to achieve, then it's likely that the code you are + trying to test is too convoluted and should be distilled into several simpler methods. + Maybe a [Refactor](@ref) contribution is also due. #### Documentation -1. For this contribution, the commit label should be `doc:`. As with other types of commits, - it's good practice to keep commits separate if you are adding documentation to different - things. -2. The main purpose of documentation is to improve the reader's understanding of something. - Write in whatever way you feel that best achieves this purpose. However, it's generally - advisable to be concise and stick to simple language. -3. If possible, please include an example of what you are documenting. This is often very - helpful for the reader to fully grasp what you are explaining. For instance, consider the - difference between - ```julia - """ - aggregate(a, b) - - The aggregate of two yonder numerals, which bear the appelations `a` and `b`. - """ - ``` - and - ````julia - """ - aggregate(a, b) - - The aggregate of two yonder numerals, which bear the appelations `a` and `b`. - - # Example - ```julia-repl - julia> aggregate(1, 2) - 3 - ``` - """ - ```` + 1. For this contribution, the commit label should be `doc:`. As with other types of commits, + it's good practice to keep commits separate if you are adding documentation to different + things. + 2. The main purpose of documentation is to improve the reader's understanding of something. + Write in whatever way you feel that best achieves this purpose. However, it's generally + advisable to be concise and stick to simple language. + 3. If possible, please include an example of what you are documenting. This is often very + helpful for the reader to fully grasp what you are explaining. For instance, consider the + difference between + ```julia + """ + aggregate(a, b) + + The aggregate of two yonder numerals, which bear the appelations `a` and `b`. + """ + ``` + and + ````julia + julia> aggregate(1, 2) + """ + aggregate(a, b) + + The aggregate of two yonder numerals, which bear the appelations `a` and `b`. + + # Example + ```julia-repl + ```` diff --git a/docs/src/Support/FeatureRequest.md b/docs/src/Support/FeatureRequest.md index faa3040c8..78271a1ca 100644 --- a/docs/src/Support/FeatureRequest.md +++ b/docs/src/Support/FeatureRequest.md @@ -11,13 +11,17 @@ organised. For completeness, the template contents are included below. ### Is your feature request related to a problem? Please describe. + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] ### Describe the solution you'd like + A clear and concise description of what you want to happen. ### Describe alternatives you've considered + A clear and concise description of any alternative solutions or features you've considered. ### Additional context + Add any other context or screenshots about the feature request here. diff --git a/docs/src/Support/GettingHelp.md b/docs/src/Support/GettingHelp.md index 1a18a5218..c125bb7fb 100644 --- a/docs/src/Support/GettingHelp.md +++ b/docs/src/Support/GettingHelp.md @@ -1,6 +1,5 @@ # Getting Help - If you need help with anything `Mantis`-related then the best way of getting in touch with us is by using the [Discussions page](https://github.com/MantisFEM/Mantis.jl/discussions). Here you can ask questions, provide feedback, or help brainstorm ideas, for example. @@ -8,6 +7,6 @@ Here you can ask questions, provide feedback, or help brainstorm ideas, for exam If, however, you already know that you want to specifically submit a bug report, request a feature, or want to contribute, you can find more information in the following pages: -1. [Submitting a bug report.](./SubmitBugReport.md) -2. [Requesting a new feature.](./FeatureRequest.md) -3. [Contributing.](./Contributing.md) + 1. [Submitting a bug report.](./SubmitBugReport.md) + 2. [Requesting a new feature.](./FeatureRequest.md) + 3. [Contributing.](./Contributing.md) diff --git a/docs/src/Support/SubmitBugReport.md b/docs/src/Support/SubmitBugReport.md index 9fc32087d..421cc3d3d 100644 --- a/docs/src/Support/SubmitBugReport.md +++ b/docs/src/Support/SubmitBugReport.md @@ -1,31 +1,39 @@ # Submitting a bug report -Unfortunately, bugs are unavoidable in software that is being actively developed. -So, should you run into a bug, please let us know! You can submit an issue on our -[GitHub issue page](https://github.com/MantisFEM/Mantis.jl/issues). Please use the -'bug report'-template to help us keep it structured and have a look at the + +Unfortunately, bugs are unavoidable in software that is being actively developed. +So, should you run into a bug, please let us know! You can submit an issue on our +[GitHub issue page](https://github.com/MantisFEM/Mantis.jl/issues). Please use the +'bug report'-template to help us keep it structured and have a look at the [dedicated issue on submitting a bug report](https://github.com/MantisFEM/Mantis.jl/issues/289). When submitting an issue, please include the following (this is part of the template): ### First checks -1. Make sure the bug is from the `Mantis` code and not other modifications. -2. Check the open issues to see if someone else has already spotted the same bug. Look for the `bug`-label to help your search. + + 1. Make sure the bug is from the `Mantis` code and not other modifications. + 2. Check the open issues to see if someone else has already spotted the same bug. Look for the `bug`-label to help your search. ### Expected behaviour + A clear and concise description of what you expected to happen. ### Observed behaviour + Prints of the output you obtain. ### Describe the bug + A clear and concise description of what the bug is. ### To Reproduce -1. Indicate the `Mantis` release used. -2. Provide the minimal but necessary code to encounter the error. + + 1. Indicate the `Mantis` release used. + 2. Provide the minimal but necessary code to encounter the error. ### Screenshots + If applicable, add screenshots to help explain your problem. ### Additional context + Add any other context about the problem here. diff --git a/docs/src/index.md b/docs/src/index.md index 1a42ee1c4..e05fa64e7 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -41,19 +41,23 @@ Tchebycheffian B-splines), and adaptively-refinable spaces (e.g., hierarchical B [EUPL licence](https://github.com/MantisFEM/Mantis.jl/blob/main/LICENSE). The `Mantis` package was created by -- Diogo C. Cabanas, -- Joey Dekker, -- Artur Palha, -- Deepesh Toshniwal, -from TU Delft's Institute of Applied Mathematics (DIAM). + + - Diogo C. Cabanas, + - Joey Dekker, + - Artur Palha, + - Deepesh Toshniwal, + from TU Delft's Institute of Applied Mathematics (DIAM). !!! warning "Under development" + `Mantis` is under active development and can still undergo large changes. ## References on this page + ```@bibliography Pages = ["index.md"] Canonical = false ``` -See the full [reference list](@ref References) for all references cited throughout this + +See the full [reference list](@ref References) for all references cited throughout this documentation. diff --git a/examples/src/HodgeLaplacian.jl b/examples/src/HodgeLaplacian.jl index 138b46027..fc77af09c 100644 --- a/examples/src/HodgeLaplacian.jl +++ b/examples/src/HodgeLaplacian.jl @@ -154,4 +154,3 @@ weak_form_3D = Assemblers.WeakForm(lhs_expressions_3D, rhs_expressions_3D, wfi_3 A_3D, b_3D = Assemblers.assemble(weak_form_3D, bc_3D) sol_3D = vec(A_3D \ b_3D) ϕ⁰_3D = Forms.build_form_field(Λ⁰_3D, sol_3D) - diff --git a/examples/src/ThreeBodyProblem.jl b/examples/src/ThreeBodyProblem.jl index 14916e614..ee2e8f54b 100644 --- a/examples/src/ThreeBodyProblem.jl +++ b/examples/src/ThreeBodyProblem.jl @@ -267,7 +267,7 @@ function compute_hamiltonian(x_n, v_n) U += 1.0 / r end end - K += 0.5 * (v[i]^2 + v[i+1]^2) + K += 0.5 * (v[i]^2 + v[i + 1]^2) end return K - U end @@ -332,7 +332,7 @@ lines!(ax_ham, hamiltonian_history_R; color=:green) record( fig, "three_body_problem.mp4", LinRange(t0, T, round(Int, T / dt)); framerate=30 ) do t - time[] = t + return time[] = t end # ```@raw html diff --git a/src/Analysis/ErrorComputations.jl b/src/Analysis/ErrorComputations.jl index c861ba956..e613ac2a9 100644 --- a/src/Analysis/ErrorComputations.jl +++ b/src/Analysis/ErrorComputations.jl @@ -40,7 +40,7 @@ function _compute_square_error_per_element( elseif norm == "Linf" result[elem_id] = maximum( abs.( - Forms.evaluate(difference, elem_id, Quadrature.get_nodes(quad_rule))[1][1] + Forms.evaluate(difference, elem_id, Quadrature.get_nodes(quad_rule))[1][1], ), ) else diff --git a/src/Assemblers/Assemblers.jl b/src/Assemblers/Assemblers.jl index 5ff8c9b9b..1b1733fb3 100644 --- a/src/Assemblers/Assemblers.jl +++ b/src/Assemblers/Assemblers.jl @@ -6,7 +6,8 @@ Contains all assembly-related structs and functions. module Assemblers import LinearAlgebra -import SparseArrays; const spa = SparseArrays +import SparseArrays; +const spa = SparseArrays using ..Geometry using ..Forms diff --git a/src/Assemblers/GlobalAssemblers.jl b/src/Assemblers/GlobalAssemblers.jl index 53a33a0b6..eb88e4a71 100644 --- a/src/Assemblers/GlobalAssemblers.jl +++ b/src/Assemblers/GlobalAssemblers.jl @@ -23,17 +23,19 @@ Assemble the left- and right-hand sides of a discrete Petrov-Galerkin problem fo weak-formulation and Dirichlet boundary conditions. # Arguments -- `weak_form::WeakForm{manifold_dim, LHS, RHS, I}`: The weak form to assemble. -- `dirichlet_bcs::Dict{Int, Float64}`: A dictionary containing the Dirichlet boundary + + - `weak_form::WeakForm{manifold_dim, LHS, RHS, I}`: The weak form to assemble. + - `dirichlet_bcs::Dict{Int, Float64}`: A dictionary containing the Dirichlet boundary conditions, where the key is the index of the boundary condition and the value is the boundary condition value. -- `lhs_type::Type`: The type of the left-hand side array. Default is + - `lhs_type::Type`: The type of the left-hand side array. Default is `SparseMatrixCSC{Float64, Int}`. -- `rhs_type::Type`: The type of the right-hand side array. Default is `Vector{Float64}`. + - `rhs_type::Type`: The type of the right-hand side array. Default is `Vector{Float64}`. # Returns -- `lhs::lhs_type`: The assembled left-hand side array. -- `rhs::rhs_type`: The assembled right-hand side vector. + + - `lhs::lhs_type`: The assembled left-hand side array. + - `rhs::rhs_type`: The assembled right-hand side vector. """ function assemble( weak_form::WeakForm{manifold_dim, LHS, RHS, I}, @@ -119,13 +121,15 @@ Returns pre-allocated row, column, and value vectors for the left-hand side (lhs right-hand side (rhs) array. # Arguments -- `weak_form::WeakForm`: The weak form to use for the pre-allocation. -- `side::String`: The side of the array to pre-allocate. Must be either "lhs" or "rhs". + + - `weak_form::WeakForm`: The weak form to use for the pre-allocation. + - `side::String`: The side of the array to pre-allocate. Must be either "lhs" or "rhs". # Returns -- `rows::Vector{Int}`: The pre-allocated row indices. -- `cols::Vector{Int}`: The pre-allocated column indices. -- `vals::Vector{T}`: The pre-allocated values. + + - `rows::Vector{Int}`: The pre-allocated row indices. + - `cols::Vector{Int}`: The pre-allocated column indices. + - `vals::Vector{T}`: The pre-allocated values. """ function get_pre_allocation( weak_form::WeakForm, side::String, ::Type{A} @@ -167,20 +171,22 @@ Updates the row, column, and value vectors with contributions from the specified expression at the element given by `element_id`. # Arguments -- `rows::Vector{Int}`: The row indices of the array. -- `cols::Vector{Int}`: The column indices of the array. -- `vals::AbstractVector`: The values of the array. -- `counts::Int`: The current count of non-zero entries in the array. -- `expressions`: The expression to evaluate. -- `element_id::Int`: The identifier of the element. -- `test_offsets::Int`: The offset for the test function. -- `trial_offsets::Int`: The offset for the trial function. + + - `rows::Vector{Int}`: The row indices of the array. + - `cols::Vector{Int}`: The column indices of the array. + - `vals::AbstractVector`: The values of the array. + - `counts::Int`: The current count of non-zero entries in the array. + - `expressions`: The expression to evaluate. + - `element_id::Int`: The identifier of the element. + - `test_offsets::Int`: The offset for the test function. + - `trial_offsets::Int`: The offset for the trial function. # Returns -- `rows::Vector{Int}`: The updated row indices of the array. -- `cols::Vector{Int}`: The updated column indices of the array. -- `vals::AbstractVector`: The updated values of the array. -- `counts::Int`: The updated count of non-zero entries in the array. + + - `rows::Vector{Int}`: The updated row indices of the array. + - `cols::Vector{Int}`: The updated column indices of the array. + - `vals::AbstractVector`: The updated values of the array. + - `counts::Int`: The updated count of non-zero entries in the array. """ function add_expression_contributions!( rows::Vector{Int}, @@ -226,10 +232,13 @@ For each index `i` that is a key of `dirichlet_bcs`, set the corresponding value row to zero, both in `lhs_vals` and `rhs_vals`. # Examples + ```jldoctest using Mantis -Assemblers.zero_rows!([1., 1., 1.], [1., 2., 3.], [1, 2, 3], [1, 3, 2], Dict(2 => 42.0)) +Assemblers.zero_rows!( + [1.0, 1.0, 1.0], [1.0, 2.0, 3.0], [1, 2, 3], [1, 3, 2], Dict(2 => 42.0) +) # output @@ -272,10 +281,11 @@ Set the rows to 0 and diagonals to 1 in `lhs`, and rows of `rhs` to a value, as the row indices (keys) and values of `dirichlet_bcs`. # Examples + ```jldoctest using Mantis -Assemblers.add_bc!([2.0 2.0 2.0; 2.0 2. 2.0; 2.0 2.0 2.0], zeros(3), Dict(2 => 42.0)) +Assemblers.add_bc!([2.0 2.0 2.0; 2.0 2.0 2.0; 2.0 2.0 2.0], zeros(3), Dict(2 => 42.0)) # output @@ -301,6 +311,8 @@ end Remove the rows and columns of `lhs` and `rhs` as specified by the keys of `dirichlet_bcs`. See also [`set_diagonal!`](@ref). + +``` ``` """ function add_bc!(lhs::AbstractMatrix, rhs::AbstractMatrix, dirichlet_bcs::Dict) @@ -316,10 +328,11 @@ Set the diagonals of `lhs` to 1 and rows of `rhs` to a value, as specified by th indices (keys) and values of `dirichlet_bcs`. See also [`add_bc!`](@ref). # Examples + ```jldoctest using Mantis -Assemblers.set_diagonal!([2.0 2.0 2.0; 2.0 2. 2.0; 2.0 2.0 2.0], zeros(3), Dict(2 => 42.0)) +Assemblers.set_diagonal!([2.0 2.0 2.0; 2.0 2.0 2.0; 2.0 2.0 2.0], zeros(3), Dict(2 => 42.0)) # output @@ -344,11 +357,14 @@ end Remove the rows and columns of `lhs` and `rhs` as specified by the keys of `dirichlet_bcs`. # Examples + ```jldoctest using Mantis Assemblers.set_diagonal!( - [2. 0. 0.; 0. 2. 0.; 0. 0. 2.], [2. 0. 0.; 0. 2. 0.; 0. 0. 2.], Dict(2 => 42.0) + [2.0 0.0 0.0; 0.0 2.0 0.0; 0.0 0.0 2.0], + [2.0 0.0 0.0; 0.0 2.0 0.0; 0.0 0.0 2.0], + Dict(2 => 42.0), ) # output @@ -378,14 +394,16 @@ end Returns a array of the specified type with the given row and column indices and values. # Arguments -- `array_type::Type{AbstractArray}`: The type of array to build. -- `rows::Vector{Int}`: The row indices of the array. -- `cols::Vector{Int}`: The column indices of the array. -- `vals::AbstractVector`: The values of the array. -- `size::Tuple{Int, Int}`: The size of the array. + + - `array_type::Type{AbstractArray}`: The type of array to build. + - `rows::Vector{Int}`: The row indices of the array. + - `cols::Vector{Int}`: The column indices of the array. + - `vals::AbstractVector`: The values of the array. + - `size::Tuple{Int, Int}`: The size of the array. # Returns -- `::array_type`: The constructed array of the specified type. + + - `::array_type`: The constructed array of the specified type. """ function build_array( array_type::Type{A}, @@ -394,7 +412,7 @@ function build_array( vals::AbstractVector, size::Tuple{Int, Int}, ) where {A <: AbstractArray} - throw( + return throw( ArgumentError("Assembly of array type `$(array_type)` not currently implemented.") ) end diff --git a/src/Assemblers/WeakFormulations/HodgeLaplace/0-form.jl b/src/Assemblers/WeakFormulations/HodgeLaplace/0-form.jl index f184826f5..75ba10548 100644 --- a/src/Assemblers/WeakFormulations/HodgeLaplace/0-form.jl +++ b/src/Assemblers/WeakFormulations/HodgeLaplace/0-form.jl @@ -10,16 +10,18 @@ Function for assembling the weak form of the 0-form Hodge Laplacian. # Arguments -- `inputs::AbstractInputs`: The inputs for the weak form assembly, including test, trial and + + - `inputs::AbstractInputs`: The inputs for the weak form assembly, including test, trial and forcing terms. -- `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral + - `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral evaluation. # Returns -- `lhs_expression<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: + + - `lhs_expression<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: The left-hand side of the weak form, which is a tuple of tuples contain all the blocks of the left-hand side matrix. -- `rhs_expression<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: + - `rhs_expression<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: The right-hand side of the weak form, which is a tuple of tuples contain all the blocks of the right-hand side matrix. """ @@ -43,12 +45,14 @@ end Returns the solution of the weak form of the 0-form Hodge Laplacian. # Arguments -- `X⁰`: The 0-form space to use as trial and test space. -- `fₑ`: The forcing term to use for the right-hand side of the weak formulation. -- `dΩ`: The quadrature rule to use for the assembly. + + - `X⁰`: The 0-form space to use as trial and test space. + - `fₑ`: The forcing term to use for the right-hand side of the weak formulation. + - `dΩ`: The quadrature rule to use for the assembly. # Returns -- `::Forms.FormField`: The solution of the weak-formulation. + + - `::Forms.FormField`: The solution of the weak-formulation. """ function solve_zero_form_hodge_laplacian(X⁰, fₑ, dΩ) weak_form_inputs = WeakFormInputs(X⁰, fₑ) diff --git a/src/Assemblers/WeakFormulations/HodgeLaplace/1-form.jl b/src/Assemblers/WeakFormulations/HodgeLaplace/1-form.jl index b6099288d..37007b587 100644 --- a/src/Assemblers/WeakFormulations/HodgeLaplace/1-form.jl +++ b/src/Assemblers/WeakFormulations/HodgeLaplace/1-form.jl @@ -10,16 +10,18 @@ Function for assembling the weak form of the 1-form Hodge Laplacian problem. # Arguments -- `inputs::AbstractInputs`: The inputs for the weak form assembly, including test, trial and + + - `inputs::AbstractInputs`: The inputs for the weak form assembly, including test, trial and forcing terms. -- `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral + - `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral evaluation. # Returns -- `lhs_expressions<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: + + - `lhs_expressions<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: The left-hand side of the weak form, which is a tuple of tuples contain all the blocks of the left-hand side matrix. -- `rhs_expressions<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: + - `rhs_expressions<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: The right-hand side of the weak form, which is a tuple of tuples contain all the blocks of the right-hand side matrix. """ @@ -46,14 +48,16 @@ end Returns the solution of the weak form of the 1-form Hodge Laplacian. # Arguments -- `X⁰`: The 0-form space to use as trial and test space. -- `X¹`: The 1-form space to use as trial and test space. -- `f¹`: The forcing term to use for the right-hand side of the weak formulation. -- `dΩ`: The quadrature rule to use for the assembly. + + - `X⁰`: The 0-form space to use as trial and test space. + - `X¹`: The 1-form space to use as trial and test space. + - `f¹`: The forcing term to use for the right-hand side of the weak formulation. + - `dΩ`: The quadrature rule to use for the assembly. # Returns -- `δu¹ₕ::Forms.FormField`: The 0-form solution of the weak-formulation. -- `u¹ₕ::Forms.FormField`: The 1-form solution of the weak-formulation. + + - `δu¹ₕ::Forms.FormField`: The 0-form solution of the weak-formulation. + - `u¹ₕ::Forms.FormField`: The 1-form solution of the weak-formulation. """ function solve_one_form_hodge_laplacian(X⁰, X¹, f¹, dΩ, bc_type="") weak_form_inputs = Assemblers.WeakFormInputs((X⁰, X¹), (f¹,)) @@ -89,16 +93,17 @@ end Returns the solution of the weak form of the 1-form Hodge Laplacian from an adaptive loop. # Arguments -- `complex::C`: The initial de Rham complex to use for the problem. -- `forcing_function::Function`: The function to use for the forcing term. -- `dΩₐ::Quadrature.AbstractGlobalQuadratureRule{manifold_dim}`: The quadrature rule to use + + - `complex::C`: The initial de Rham complex to use for the problem. + - `forcing_function::Function`: The function to use for the forcing term. + - `dΩₐ::Quadrature.AbstractGlobalQuadratureRule{manifold_dim}`: The quadrature rule to use for the assembly. -- `num_steps::Int`: The number of steps to use for the adaptive loop. -- `dorfler_parameter::Float64`: The parameter to use for the Dörfler marking. -- `dΩₑ::Quadrature.AbstractGlobalQuadratureRule{manifold_dim}`: The quadrature rule to use + - `num_steps::Int`: The number of steps to use for the adaptive loop. + - `dorfler_parameter::Float64`: The parameter to use for the Dörfler marking. + - `dΩₑ::Quadrature.AbstractGlobalQuadratureRule{manifold_dim}`: The quadrature rule to use for the error estimation. -- `Lchains::Bool`: Whether to use L-chains for the refinement. -- `verbose::Bool=false`: Whether to print the progress of the adaptive loop. + - `Lchains::Bool`: Whether to use L-chains for the refinement. + - `verbose::Bool=false`: Whether to print the progress of the adaptive loop. """ function solve_one_form_hodge_laplacian( complex::C, diff --git a/src/Assemblers/WeakFormulations/HodgeLaplace/n-form.jl b/src/Assemblers/WeakFormulations/HodgeLaplace/n-form.jl index f383ac04e..52011cfd9 100644 --- a/src/Assemblers/WeakFormulations/HodgeLaplace/n-form.jl +++ b/src/Assemblers/WeakFormulations/HodgeLaplace/n-form.jl @@ -10,16 +10,18 @@ Function for assembling the weak form of the n-form Hodge Laplacian problem. # Arguments -- `inputs::WeakFormInputs`: The inputs for the weak form assembly, including test, trial and + + - `inputs::WeakFormInputs`: The inputs for the weak form assembly, including test, trial and forcing terms. -- `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral + - `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral evaluation. # Returns -- `lhs_expressions<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: + + - `lhs_expressions<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: The left-hand side of the weak form, which is a tuple of tuples contain all the blocks of the left-hand side matrix. -- `rhs_expressions<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: + - `rhs_expressions<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: The right-hand side of the weak form, which is a tuple of tuples contain all the blocks of the right-hand side matrix. """ @@ -45,14 +47,16 @@ end Returns the solution of the weak form of the n-form Hodge Laplacian. # Arguments -- `Xⁿ⁻¹`: The (n-1)-form space to use as trial and test space. -- `Xⁿ`: The n-form space to use as trial and test space. -- `fₑ`: The forcing term to use for the right-hand side of the weak formulation. -- `dΩ`: The quadrature rule to use for the assembly. + + - `Xⁿ⁻¹`: The (n-1)-form space to use as trial and test space. + - `Xⁿ`: The n-form space to use as trial and test space. + - `fₑ`: The forcing term to use for the right-hand side of the weak formulation. + - `dΩ`: The quadrature rule to use for the assembly. # Returns -- `u¹ₕ`: The (n-1)-form solution of the weak-formulation. -- `ϕ²ₕ`: The n-form solution of the weak-formulation. + + - `u¹ₕ`: The (n-1)-form solution of the weak-formulation. + - `ϕ²ₕ`: The n-form solution of the weak-formulation. """ function solve_volume_form_hodge_laplacian(Xⁿ⁻¹, Xⁿ, fₑ, dΩ) weak_form_inputs = WeakFormInputs((Xⁿ⁻¹, Xⁿ), (fₑ,)) diff --git a/src/Assemblers/WeakFormulations/L2Projection/L2Projection.jl b/src/Assemblers/WeakFormulations/L2Projection/L2Projection.jl index 734bc77a7..d526c4b04 100644 --- a/src/Assemblers/WeakFormulations/L2Projection/L2Projection.jl +++ b/src/Assemblers/WeakFormulations/L2Projection/L2Projection.jl @@ -8,16 +8,18 @@ Function to compute the L2 projection of a function onto a discrete form space. # Arguments -- `inputs::AbstractInputs`: The inputs for the weak form assembly, including test, trial and + + - `inputs::AbstractInputs`: The inputs for the weak form assembly, including test, trial and forcing terms. -- `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral + - `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral evaluation. # Returns -- `lhs_expression<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: + + - `lhs_expression<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: The left-hand side of the weak form, which is a tuple of tuples contain all the blocks of the left-hand side matrix. -- `rhs_expression<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: + - `rhs_expression<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: The right-hand side of the weak form, which is a tuple of tuples contain all the blocks of the right-hand side matrix. """ @@ -39,12 +41,14 @@ end Returns the solution of the weak form of the L2 projection. # Arguments -- `Xᵏ`: The k-form space to use as trial and test space. -- `fₑ`: The forcing term to use for the right-hand side of the weak formulation. -- `dΩ`: The quadrature rule to use for the assembly. + + - `Xᵏ`: The k-form space to use as trial and test space. + - `fₑ`: The forcing term to use for the right-hand side of the weak formulation. + - `dΩ`: The quadrature rule to use for the assembly. # Returns -- `fₕ::FormField`: The projection of `fₑ` onto `Xᵏ`. + + - `fₕ::FormField`: The projection of `fₑ` onto `Xᵏ`. """ function solve_L2_projection(Xᵏ, fₑ, dΩ) weak_form_inputs = WeakFormInputs(Xᵏ, fₑ) diff --git a/src/Assemblers/WeakFormulations/MaxwellEigenvalue/MaxwellEigenvalue.jl b/src/Assemblers/WeakFormulations/MaxwellEigenvalue/MaxwellEigenvalue.jl index 4751c09c1..05fac797a 100644 --- a/src/Assemblers/WeakFormulations/MaxwellEigenvalue/MaxwellEigenvalue.jl +++ b/src/Assemblers/WeakFormulations/MaxwellEigenvalue/MaxwellEigenvalue.jl @@ -8,16 +8,18 @@ Function for assembling the weak form of the Maxwell eigenvalue problem. # Arguments -- `inputs::WeakFormInputs`: The inputs for the weak form assembly, including test and trial + + - `inputs::WeakFormInputs`: The inputs for the weak form assembly, including test and trial spaces. -- `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral + - `dΩ::Quadrature.AbstractGlobalQuadratureRule`: The quadrature rule to use for the integral evaluation. # Returns -- `lhs_expression<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: + + - `lhs_expression<:NTuple{num_lhs_rows, NTuple{num_lhs_cols, AbstractRealValuedOperator}}`: The left-hand side of the weak form, which is a tuple of tuples contain all the blocks of the left-hand side matrix. -- `rhs_expression<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: + - `rhs_expression<:NTuple{num_rhs_rows, NTuple{num_rhs_cols, AbstractRealValuedOperator}}`: The right-hand side of the weak form, which is a tuple of tuples contain all the blocks of the right-hand side matrix. """ @@ -42,13 +44,15 @@ end Evaluates the analytical Maxwell eigenfunction for the eigenmode `(m, n)` at points `x`. # Arguments -- `m::Int`: Eigenmode component x. -- `n::Int`: Eigenmode component y. -- `scale_factors::NTuple{2, Int}`: Scaling factors based on the size of the domain. -- `x::Matrix{Float64}`: Evaluation points. + + - `m::Int`: Eigenmode component x. + - `n::Int`: Eigenmode component y. + - `scale_factors::NTuple{2, Int}`: Scaling factors based on the size of the domain. + - `x::Matrix{Float64}`: Evaluation points. # Returns -- `NTuple{2, Vector{Float64}}`: The evaluated Maxwell eigenfunction at points `x`. + + - `NTuple{2, Vector{Float64}}`: The evaluated Maxwell eigenfunction at points `x`. """ function analytical_maxwell_eigenfunction( m::Int, n::Int, scale_factors::NTuple{2, Float64}, x::Matrix{Float64} @@ -74,12 +78,14 @@ end Returns the first `num_eig` eigenvalues and 1-form eigenfunctions on the geometry `geom`. # Arguments -- `num_eig::Int`: Number of eigenvalues and eigenfunctions to compute. -- `geom::Geometry.AbstractGeometry{2}`: The two-dimensional geometry. + + - `num_eig::Int`: Number of eigenvalues and eigenfunctions to compute. + - `geom::Geometry.AbstractGeometry{2}`: The two-dimensional geometry. # Returns -- `Vector{Float64}`: The first `num_eig` analytical eigenvalues. -- `Vector{Forms.AnalyticalFormField{2, 1, G}`: The first `num_eig` analytical + + - `Vector{Float64}`: The first `num_eig` analytical eigenvalues. + - `Vector{Forms.AnalyticalFormField{2, 1, G}`: The first `num_eig` analytical eigenfunctions. """ function get_analytical_maxwell_eig( @@ -120,15 +126,17 @@ Returns the first `num_eig` eigenvalues and 1-form eigenfunctions of the Maxwell problem. # Arguments -- `X⁰::Forms.AbstractFormSpace{2, 0}`: The 0-form space to use as trial and test space. -- `X¹::Forms.AbstractFormSpace{2, 1}`: The 1-form space to use as trial and test space. -- `dΩ::Quadrature.AbstractGlobalQuadratureRule{2}`: The quadrature rule to use for the assembly. -- `num_eig::Int`: The number of eigenvalues and eigenfunctions to compute. -- `verbose::Bool=false`: Whether to print the nullspace offset. + + - `X⁰::Forms.AbstractFormSpace{2, 0}`: The 0-form space to use as trial and test space. + - `X¹::Forms.AbstractFormSpace{2, 1}`: The 1-form space to use as trial and test space. + - `dΩ::Quadrature.AbstractGlobalQuadratureRule{2}`: The quadrature rule to use for the assembly. + - `num_eig::Int`: The number of eigenvalues and eigenfunctions to compute. + - `verbose::Bool=false`: Whether to print the nullspace offset. # Returns -- `ω²ₕ::Vector{Float64}`: The first `num_eig` eigenvalues. -- `u¹ₕ::Vector{Forms.FormField{2, 1}}`: The first `num_eig` eigenfunctions. + + - `ω²ₕ::Vector{Float64}`: The first `num_eig` eigenvalues. + - `u¹ₕ::Vector{Forms.FormField{2, 1}}`: The first `num_eig` eigenfunctions. """ function solve_maxwell_eig( X⁰::Forms.AbstractFormSpace{2, 0}, @@ -165,8 +173,9 @@ function solve_maxwell_eig( u¹ₕ[eig_id] = Forms.FormField( X¹, zeros(Forms.get_num_basis(X¹)), original_label * subscript_str ) - u¹ₕ[eig_id].coefficients[non_boundary_rows_cols] .= - real.(eig_vecs[:, nullspace_offset + eig_id]) + u¹ₕ[eig_id].coefficients[non_boundary_rows_cols] .= real.( + eig_vecs[:, nullspace_offset + eig_id] + ) end return ωₕ², u¹ₕ @@ -190,22 +199,24 @@ Returns the first `num_eig` eigenvalues and 1-form eigenfunctions of the Maxwell problem for an adaptive loop. # Arguments -- `complex::C`: The initial de Rham complex to use for the problem. -- `dΩₐ::Quadrature.StandardQuadrature{manifold_dim}`: The quadrature rule to use for the + + - `complex::C`: The initial de Rham complex to use for the problem. + - `dΩₐ::Quadrature.StandardQuadrature{manifold_dim}`: The quadrature rule to use for the assembly. -- `num_steps::Int`: The number of adaptive steps to perform. -- `dorfler_parameter::Float64`: The Dörfler marking parameter. -- `dΩₑ::Quadrature.StandardQuadrature{manifold_dim}`: The quadrature rule to use for the + - `num_steps::Int`: The number of adaptive steps to perform. + - `dorfler_parameter::Float64`: The Dörfler marking parameter. + - `dΩₑ::Quadrature.StandardQuadrature{manifold_dim}`: The quadrature rule to use for the error estimation. -- `Lchains::Bool`: Whether to use L-chains for the refinement. -- `eigenfunction::Int`: The index of the eigenfunction to use for the error estimation. -- `num_eig::Int`: The number of eigenvalues and eigenfunctions to compute. -- `scale_factors::NTuple{manifold_dim, Float64}`: The scaling factors for the geometry. -- `verbose::Bool=false`: Whether to print the progress of the adaptive loop. + - `Lchains::Bool`: Whether to use L-chains for the refinement. + - `eigenfunction::Int`: The index of the eigenfunction to use for the error estimation. + - `num_eig::Int`: The number of eigenvalues and eigenfunctions to compute. + - `scale_factors::NTuple{manifold_dim, Float64}`: The scaling factors for the geometry. + - `verbose::Bool=false`: Whether to print the progress of the adaptive loop. # Returns -- `ω²ₕ::Vector{Float64}`: The first `num_eig` eigenvalues. -- `u¹ₕ::Vector{Forms.FormField{manifold_dim, 1, G}}`: The first `num_eig` eigenfunctions. + + - `ω²ₕ::Vector{Float64}`: The first `num_eig` eigenvalues. + - `u¹ₕ::Vector{Forms.FormField{manifold_dim, 1, G}}`: The first `num_eig` eigenfunctions. """ function solve_maxwell_eig( complex::C, @@ -244,7 +255,9 @@ function solve_maxwell_eig( H⁰, dorfler_marking ) if Lchains - domains = FunctionSpaces.update_domains_with_lchains!(H⁰, marked_elements_per_level) + domains = FunctionSpaces.update_domains_with_lchains!( + H⁰, marked_elements_per_level + ) complex = Forms.update_hierarchical_de_rham_complex(complex, domains) else complex = Forms.update_hierarchical_de_rham_complex( diff --git a/src/Assemblers/WeakFormulations/WeakForm.jl b/src/Assemblers/WeakFormulations/WeakForm.jl index c837dc191..638ded84b 100644 --- a/src/Assemblers/WeakFormulations/WeakForm.jl +++ b/src/Assemblers/WeakFormulations/WeakForm.jl @@ -11,19 +11,24 @@ operators; these are defined from a set of inputs holding the test, trial and fo and a constructor method that defines where the blocks are placed. # Fields -- `lhs_expressions::LHS`: The left-hand side blocks of the weak-formulation. -- `rhs_expressions::RHS`: The right-hand side blocks of the weak-formulation. -- `inputs::I`: The inputs for the weak-formulation, which include the test and trial spaces, + + - `lhs_expressions::LHS`: The left-hand side blocks of the weak-formulation. + - `rhs_expressions::RHS`: The right-hand side blocks of the weak-formulation. + - `inputs::I`: The inputs for the weak-formulation, which include the test and trial spaces, and forcing terms. + # Type parameters -- `manifold_dim::Int`: The dimension of the manifold where the weak-formulation is defined. -- `LHS`: The type of the left-hand side expressions. Each row-column entry should be a + + - `manifold_dim::Int`: The dimension of the manifold where the weak-formulation is defined. + - `LHS`: The type of the left-hand side expressions. Each row-column entry should be a subtype of `AbstractRealValuedOperator` or `0`. -- `RHS`: The type of the right-hand side expressions. Each row-column entry should be a + - `RHS`: The type of the right-hand side expressions. Each row-column entry should be a subtype of `AbstractRealValuedOperator` or `0`. -- `I`: The type of the inputs. It should be a subtype of `WeakFormInputs{manifold_dim}`. + - `I`: The type of the inputs. It should be a subtype of `WeakFormInputs{manifold_dim}`. + # Inner constructors -- `WeakForm(inputs::I, constructor::F)`: Creates a new `WeakForm` instance with the given + + - `WeakForm(inputs::I, constructor::F)`: Creates a new `WeakForm` instance with the given inputs and constructor function. The constructor function is used to generate the left-hand side and right-hand side blocks of real-valued operators. """ @@ -125,10 +130,12 @@ get_lhs_size(wf::WeakForm) = get_test_size(wf), get_trial_size(wf) Returns the offsets of the test function spaces used in the weak form. # Arguments -- `wf::WeakForm{manifold_dim, LHS, RHS, I}`: The weak form being used. + + - `wf::WeakForm{manifold_dim, LHS, RHS, I}`: The weak form being used. # Returns -- `NTuple{num_rows, Int}`: The offsets of the test function spaces. + + - `NTuple{num_rows, Int}`: The offsets of the test function spaces. """ function get_test_offsets( wf::WeakForm{manifold_dim, LHS, RHS, I} @@ -170,10 +177,12 @@ end Returns the offsets of the trial function spaces used in the weak form. # Arguments -- `wf::WeakForm{manifold_dim, LHS, RHS, I}`: The weak form being used. + + - `wf::WeakForm{manifold_dim, LHS, RHS, I}`: The weak form being used. # Returns -- `NTuple{lhs_num_cols, lhs_num_cols}`: The offsets of the trial function spaces. + + - `NTuple{lhs_num_cols, lhs_num_cols}`: The offsets of the trial function spaces. """ function get_trial_offsets( wf::WeakForm{manifold_dim, LHS, RHS, I} @@ -208,11 +217,13 @@ Returns the estimated number of non-zero entries per element for the left- and r sides of the weak-formulation. # Arguments -- `wf::WeakForm`: The weak-formulation for which the estimated number of non-zero entries is + + - `wf::WeakForm`: The weak-formulation for which the estimated number of non-zero entries is to be determined. # Returns -- `Tuple(Int, Int)`: The estimated number of non-zero entries per element for the left-hand + + - `Tuple(Int, Int)`: The estimated number of non-zero entries per element for the left-hand side and right-hand side of the weak-formulation, respectively. """ function get_estimated_nnz_per_elem(wf::WeakForm) @@ -243,10 +254,12 @@ end Returns the number of elements over which the discrete weak-formulation is defined. # Arguments -- `wf::WeakForm`: The weak-formulation for which the number of elements is to be determined. + + - `wf::WeakForm`: The weak-formulation for which the number of elements is to be determined. # Returns -- `::Int`: The number of elements. + + - `::Int`: The number of elements. """ function get_num_elements(wf::WeakForm) for lhs_row in get_lhs_expressions(wf), expression in lhs_row @@ -257,7 +270,9 @@ function get_num_elements(wf::WeakForm) return Forms.get_num_elements(expression) end - throw(ArgumentError("No elements found in the left-hand side of the weak-formulation.")) + return throw( + ArgumentError("No elements found in the left-hand side of the weak-formulation.") + ) end """ @@ -267,11 +282,13 @@ Returns the maximum number of elements over which the weak form blocks are evalu is the max over all lhs and rhs expression blocks. # Arguments -- `wf::WeakForm`: The weak-formulation for which the number of quadrature elements is to be + + - `wf::WeakForm`: The weak-formulation for which the number of quadrature elements is to be determined. # Returns -- `::Int`: The number of quadrature elements. + + - `::Int`: The number of quadrature elements. """ function get_num_evaluation_elements(wf::WeakForm) num_eval_elements = 0 diff --git a/src/Assemblers/WeakFormulations/WeakFormInputs.jl b/src/Assemblers/WeakFormulations/WeakFormInputs.jl index 7749527b0..af6c83dc8 100644 --- a/src/Assemblers/WeakFormulations/WeakFormInputs.jl +++ b/src/Assemblers/WeakFormulations/WeakFormInputs.jl @@ -4,37 +4,42 @@ Container for test and trial spaces, and forcing terms to be used in a weak-formulation. # Fields -- `test_forms::TeF`: The test forms for the weak-formulation. -- `trial_forms::TrF`: The trial forms for the weak-formulation. -- `forcings::F`: The forcing terms for the weak-formulation, possibly nothing. + + - `test_forms::TeF`: The test forms for the weak-formulation. + - `trial_forms::TrF`: The trial forms for the weak-formulation. + - `forcings::F`: The forcing terms for the weak-formulation, possibly nothing. + # Type parameters -- `manifold_dim::Int`: The dimension of the manifold where the weak-formulation is defined. -- `TeF`: The type of the tupe of test forms. Each entry should be a subtype of + + - `manifold_dim::Int`: The dimension of the manifold where the weak-formulation is defined. + - `TeF`: The type of the tupe of test forms. Each entry should be a subtype of `Forms.AbstractFormSpace`. -- `TrF`: The type of the tupe of trial forms. Each entry should be a subtype of + - `TrF`: The type of the tupe of trial forms. Each entry should be a subtype of `Forms.AbstractFormSpace`. -- `F`: The type of the tupe of forcing terms. Each entry should be a subtype of + - `F`: The type of the tupe of forcing terms. Each entry should be a subtype of `Forms.AbstractFormField`. + # Inner constructors -- `WeakFormInputs(test_forms::TeF, trial_forms::TrF, forcings::F)`: Creates a new + + - `WeakFormInputs(test_forms::TeF, trial_forms::TrF, forcings::F)`: Creates a new `WeakFormInputs` instance with the given test forms, trial forms, and forcing terms. -- `WeakFormInputs(test_forms::TeF, trial_forms::TrF)`: Creates a new `WeakFormInputs` + - `WeakFormInputs(test_forms::TeF, trial_forms::TrF)`: Creates a new `WeakFormInputs` instance with the given test forms and trial forms. The forcing terms are set to nothing. -- `WeakFormInputs(test_forms::TeF, trial_forms::TrF, forcing::F)`: Creates a new - `WeakFormInputs` instance from a single test and trial space and a forcing term. -- `WeakFormInputs(test_forms::TeF, trial_forms::TrF)`: Creates a new `WeakFormInputs` + - `WeakFormInputs(test_forms::TeF, trial_forms::TrF, forcing::F)`: Creates a new + `WeakFormInputs` instance from a single test and trial space and a forcing term. + - `WeakFormInputs(test_forms::TeF, trial_forms::TrF)`: Creates a new `WeakFormInputs` instance from a single test and trial space. The forcing terms are set to nothing. -- `WeakFormInputs(forms::TrF, forcing::F)`: Creates a new `WeakFormInputs` instance with the + - `WeakFormInputs(forms::TrF, forcing::F)`: Creates a new `WeakFormInputs` instance with the given trial spaces and forcing terms. The test spaces are set to the same as the trial spaces. -- `WeakFormInputs(forms::TrF)`: Creates a new `WeakFormInputs` instance with the given trial + - `WeakFormInputs(forms::TrF)`: Creates a new `WeakFormInputs` instance with the given trial spaces. The test spaces are set to the same as the trial spaces and the forcing terms are set to nothing. -- `WeakFormInputs(forms::TrF, forcing::F)`: Creates a new `WeakFormInputs` instance with a + - `WeakFormInputs(forms::TrF, forcing::F)`: Creates a new `WeakFormInputs` instance with a single trial space and forcing term. The test space is set to the same as the trial space. -- `WeakFormInputs(forms::TrF)`: Creates a new `WeakFormInputs` instance with a single trial + - `WeakFormInputs(forms::TrF)`: Creates a new `WeakFormInputs` instance with a single trial space. The test space is set to the same as the trial space and the forcing term is set to nothing. """ diff --git a/src/Forms/FormExpressions/ConstantFormSpaces.jl b/src/Forms/FormExpressions/ConstantFormSpaces.jl index 36e758025..0f3357b2a 100644 --- a/src/Forms/FormExpressions/ConstantFormSpaces.jl +++ b/src/Forms/FormExpressions/ConstantFormSpaces.jl @@ -12,9 +12,11 @@ This can, for instance, be used as a Lagrange multiplier enforcing a zero-averag constraint on another differential form. # Constructors -- `ConstantFormSpace(form_rank::Int, geometry::G, label::L)`: Generic constructor. + + - `ConstantFormSpace(form_rank::Int, geometry::G, label::L)`: Generic constructor. # Example + ```jldoctest julia> using Mantis @@ -23,20 +25,23 @@ julia> geometry = Geometry.create_cartesian_box((0.0, 0.0), (1.0, 1.0), (4, 4)); julia> Λ⁰ₕ = Forms.ConstantFormSpace(0, geometry, "0-form"); # 0-form constant on geometry. julia> Λ²ₕ = Forms.ConstantFormSpace(2, geometry, "2-form"); # 2-form constant on geometry. + ``` # Fields -- `geometry::G`: The geometry [Geometry.AbstractGeometry](@ref) on which the + + - `geometry::G`: The geometry [Geometry.AbstractGeometry](@ref) on which the `ConstantFormSpace` should be created. The `manifold_dim` will be inherited from this geometry. -- `label::L`: Label for the constant form space. This will be used in export and plotting + - `label::L`: Label for the constant form space. This will be used in export and plotting functions to easily identify the form. # Type parameters -- `manifold_dim`: Dimension of the manifold. -- `form_rank`: Rank of the differential form. -- `G`: Type of the geometry (a [Geometry.AbstractGeometry](@ref)). -- `L`: Type of the label (an `AbstractString`). + + - `manifold_dim`: Dimension of the manifold. + - `form_rank`: Rank of the differential form. + - `G`: Type of the geometry (a [Geometry.AbstractGeometry](@ref)). + - `L`: Type of the label (an `AbstractString`). """ struct ConstantFormSpace{manifold_dim, form_rank, G, L} <: AbstractFormSpace{manifold_dim, form_rank} @@ -78,10 +83,8 @@ get_form_space_tree(form::ConstantFormSpace) = (get_form(form_space),) get_geometry(form::ConstantFormSpace) = form.geometry function get_fe_space(::ConstantFormSpace) - throw( - ArgumentError( - "ConstantFormSpace does not have an associated finite element space.", - ), + return throw( + ArgumentError("ConstantFormSpace does not have an associated finite element space.") ) end @@ -90,9 +93,7 @@ end ############################################################################################ function evaluate( - ::ConstantFormSpace{manifold_dim, 0}, - ::Int, - xi::Points.AbstractPoints{manifold_dim}, + ::ConstantFormSpace{manifold_dim, 0}, ::Int, xi::Points.AbstractPoints{manifold_dim} ) where {manifold_dim} num_evaluation_points = Points.get_num_points(xi) return [ones(Float64, num_evaluation_points, 1)], [[1]] diff --git a/src/Forms/FormExpressions/FormFields.jl b/src/Forms/FormExpressions/FormFields.jl index ff21d6a6f..db6f589e3 100644 --- a/src/Forms/FormExpressions/FormFields.jl +++ b/src/Forms/FormExpressions/FormFields.jl @@ -9,18 +9,16 @@ Represents a differential form field, i.e., a differential form with `coefficien `form_space`. Note that this is considered a field, and thus to **not** have a basis. # Constructors -- `FormField( - form_space::FS, - coefficients::Vector{Float64}=zeros(get_num_basis(form_space)), - label::AbstractString=get_label(form_space), - )`: General constructor for form fields. Note that the coefficients default to zero if + + - `FormField( form_space::FS, coefficients::Vector{Float64}=zeros(get_num_basis(form_space)), label::AbstractString=get_label(form_space), )`: General constructor for form fields. Note that the coefficients default to zero if not given, and that the label also has a default. # Example + ```jldoctest julia> using Mantis -julia> B = FunctionSpaces.create_bspline_space((0.0, 0.0), (1.0, 1.0), (4, 4), (3, 3), (2,2)); +julia> B = FunctionSpaces.create_bspline_space((0.0, 0.0), (1.0, 1.0), (4, 4), (3, 3), (2, 2)); julia> Λ⁰ₕ = Forms.FormSpace(0, B, "0-form"); # 0-form space with B as basis. @@ -29,18 +27,21 @@ julia> coefficients = ones(Forms.get_num_basis(Λ⁰ₕ)); # Create some coeffic julia> α⁰ₕ = Forms.FormField(Λ⁰ₕ, coefficients, "0-form-field"); # 0-form field with Λ⁰ₕ as basis and all ones as coefficients. julia> β⁰ₕ = Forms.FormField(Λ⁰ₕ); # 0-form field with Λ⁰ₕ as basis and all zero coefficients. + ``` # Fields -- `form_space::FS`: The form space associated with this field. -- `coefficients::Vector{Float64}`: Coefficients of the form field. -- `label::AbstractString`: Label for the form field. + + - `form_space::FS`: The form space associated with this field. + - `coefficients::Vector{Float64}`: Coefficients of the form field. + - `label::AbstractString`: Label for the form field. # Type parameters -- `manifold_dim`: Dimension of the manifold. -- `form_rank`: Rank of the differential form. -- `FS`: Type of the form space. -- `L`: Type of the label (an `AbstractString`). + + - `manifold_dim`: Dimension of the manifold. + - `form_rank`: Rank of the differential form. + - `FS`: Type of the form space. + - `L`: Type of the label (an `AbstractString`). """ struct FormField{manifold_dim, form_rank, FS, L} <: AbstractFormField{manifold_dim, form_rank} @@ -62,8 +63,8 @@ struct FormField{manifold_dim, form_rank, FS, L} <: ") must match the number of basis functions (", get_num_basis(form_space), ") in the space, but doesn't.", - ) - ) + ), + ), ) end @@ -84,10 +85,12 @@ domain. See the [documentation on the Geometry module](@ref DocGeometryModule) f difference between the domains used in `Mantis`. # Constructors -- `AnalyticalFormField(form_rank::Int, expression::E, geometry::G, label::AbstractString)`: + + - `AnalyticalFormField(form_rank::Int, expression::E, geometry::G, label::AbstractString)`: General constructor for analytical form fields. # Example + ```jldoctest julia> using Mantis @@ -102,18 +105,21 @@ julia> geometry = Geometry.create_cartesian_box((0.0, 0.0), (1.0, 1.0), (4, 4)); julia> α⁰ₕ = Forms.AnalyticalFormField(0, my_form_expression, geometry, "Analytical 0-form"); julia> α²ₕ = Forms.AnalyticalFormField(2, my_form_expression, geometry, "Analytical 2-form"); + ``` # Fields -- `geometry::G`: The geometry associated with this field. -- `expression::E`: The expression defining the form field. -- `label::AbstractString`: Label for the form field. + + - `geometry::G`: The geometry associated with this field. + - `expression::E`: The expression defining the form field. + - `label::AbstractString`: Label for the form field. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `G`: Type of the geometry. -- `E`: Type of the expression. -- `L`: Type of the label (an `AbstractString`). + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `G`: Type of the geometry. + - `E`: Type of the expression. + - `L`: Type of the label (an `AbstractString`). """ struct AnalyticalFormField{manifold_dim, form_rank, G, E, L} <: AbstractFormField{manifold_dim, form_rank} @@ -142,10 +148,12 @@ get_form(form_field::FormField) = form_field.form_space Returns the coefficients of the form field. # Arguments -- `form_field::FormField`: The form field. + + - `form_field::FormField`: The form field. # Returns -- `Vector{Float64}`: The coefficients of the form field. + + - `Vector{Float64}`: The coefficients of the form field. """ get_coefficients(form_field::FormField) = form_field.coefficients @@ -155,10 +163,12 @@ get_coefficients(form_field::FormField) = form_field.coefficients Returns the number of coefficients of the form field. # Arguments -- `form_field::FormField`: The form field. + + - `form_field::FormField`: The form field. # Returns -- `Int`: The number of coefficients (dofs) of the form field. + + - `Int`: The number of coefficients (dofs) of the form field. """ get_num_coefficients(form_field::FormField) = size(form_field.coefficients, 1) @@ -169,10 +179,12 @@ Returns the expression of the analytical form field. Remember that the expressio defined in the physical domain. See [`AnalyticalFormField`](@ref) for the details. # Arguments -- `form_field::AnalyticalFormField`: The analytical form field. + + - `form_field::AnalyticalFormField`: The analytical form field. # Returns -- `<:Function`: The expression of the analytical form field. + + - `<:Function`: The expression of the analytical form field. """ get_expression(form_field::AnalyticalFormField) = form_field.expression @@ -188,9 +200,7 @@ function evaluate( xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim, form_rank, FS} n_form_components = binomial(manifold_dim, form_rank) - form_basis_eval, form_basis_indices = evaluate( - get_form(form_field), element_idx, xi - ) + form_basis_eval, form_basis_indices = evaluate(get_form(form_field), element_idx, xi) form_eval = Vector{Vector{Float64}}(undef, n_form_components) form_field_coefficients = get_coefficients(form_field) for form_component_idx in 1:n_form_components @@ -223,10 +233,12 @@ Internal function to evaluate an analytical form field, by first pulling back th the canonical domain. The used pull-back is dictated by the `form_rank`. # Arguments -- See [evaluate](@ref) for the details. + + - See [evaluate](@ref) for the details. # Returns -- See [evaluate](@ref) for the details. + + - See [evaluate](@ref) for the details. """ function _evaluate( form_field::AnalyticalFormField{manifold_dim, 0}, diff --git a/src/Forms/FormExpressions/FormSpaces.jl b/src/Forms/FormExpressions/FormSpaces.jl index e5454a16e..d8ce36ba8 100644 --- a/src/Forms/FormExpressions/FormSpaces.jl +++ b/src/Forms/FormExpressions/FormSpaces.jl @@ -13,32 +13,37 @@ basis, the `form_rank` of the `FormSpace` will dictate the behaviour of the form it a ``0``-form, ``1``-form, etc.) and thus its properties. # Constructors -- `FormSpace(form_rank::Int, fem_space::F, label::AbstractString)`: General constructor. + + - `FormSpace(form_rank::Int, fem_space::F, label::AbstractString)`: General constructor. # Example + ```jldoctest julia> using Mantis -julia> B = FunctionSpaces.create_bspline_space((0.0, 0.0), (1.0, 1.0), (4, 4), (3, 3), (2,2)); +julia> B = FunctionSpaces.create_bspline_space((0.0, 0.0), (1.0, 1.0), (4, 4), (3, 3), (2, 2)); julia> Λ⁰ₕ = Forms.FormSpace(0, B, "0-form"); # 0-form with B as basis. julia> Λ²ₕ = Forms.FormSpace(2, B, "2-form"); # 2-form with B as basis. + ``` # Fields -- `fem_space::F`: The finite element space [FunctionSpaces.AbstractFESpace](@ref) used as + + - `fem_space::F`: The finite element space [FunctionSpaces.AbstractFESpace](@ref) used as basis for this form. From this space, the `manifold_dim` and geometry are inherited. Additionally, the `num_components` of the function space must be consistent with the provided `form_rank` and the `manifold_dim`, i.e., a real-valued ``0``-form has 1 component (in any dimension), a ``1``-form in 3D has 3 components, etc. -- `label::AbstractString`: Label for the form space. This will be used in export and + - `label::AbstractString`: Label for the form space. This will be used in export and plotting functions to easily identify the form. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F`: Type of the finite element space (a [FunctionSpaces.AbstractFESpace](@ref)). -- `L`: Type of the label (an `AbstractString`). + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F`: Type of the finite element space (a [FunctionSpaces.AbstractFESpace](@ref)). + - `L`: Type of the label (an `AbstractString`). """ struct FormSpace{manifold_dim, form_rank, F, L} <: AbstractFormSpace{manifold_dim, form_rank} @@ -133,14 +138,16 @@ end Evaluate the form basis functions and their arbitrary derivatives in canonical coordinates. # Arguments -- `form_space::FormSpace{manifold_dim, form_rank}`: The form space. -- `element_idx::Int`: Index of the element where the evaluation is performed. -- `xi::Points.AbstractPoints{manifold_dim}`: Canonical points for evaluation. + + - `form_space::FormSpace{manifold_dim, form_rank}`: The form space. + - `element_idx::Int`: Index of the element where the evaluation is performed. + - `xi::Points.AbstractPoints{manifold_dim}`: Canonical points for evaluation. # Returns -- `local_form_basis::Vector{Vector{Vector{Matrix{Float64}}}}`: The basis functions evaluated + + - `local_form_basis::Vector{Vector{Vector{Matrix{Float64}}}}`: The basis functions evaluated at the canonical coordinates of the element. -- `::Vector{Vector{Int}}`: The basis functions evaluated at the canonical coordinates of the + - `::Vector{Vector{Int}}`: The basis functions evaluated at the canonical coordinates of the element. """ function _evaluate_form_in_canonical_coordinates( @@ -175,14 +182,16 @@ end Pullback the basis functions to the canonical coordinates of the element. # Arguments -- `geometry::Geometry.AbstractGeometry{manifold_dim}`: The geometry of the form space. -- `form_evaluations::Vector{Vector{Vector{Matrix{Float64}}}}`: The basis functions evaluated + + - `geometry::Geometry.AbstractGeometry{manifold_dim}`: The geometry of the form space. + - `form_evaluations::Vector{Vector{Vector{Matrix{Float64}}}}`: The basis functions evaluated at the parametric coordinates. -- `element_idx::Int`: Index of the element to evaluate. -- `form_rank::Int`: Rank of the form. + - `element_idx::Int`: Index of the element to evaluate. + - `form_rank::Int`: Rank of the form. # Returns -- `form_evaluations::Vector{Vector{Vector{Matrix{Float64}}}}`: The form evaluations + + - `form_evaluations::Vector{Vector{Vector{Matrix{Float64}}}}`: The form evaluations pulled-back to canonical coordinates. """ function _pullback_to_canonical_coordinates( diff --git a/src/Forms/FormOperators/Algebraic.jl b/src/Forms/FormOperators/Algebraic.jl index 4f11b08ac..dbde13b89 100644 --- a/src/Forms/FormOperators/Algebraic.jl +++ b/src/Forms/FormOperators/Algebraic.jl @@ -9,21 +9,24 @@ Unary, algebraic transformation of an `AbstractRealValuedOperator`. # Constructors -- `UnaryOperatorTransformation(operator::O, transformation::T)`: General constructor. -- `Base.:*(factor::Number, operator::AbstractRealValuedOperator)`: Point-wise + + - `UnaryOperatorTransformation(operator::O, transformation::T)`: General constructor. + - `Base.:*(factor::Number, operator::AbstractRealValuedOperator)`: Point-wise multiplication of an operator with a number. -- `Base.:-(operator::AbstractRealValuedOperator)`: Point-wise additive inverse of an + - `Base.:-(operator::AbstractRealValuedOperator)`: Point-wise additive inverse of an operator. # Fields -- `operator::O`: The operator to which the transformation is applied. -- `transformation::T`: The transformation to apply to the operator. + + - `operator::O`: The operator to which the transformation is applied. + - `transformation::T`: The transformation to apply to the operator. # Type parameters -- `manifold_dim`: See [AbstractRealValuedOperator](@ref) for the details. -- `O <: AbstractRealValuedOperator{manifold_dim}`: Type of the original real-valued - operator. -- `T <: Function`: Function defining the algebraic transformation. + + - `manifold_dim`: See [AbstractRealValuedOperator](@ref) for the details. + - `O <: AbstractRealValuedOperator{manifold_dim}`: Type of the original real-valued + operator. + - `T <: Function`: Function defining the algebraic transformation. """ struct UnaryOperatorTransformation{manifold_dim, O, T} <: AbstractRealValuedOperator{manifold_dim} @@ -44,31 +47,35 @@ struct UnaryOperatorTransformation{manifold_dim, O, T} <: end """ - BinaryOperatorTransformation{manifold_dim, O1, O2, T} <: - AbstractRealValuedOperator{manifold_dim} +BinaryOperatorTransformation{manifold_dim, O1, O2, T} <: +AbstractRealValuedOperator{manifold_dim} Binary, algebraic transformation acting on two real-valued operators. !!! warning + The basis underlying each operator must compatible, this is checked. If not compatible an ArgumentError is thrown. # Constructors -- `BinaryOperatorTransformation(operator_1::O1, operator_2::O2, transformation::T )`: + + - `BinaryOperatorTransformation(operator_1::O1, operator_2::O2, transformation::T )`: General constructor. -- `Base.:+(operator_1::O1, operator_1::O2)`: Point-wise sum of two operators. -- `Base.:-(operator_1::O1, operator_2::O2)`: Point-wise difference of two operators. + - `Base.:+(operator_1::O1, operator_1::O2)`: Point-wise sum of two operators. + - `Base.:-(operator_1::O1, operator_2::O2)`: Point-wise difference of two operators. # Fields -- `operator_1::O1`: The first real-valued operator. -- `operator_2::O2`: The second real-valued operator. -- `transformation::T`: The transformation to apply to the operators. + + - `operator_1::O1`: The first real-valued operator. + - `operator_2::O2`: The second real-valued operator. + - `transformation::T`: The transformation to apply to the operators. # Type parameters -- `manifold_dim`: See [AbstractRealValuedOperator](@ref) for the details. -- `O1 <: AbstractRealValuedOperator{manifold_dim}`: The type of the first operator. -- `O2 <: AbstractRealValuedOperator{manifold_dim}`: The type of the second operator. -- `T <: Function`: The type of the algebraic transformation. + + - `manifold_dim`: See [AbstractRealValuedOperator](@ref) for the details. + - `O1 <: AbstractRealValuedOperator{manifold_dim}`: The type of the first operator. + - `O2 <: AbstractRealValuedOperator{manifold_dim}`: The type of the second operator. + - `T <: Function`: The type of the algebraic transformation. """ struct BinaryOperatorTransformation{manifold_dim, O1, O2, T} <: AbstractRealValuedOperator{manifold_dim} @@ -119,25 +126,28 @@ end Unary, algebraic transformation of a differential form expression. # Constructors -- `UnaryFormTransformation(form::F, transformation::T, label::String)`: + + - `UnaryFormTransformation(form::F, transformation::T, label::String)`: General constructor. -- `Base.:-(form::AbstractForm)`: Point-wise additive inverse of a form. -- `Base.:*(factor::Number, form::AbstractForm)`: Point-wise multiplication of a form with a + - `Base.:-(form::AbstractForm)`: Point-wise additive inverse of a form. + - `Base.:*(factor::Number, form::AbstractForm)`: Point-wise multiplication of a form with a number. -- `Base.:*(form::AbstractForm, factor::Number)`: Point-wise multiplication of a form with a + - `Base.:*(form::AbstractForm, factor::Number)`: Point-wise multiplication of a form with a number. # Fields -- `form::F`: The differential form expression to which the transformation is applied. -- `transformation::T`: The transformation function to apply to the form. -- `label::L`: The label to associate with the resulting transformed form. + + - `form::F`: The differential form expression to which the transformation is applied. + - `transformation::T`: The transformation function to apply to the form. + - `label::L`: The label to associate with the resulting transformed form. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F <: AbstractForm{manifold_dim, form_rank, expression_rank}`: The type of the original + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F <: AbstractForm{manifold_dim, form_rank, expression_rank}`: The type of the original form expression . -- `T <: Function`: The type of the algebraic transformation. -- `L <: AbstractString`: The type of the label. + - `T <: Function`: The type of the algebraic transformation. + - `L <: AbstractString`: The type of the label. """ struct UnaryFormTransformation{manifold_dim, form_rank, expression_rank, F, T, L} <: AbstractForm{manifold_dim, form_rank, expression_rank} @@ -181,17 +191,20 @@ end Binary, algebraic transformation acting on two differential form expressions. !!! warning "Compatibility of forms" + When using these binary operations, you have to ensure that the operation makes sense for the given input. This is **not** checked! # Constructors -- `BinaryFormTransformation(form_1::F1, form_2::F2, transformation::T, label::AbstractString)`: General + + - `BinaryFormTransformation(form_1::F1, form_2::F2, transformation::T, label::AbstractString)`: General constructor. -- `Base.:+(form_1::AbstractForm, form_2::AbstractForm)`: Point-wise sum of two forms. -- `Base.:-(form_1::AbstractForm, form_2::AbstractForm)`: Point-wise difference of two froms. -- `Base.:*(form_1::AbstractForm, form_2::AbstractForm)`: Point-wise product of two forms. + - `Base.:+(form_1::AbstractForm, form_2::AbstractForm)`: Point-wise sum of two forms. + - `Base.:-(form_1::AbstractForm, form_2::AbstractForm)`: Point-wise difference of two froms. + - `Base.:*(form_1::AbstractForm, form_2::AbstractForm)`: Point-wise product of two forms. # Examples + ```jldoctest julia> using Mantis @@ -203,23 +216,24 @@ julia> sum_example = Λ⁰ₕ + Λ⁰ₕ; julia> isa(sum_example, Forms.BinaryFormTransformation{2, 0, 1}) true - ``` # Fields -- `form_1::F1`: The first differential form expression. -- `form_2::F2`: The second differential form expression. -- `transformation::T`: The transformation to apply to the differential forms. -- `label::L`: The label to associate to the resulting differential form. + + - `form_1::F1`: The first differential form expression. + - `form_2::F2`: The second differential form expression. + - `transformation::T`: The transformation to apply to the differential forms. + - `label::L`: The label to associate to the resulting differential form. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F1 <: AbstractForm{manifold_dim, form_rank, expression_rank}`: The type of the first + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F1 <: AbstractForm{manifold_dim, form_rank, expression_rank}`: The type of the first form expression. -- `F2 <: AbstractForm{manifold_dim, form_rank, expression_rank}`: The type of the second + - `F2 <: AbstractForm{manifold_dim, form_rank, expression_rank}`: The type of the second form expression. -- `T <: Function`: The type of the algebraic transformation. -- `L <: AbstractString`: The type of the label. + - `T <: Function`: The type of the algebraic transformation. + - `L <: AbstractString`: The type of the label. """ struct BinaryFormTransformation{manifold_dim, form_rank, expression_rank, F1, F2, T, L} <: AbstractForm{manifold_dim, form_rank, expression_rank} diff --git a/src/Forms/FormOperators/Codifferential.jl b/src/Forms/FormOperators/Codifferential.jl index 3d2a5e40a..9b1e87578 100644 --- a/src/Forms/FormOperators/Codifferential.jl +++ b/src/Forms/FormOperators/Codifferential.jl @@ -12,9 +12,11 @@ The `manifold_dim` of the `CoDifferential` is inherited from the input form, whi `form_rank` is the form rank of the input form minus 1. # Constructors -- `CoDifferential(form::F)`: General constructor. + + - `CoDifferential(form::F)`: General constructor. # Examples + ```jldoctest julia> using Mantis @@ -30,17 +32,18 @@ julia> δΛ¹ₕ = δ(Λ¹ₕ); julia> isa(δΛ¹ₕ, Forms.CoDifferential{2, 0, 1}) true - ``` # Fields -- `form::F`: The form to which the codifferential is applied. -- `label::L`: The codifferential label. Adds "δ" to the label of `form`. + + - `form::F`: The form to which the codifferential is applied. + - `label::L`: The codifferential label. Adds "δ" to the label of `form`. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F <: Forms.AbstractForm{manifold_dim, form_rank+1, expression_rank}`: The type of `form`. -- `L <: AbstractString`: The type of the label. Since a "δ" is added to the label, this + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F <: Forms.AbstractForm{manifold_dim, form_rank+1, expression_rank}`: The type of `form`. + - `L <: AbstractString`: The type of the label. Since a "δ" is added to the label, this type may differ from the label type of the underlying form. """ struct CoDifferential{manifold_dim, form_rank, expression_rank, F, L} <: @@ -111,7 +114,7 @@ end function _evaluate_codifferential( form::AbstractForm{manifold_dim}, ::Int, ::Points.AbstractPoints{manifold_dim} ) where {manifold_dim} - throw(ArgumentError("Method not implement for type $(typeof(form)).")) + return throw(ArgumentError("Method not implement for type $(typeof(form)).")) end ############################################################################################ diff --git a/src/Forms/FormOperators/ExteriorDerivative.jl b/src/Forms/FormOperators/ExteriorDerivative.jl index 6712fa1d1..a43155cd8 100644 --- a/src/Forms/FormOperators/ExteriorDerivative.jl +++ b/src/Forms/FormOperators/ExteriorDerivative.jl @@ -17,10 +17,13 @@ the dimension of the manifold) returns zero. However, in `Mantis`, the construct an error instead. # Constructors -- `ExteriorDerivative(form::F)`: General constructor for any `AbstractForm`. + + - `ExteriorDerivative(form::F)`: General constructor for any `AbstractForm`. # Examples + Creating the exterior derivative of a ``0``-form: + ```jldoctest julia> using Mantis @@ -32,20 +35,21 @@ julia> dΛ⁰ₕ = d(Λ⁰ₕ); # Note that dΛ⁰ₕ is a 1-form. julia> isa(dΛ⁰ₕ, Forms.ExteriorDerivative{2, 1, 1}) true - ``` # Fields -- `form::F`: The form to which the exterior derivative is applied. Note that the form rank + + - `form::F`: The form to which the exterior derivative is applied. Note that the form rank of this form is one lower than the `form_rank` of the exterior derivative. -- `label::L`: The exterior derivative label. This is a concatenation of "d" with the label + - `label::L`: The exterior derivative label. This is a concatenation of "d" with the label of `form`. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F <: Forms.AbstractForm{manifold_dim, form_rank - 1, expression_rank}`: The type of + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F <: Forms.AbstractForm{manifold_dim, form_rank - 1, expression_rank}`: The type of `form`. -- `L <: AbstractString`: The type of the label. Since a "d" is added to the label, this + - `L <: AbstractString`: The type of the label. Since a "d" is added to the label, this type may differ from the label type of the underlying form. """ struct ExteriorDerivative{manifold_dim, form_rank, expression_rank, F, L} <: @@ -106,7 +110,7 @@ function _evaluate_exterior_derivative( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim} - throw(ArgumentError("Method not implement for type $(typeof(form)).")) + return throw(ArgumentError("Method not implement for type $(typeof(form)).")) end ############################################################################################ @@ -287,9 +291,7 @@ end ############################################################################################ function _evaluate_exterior_derivative( - ::ConstantFormSpace{manifold_dim, 0}, - ::Int, - xi::Points.AbstractPoints{manifold_dim}, + ::ConstantFormSpace{manifold_dim, 0}, ::Int, xi::Points.AbstractPoints{manifold_dim} ) where {manifold_dim} # Preallocate memory for output array n_derivative_form_components = manifold_dim diff --git a/src/Forms/FormOperators/Hodge.jl b/src/Forms/FormOperators/Hodge.jl index f11281278..13501b12f 100644 --- a/src/Forms/FormOperators/Hodge.jl +++ b/src/Forms/FormOperators/Hodge.jl @@ -12,9 +12,11 @@ The `manifold_dim` of the `Hodge` is inherited from the input form, while the `f is the `manifold_dim` minus the form rank of the input form. # Inner Constructors -- `Hodge(form::F)`: General constructor. + + - `Hodge(form::F)`: General constructor. # Examples + ```jldoctest julia> using Mantis @@ -33,18 +35,19 @@ julia> ★Λ²ₕ = ★(Λ²ₕ); julia> isa(★Λ²ₕ, Forms.Hodge{2, 0, 1}) true - ``` # Fields -- `form::F`: The form to which the hodge star is applied. -- `label::L`: The hodge star label. This is a concatenation of "★" with the label of `form`. + + - `form::F`: The form to which the hodge star is applied. + - `label::L`: The hodge star label. This is a concatenation of "★" with the label of `form`. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F <: Forms.AbstractForm{manifold_dim, manifold_dim-form_rank, expression_rank}`: The + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F <: Forms.AbstractForm{manifold_dim, manifold_dim-form_rank, expression_rank}`: The type of `form`. -- `L <: AbstractString`: The type of the label. Since a "★" is added to the label, this + - `L <: AbstractString`: The type of the label. Since a "★" is added to the label, this type may differ from the label type of the underlying form. """ struct Hodge{manifold_dim, form_rank, expression_rank, F, L} <: @@ -105,7 +108,7 @@ function _evaluate_hodge( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim, form_rank, expression_rank} - throw(ArgumentError("Method not implement for type $(typeof(form)).")) + return throw(ArgumentError("Method not implement for type $(typeof(form)).")) end ############################################################################################ diff --git a/src/Forms/FormOperators/Integral.jl b/src/Forms/FormOperators/Integral.jl index dc56bf25b..30b4c4151 100644 --- a/src/Forms/FormOperators/Integral.jl +++ b/src/Forms/FormOperators/Integral.jl @@ -7,16 +7,13 @@ Integral of a form over a manifold. # Constructors -- `Integral( - form::F, quad_rule::Q - ) where { - manifold_dim, - F <: AbstractForm{manifold_dim, manifold_dim}, - Q <: Quadrature.AbstractGlobalQuadratureRule{manifold_dim}, - }`: General constructor. + + - `Integral( form::F, quad_rule::Q ) where { manifold_dim, F <: AbstractForm{manifold_dim, manifold_dim}, Q <: Quadrature.AbstractGlobalQuadratureRule{manifold_dim}, }`: General constructor. # Examples + Basic syntax: + ```jldoctest julia> using Mantis @@ -35,11 +32,11 @@ true julia> isa(Forms.get_form(integral), Forms.FormSpace{2, 2}) true - ``` The `Integral` is more commonly used to represent inner products in combination with the [Wedge](@ref) and [Hodge](@ref) operators: + ```jldoctest julia> using Mantis @@ -58,18 +55,19 @@ true julia> isa(Forms.get_form(integral), Forms.Wedge{2, 2}) true - ``` # Fields -- `form::F`: The form expression to be integrated. -- `quad_rule::Quadrature.AbstractGlobalQuadratureRule{manifold_dim}`: The quadrature rule + + - `form::F`: The form expression to be integrated. + - `quad_rule::Quadrature.AbstractGlobalQuadratureRule{manifold_dim}`: The quadrature rule used for the integral. # Type Parameters -- `manifold_dim::Int`: The dimension of the manifold. -- `F`: The type of the form expression. -- `Q`: The type of the quadrature expression. + + - `manifold_dim::Int`: The dimension of the manifold. + - `F`: The type of the form expression. + - `Q`: The type of the quadrature expression. """ struct Integral{manifold_dim, F, Q} <: AbstractRealValuedOperator{manifold_dim} form::F @@ -115,10 +113,12 @@ const ∫ = Integral Returns the quadrature rule associated with the integral operator. # Arguments -- `integral::Integral`: The integral operator. + + - `integral::Integral`: The integral operator. # Returns -- `<:Quadrature.AbstractGlobalQuadratureRule`: Returns the quadrature rule associated with + + - `<:Quadrature.AbstractGlobalQuadratureRule`: Returns the quadrature rule associated with the integral operator. """ get_quadrature_rule(integral::Integral) = integral.quad_rule @@ -129,10 +129,12 @@ get_quadrature_rule(integral::Integral) = integral.quad_rule Returns the number of elements in the geometry associated with the integral operator. # Arguments -- `integral::Integral`: The integral operator. + + - `integral::Integral`: The integral operator. # Returns -- `::Int`: The number of elements associated with the integral operator. + + - `::Int`: The number of elements associated with the integral operator. """ function get_num_elements(integral::Integral) return Quadrature.get_num_base_elements(get_quadrature_rule(integral)) @@ -145,10 +147,12 @@ Returns the number of evaluation elements in the quadrature rule associated with integral operator. # Arguments -- `integral::Integral`: The integral operator. + + - `integral::Integral`: The integral operator. # Returns -- `::Int`: The number of evaluation elements associated with the integral operator. + + - `::Int`: The number of evaluation elements associated with the integral operator. """ function get_num_evaluation_elements(integral::Integral) return Quadrature.get_num_evaluation_elements(get_quadrature_rule(integral)) @@ -160,10 +164,12 @@ end Returns the estimated number of non-zero entries per element for the integral operator. # Arguments -- `integral::Integral`: The integral operator. + + - `integral::Integral`: The integral operator. # Returns -- `::Int`: The estimated number of non-zero entries per element associated with the integral + + - `::Int`: The estimated number of non-zero entries per element associated with the integral operator. """ function get_estimated_nnz_per_elem(integral::Integral) @@ -188,12 +194,14 @@ Evaluates the integral of a form over a given global element using a specified q rule. # Arguments -- `integral::Integral{manifold_dim, F, Q}`: The integral operator to evaluate. -- `global_element_id::Int`: The global element over which to evaluate the integral. + + - `integral::Integral{manifold_dim, F, Q}`: The integral operator to evaluate. + - `global_element_id::Int`: The global element over which to evaluate the integral. # Returns -- `integral_eval::Vector{Float64}`: The evaluated integral. -- `integral_indices::Vector{Vector{Int}}`: The indices of the evaluated integral. The length + + - `integral_eval::Vector{Float64}`: The evaluated integral. + - `integral_indices::Vector{Vector{Int}}`: The indices of the evaluated integral. The length of the outer vector depends on the `expression_rank` of the form expression. """ function evaluate( diff --git a/src/Forms/FormOperators/Pushforward.jl b/src/Forms/FormOperators/Pushforward.jl index 2e5774302..428ead2f7 100644 --- a/src/Forms/FormOperators/Pushforward.jl +++ b/src/Forms/FormOperators/Pushforward.jl @@ -8,14 +8,16 @@ Evaluate the pushforward of the vector field from the canonical to the physical The pushforward is the action of the Jacobian of the field on the field itself. # Arguments -- `vfield::Vector{Matrix{Float64}}`: A pointwise evaluated vector field. -- `jacobian::AbstractVector`: The Jacobian of the vector field evaluated at the same points + + - `vfield::Vector{Matrix{Float64}}`: A pointwise evaluated vector field. + - `jacobian::AbstractVector`: The Jacobian of the vector field evaluated at the same points as `vfield`. Each entry in the vector should contain the evaluated Jacobian at that point. This is also the default output of [Geometry.jacobian](@ref). -- `manifold_dim::Int`: The dimension of the embedding manifold. + - `manifold_dim::Int`: The dimension of the embedding manifold. # Returns -- `::Vector{Matrix{Float64}}`: The evaluated pushforward of the vector field. + + - `::Vector{Matrix{Float64}}`: The evaluated pushforward of the vector field. """ function evaluate_pushforward( vfield::Vector{Matrix{Float64}}, jacobian::AbstractVector, manifold_dim::Int @@ -50,15 +52,17 @@ field is defined in physical coordinates. See [Sharp](@ref) and [evaluate_pushforward](@ref) for the details. # Arguments -- `form::AbstractForm{manifold_dim, 1, 0}`: An expression representing + + - `form::AbstractForm{manifold_dim, 1, 0}`: An expression representing the 1-form on the manifold. -- `element_id::Int`, `xi::Points.AbstractPoints{manifold_dim}`: See [evaluate](@ref). + - `element_id::Int`, `xi::Points.AbstractPoints{manifold_dim}`: See [evaluate](@ref). # Returns -- `evaluated_pushforward::Vector{Matrix{Float64}}`: Each component of the vector, stores the + + - `evaluated_pushforward::Vector{Matrix{Float64}}`: Each component of the vector, stores the evaluated pushforward of the sharp of the 1-form. The size of each matrix is (number of evaluation points)x(number of basis functions). -- `sharp_indices::Vector{Vector{Int}}`: Each component of the vector, stores the indices of + - `sharp_indices::Vector{Vector{Int}}`: Each component of the vector, stores the indices of the evaluated basis functions. """ function evaluate_sharp_pushforward( diff --git a/src/Forms/FormOperators/Sharp.jl b/src/Forms/FormOperators/Sharp.jl index 2c3fd9d4f..298e4ed58 100644 --- a/src/Forms/FormOperators/Sharp.jl +++ b/src/Forms/FormOperators/Sharp.jl @@ -8,9 +8,11 @@ Represents the sharp operator, which converts a differential 1-form into a vector field. # Constructors -- `Sharp(form::F)`: General constructor. + + - `Sharp(form::F)`: General constructor. # Examples + ```jldoctest julia> using Mantis @@ -28,15 +30,16 @@ julia> ♯β¹ₕ = ♯(β¹ₕ); # This is no longer a form! julia> isa(♯β¹ₕ, Forms.Sharp) true - ``` # Fields -- `form::F`: The differential 1-form to be converted into a vector field. + + - `form::F`: The differential 1-form to be converted into a vector field. # Type Parameters -- `manifold_dim`: The dimension of the manifold. -- `F`: The type of the differential 1-form. + + - `manifold_dim`: The dimension of the manifold. + - `F`: The type of the differential 1-form. """ struct Sharp{manifold_dim, F} form::F @@ -89,17 +92,19 @@ manifold, converting the form into a vector field. Note that both the 1-form and vector-field are defined in reference, curvilinear coordinates. # Arguments -- `sharp::Sharp{manifold_dim}`: The sharp structure containing the form to be evaluated. -- `element_id::Int`: The identifier of the element on which the sharp is to be evaluated. -- `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain at which to + + - `sharp::Sharp{manifold_dim}`: The sharp structure containing the form to be evaluated. + - `element_id::Int`: The identifier of the element on which the sharp is to be evaluated. + - `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain at which to evaluate the form. See [Geometry](@ref) and [Points](@ref) for more details on the canonical domain and point structure. # Returns -- `::Vector{Matrix{Float64}}`: Each component of the vector, corresponding to each ∂ᵢ, + + - `::Vector{Matrix{Float64}}`: Each component of the vector, corresponding to each ∂ᵢ, stores the sharp evaluation. The size of each matrix is (number of evaluation points)x(number of basis functions). -- `::Vector{Vector{Int}}`: Each component of the vector, corresponding to each ∂ᵢ, stores + - `::Vector{Vector{Int}}`: Each component of the vector, corresponding to each ∂ᵢ, stores the indices of the evaluated basis functions. """ function evaluate( diff --git a/src/Forms/FormOperators/Wedge.jl b/src/Forms/FormOperators/Wedge.jl index 48976ff0f..2b6fe36e1 100644 --- a/src/Forms/FormOperators/Wedge.jl +++ b/src/Forms/FormOperators/Wedge.jl @@ -14,9 +14,11 @@ respective ranks of the input forms. If the `expression_rank` of the `Wedge` wou larger than 2, an error is thrown. # Constructors -- `Wedge(form_1::F1, form_2::F2)`: General constructor. + + - `Wedge(form_1::F1, form_2::F2)`: General constructor. # Examples + ```jldoctest julia> using Mantis @@ -30,19 +32,20 @@ julia> wedged = Λ⁰ₕ ∧ Λ²ₕ; # Wedge operator between the two spaces. julia> isa(wedged, Forms.Wedge{2, 2, 2}) true - ``` # Fields -- `form_1::F1`: The first form. -- `form_2::F2`: The second form. -- `label::L`: A label for the wedge. + + - `form_1::F1`: The first form. + - `form_2::F2`: The second form. + - `label::L`: A label for the wedge. # Type parameters -- `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. -- `F1 <: Forms.AbstractForm`: The type of `form_1`. -- `F2 <: Forms.AbstractForm`: The type of `form_2`. -- `L <: AbstractString`: The type of the label. Since a "∧" is added to the label, this + + - `manifold_dim`, `form_rank`, `expression_rank`: See [AbstractForm](@ref) for the details. + - `F1 <: Forms.AbstractForm`: The type of `form_1`. + - `F2 <: Forms.AbstractForm`: The type of `form_2`. + - `L <: AbstractString`: The type of the label. Since a "∧" is added to the label, this type may differ from the label type of the underlying form. """ struct Wedge{manifold_dim, form_rank, expression_rank, F1, F2, L} <: @@ -112,7 +115,9 @@ function get_forms(form::Wedge) end function get_form(form::Wedge) - throw(ArgumentError("'get_form' is not defined for Wedges. Use 'get_forms' instead.")) + return throw( + ArgumentError("'get_form' is not defined for Wedges. Use 'get_forms' instead.") + ) end function get_geometry(form::Wedge) @@ -147,7 +152,7 @@ function _evaluate_wedge( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim} - throw(ArgumentError("Method not implemented for the given form expressions.")) + return throw(ArgumentError("Method not implemented for the given form expressions.")) end ############################################################################################ diff --git a/src/Forms/Forms.jl b/src/Forms/Forms.jl index 44b5505a6..371c337e6 100644 --- a/src/Forms/Forms.jl +++ b/src/Forms/Forms.jl @@ -58,14 +58,16 @@ export ∧, Wedge, get_forms Supertype for all form expressions representing differential forms. # Type parameters -- `manifold_dim`: Dimension of the manifold on which the form lives. This will always be + + - `manifold_dim`: Dimension of the manifold on which the form lives. This will always be inherited from the underlying function space or geometry. -- `form_rank`: The rank of the form, i.e. ``0``-form, ``1``-form, ``2``-form, etc. -- `expression_rank`: The number of bases present in an expression. Is ``0`` if no bases are + - `form_rank`: The rank of the form, i.e. ``0``-form, ``1``-form, ``2``-form, etc. + - `expression_rank`: The number of bases present in an expression. Is ``0`` if no bases are present, ``1`` for a single basis, and ``2`` for two bases. Expression ranks larger than ``2`` are not allowed. !!! note "Non-zero expression rank does not mean that the full expression has a basis." + If `expression_rank` is larger than ``0``, this means that the expression acts on at least one basis, but not necessarily that there is a basis for the total expression. For example, applying the exterior derivative to a `FormSpace` will @@ -172,10 +174,12 @@ and `γ`, if all have expression_rank > 1. If, e.g., `α` has expression_rank = returns the spaces of `β` and `γ`. # Arguments -- `form_space::AbstractFormSpace`: The AbstractFormSpace structure. + + - `form_space::AbstractFormSpace`: The AbstractFormSpace structure. # Returns -- `::Tuple(<:AbstractForm)`: The list of forms present in the tree of the expression. + + - `::Tuple(<:AbstractForm)`: The list of forms present in the tree of the expression. """ function get_form_space_tree(form::AbstractFormSpace) return get_form_space_tree(get_form(form)) @@ -214,6 +218,7 @@ If a single form is given, returns the geometry of that form. If additional form checks if the number of elements is the same between them; throws an error if not. !!! warning + Even if the number of elements is the same, the geometries might be incompatible. """ function get_geometry(single_form::AbstractForm, additional_forms::AbstractForm...) @@ -234,10 +239,12 @@ end Returns the number of elements in the geometry of the given form expression. # Arguments -- `form::AbstractForm`: The form expression. + + - `form::AbstractForm`: The form expression. # Returns -- `Int`: The number of elements in the geometry of the form expression. + + - `Int`: The number of elements in the geometry of the form expression. """ function get_num_elements(form::AbstractForm) return Geometry.get_num_elements(get_geometry(form)) @@ -252,10 +259,12 @@ end Returns the estimated number of non-zero entries per element for the given form expression. # Arguments -- `form::AbstractForm`: The form expression. + + - `form::AbstractForm`: The form expression. # Returns -- `::Int`: The estimated number of non-zero entries per element. + + - `::Int`: The estimated number of non-zero entries per element. """ function get_estimated_nnz_per_elem(form::AbstractForm) return prod(get_estimated_nnz_per_elem.(get_forms(form))) @@ -276,10 +285,12 @@ Compute an upper bound of the element-local dimension of `form_space`. Note that necessarily a tight upper bound. # Arguments -- `form_space::AbstractFormSpace`: The form space. + + - `form_space::AbstractFormSpace`: The form space. # Returns -- `::Int`: The element-local upper bound. + + - `::Int`: The element-local upper bound. """ function get_max_local_dim(form_space::AbstractFormSpace) return FunctionSpaces.get_max_local_dim(get_fe_space(form_space)) @@ -293,10 +304,12 @@ recurses untill it finds a form (usually a [`FormSpace`](@ref)) which has an und finite element space. # Arguments -- `form_space::AbstractForm`: The form space. + + - `form_space::AbstractForm`: The form space. # Returns -- `<:FunctionSpaces.AbstractFESpace`: The finite element space. + + - `<:FunctionSpaces.AbstractFESpace`: The finite element space. """ function get_fe_space(form::FS) where {FS <: AbstractForm} if hasfield(FS, :fem_space) @@ -313,10 +326,12 @@ Returns the number of basis functions of the function space associated with the space. # Arguments -- `form_space::AbstractFormSpace`: The form space. + + - `form_space::AbstractFormSpace`: The form space. # Returns -- `Int`: The number of basis functions of the function space. + + - `Int`: The number of basis functions of the function space. """ function get_num_basis(form_space::AbstractFormSpace) return get_num_basis(get_form(form_space)) @@ -329,10 +344,12 @@ Returns the number of basis functions at the given element of the function space the given form space. # Arguments -- `form_space::AbstractFormSpace`: The form space. + + - `form_space::AbstractFormSpace`: The form space. # Returns -- `Int`: The number of basis functions at the given element. + + - `Int`: The number of basis functions at the given element. """ function get_num_basis(form_space::AbstractFormSpace, element_id::Int) return get_num_basis(get_form(form_space), element_id) @@ -351,30 +368,35 @@ end Evaluate any form (expression) on the given `element_id` at the given points `xi`. !!! note "Evaluation in the canonical domain." + The evaluation of a form (expression) is always done in the canonical domain, not the physical domain. See the [documentation on the Geometry module](@ref DocGeometryModule) for more details on these domains. # Arguments -- `form::AbstractForm{manifold_dim}`: The differential form space. -- `element_id::Int`: The global element id. See [Geometry](@ref) for the details. -- `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain at which to + + - `form::AbstractForm{manifold_dim}`: The differential form space. + - `element_id::Int`: The global element id. See [Geometry](@ref) for the details. + - `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain at which to evaluate the form. See [Geometry](@ref) and [Points](@ref) for more details on the canonical domain and point structure. # Returns -- `Vector{Array{Float64, expression_rank+1}}`: Vector of length equal to the number of + + - `Vector{Array{Float64, expression_rank+1}}`: Vector of length equal to the number of components of the form, where each entry is a `Array{Float64, expression_rank+1}` (so, a `Vector` for `AbstractFormField`s and a `Matrix` for `AbstractFormSpace`s) of size `(num_evaluation_points,)`, `(num_evaluation_points, num_basis_functions_on_element)`, respectively. For expressions involving two forms (such as the `wedge`), the entries will be of type `Array{Float64, 1 + expression_rank_1 + expression_rank_2}` -- `form_basis_indices::Vector{Vector{Int}}`: The indices of the underlying function space + - `form_basis_indices::Vector{Vector{Int}}`: The indices of the underlying function space that have been evaluated (the inner vector), per basis (the outer vector). For `AbstractFormField`s (things without a basis), this will always be [[1]]. # Examples + Evaluating a ``0``-form: + ```jldoctest julia> using Mantis @@ -387,8 +409,10 @@ julia> xi = Points.CartesianPoints((LinRange(0.0, 1.0, 2), LinRange(0.0, 1.0, 3) julia> Forms.evaluate(Λ⁰ₕ, 1, xi) ([[1.0 0.0 … 0.0 0.0; 0.0 0.5 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.25 0.25]], [[1, 2, 3, 5, 6, 7, 9, 10, 11]]) ``` + Evaluating a ``2``-form in 2D (a top form). Note how the result is scaled by the pullback to the canonical domain. + ```jldoctest julia> using Mantis @@ -407,7 +431,7 @@ function evaluate( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim} - throw(MethodError(evaluate, (form, element_id, xi))) + return throw(MethodError(evaluate, (form, element_id, xi))) end ############################################################################################ diff --git a/src/Forms/FormsHelpers.jl b/src/Forms/FormsHelpers.jl index b3a50369a..fe8be455f 100644 --- a/src/Forms/FormsHelpers.jl +++ b/src/Forms/FormsHelpers.jl @@ -40,7 +40,9 @@ end function build_form_fields( form_spaces::FS; labels::Union{L, Nothing}=nothing ) where { - num_forms, FS <: NTuple{num_forms, AbstractFormSpace}, L <: NTuple{num_forms, AbstractString} + num_forms, + FS <: NTuple{num_forms, AbstractFormSpace}, + L <: NTuple{num_forms, AbstractString}, } if isnothing(labels) labels = ntuple(num_forms) do _ @@ -70,9 +72,7 @@ function build_form_fields( form_fields = ntuple(num_forms) do i num_coeffs = get_num_basis(form_spaces[i]) ff = build_form_field( - form_spaces[i], - coeffs[start_id:(start_id + num_coeffs - 1)]; - label=labels[i], + form_spaces[i], coeffs[start_id:(start_id + num_coeffs - 1)]; label=labels[i] ) start_id += num_coeffs @@ -92,11 +92,13 @@ end Generate all possible k-form basis index combinations. # Arguments -- `manifold_dim::Int`: the dimension of the manifold. -- `form_rank::Int`: the rank of the form. + + - `manifold_dim::Int`: the dimension of the manifold. + - `form_rank::Int`: the rank of the form. # Returns -- `NTuple{binomial(manifold_dim, form_rank), Vector{Int}}`: the basis index combinations. + + - `NTuple{binomial(manifold_dim, form_rank), Vector{Int}}`: the basis index combinations. """ function get_basis_index_combinations(manifold_dim::Int, form_rank::Int) @@ -128,15 +130,17 @@ end Create a tensor-product B-spline de Rham complex. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. -- `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. -- `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. -- `section_spaces::NTuple{manifold_dim, F}`: the section spaces. -- `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. -- `geometry::G`: the geometry of the domain. + + - `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. + - `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. + - `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. + - `section_spaces::NTuple{manifold_dim, F}`: the section spaces. + - `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. + - `geometry::G`: the geometry of the domain. # Returns -- `::Tuple{<:AbstractFormSpace{manifold_dim, form_rank}}`: Tuple with the form + + - `::Tuple{<:AbstractFormSpace{manifold_dim, form_rank}}`: Tuple with the form spaces of the complex, for each `form_rank` from `0` to `manifold_dim+1`. """ function create_tensor_product_bspline_de_rham_complex( @@ -198,7 +202,7 @@ end num_elements::NTuple{manifold_dim, Int}, section_spaces::NTuple{manifold_dim, F}, regularities::NTuple{manifold_dim, Int}, - mapping::M, + mapping::M, ) where { manifold_dim, F <: FunctionSpaces.AbstractCanonicalSpace, @@ -208,15 +212,17 @@ end Create a tensor-product B-spline de Rham complex. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. -- `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. -- `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. -- `section_spaces::NTuple{manifold_dim, F}`: the section spaces. -- `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. -- `mapping::M`: the mapping that applied to be base geometry. + + - `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. + - `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. + - `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. + - `section_spaces::NTuple{manifold_dim, F}`: the section spaces. + - `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. + - `mapping::M`: the mapping that applied to be base geometry. # Returns -- `::Tuple{<:AbstractFormSpace{manifold_dim, form_rank}}`: Tuple with the form + + - `::Tuple{<:AbstractFormSpace{manifold_dim, form_rank}}`: Tuple with the form spaces of the complex, for each `form_rank` from `0` to `manifold_dim+1`. """ function create_tensor_product_bspline_de_rham_complex( @@ -288,14 +294,16 @@ end Create a tensor-product B-spline de Rham complex. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. -- `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. -- `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. -- `degrees::NTuple{manifold_dim, Int}`: the degrees of the B-spline spaces. -- `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. + + - `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. + - `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. + - `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. + - `degrees::NTuple{manifold_dim, Int}`: the degrees of the B-spline spaces. + - `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. # Returns -- `Vector{AbstractFormSpace}`: the `manifold_dim+1` form spaces of the complex. + + - `Vector{AbstractFormSpace}`: the `manifold_dim+1` form spaces of the complex. """ function create_tensor_product_bspline_de_rham_complex( starting_points::NTuple{manifold_dim, Float64}, @@ -325,14 +333,16 @@ end Create a tensor-product B-spline de Rham complex on a crazy mesh. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. -- `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. -- `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. -- `section_spaces::NTuple{manifold_dim, F}`: the section spaces. -- `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. + + - `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. + - `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. + - `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. + - `section_spaces::NTuple{manifold_dim, F}`: the section spaces. + - `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. # Returns -- `Vector{AbstractFormSpace}`: the `manifold_dim+1` form spaces of the complex. + + - `Vector{AbstractFormSpace}`: the `manifold_dim+1` form spaces of the complex. """ function create_curvilinear_tensor_product_bspline_de_rham_complex( starting_points::NTuple{manifold_dim, Float64}, @@ -361,14 +371,16 @@ end Create a tensor-product B-spline de Rham complex on a crazy geometry. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. -- `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. -- `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. -- `degrees::NTuple{manifold_dim, Int}`: the degrees of the B-spline spaces. -- `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. + + - `starting_points::NTuple{manifold_dim, Float64}`: the starting points of the domain. + - `box_sizes::NTuple{manifold_dim, Float64}`: the sizes of the domain. + - `num_elements::NTuple{manifold_dim, Int}`: the number of elements in each direction. + - `degrees::NTuple{manifold_dim, Int}`: the degrees of the B-spline spaces. + - `regularities::NTuple{manifold_dim, Int}`: the regularities of the B-spline spaces. # Returns -- `Vector{AbstractFormSpace}`: the `manifold_dim+1` form spaces of the complex. + + - `Vector{AbstractFormSpace}`: the `manifold_dim+1` form spaces of the complex. """ function create_curvilinear_tensor_product_bspline_de_rham_complex( starting_points::NTuple{manifold_dim, Float64}, @@ -393,21 +405,21 @@ end ############################################################################################ """ - create_hierarchical_de_rham_complex( - starting_points::NTuple{manifold_dim, Float64}, - box_sizes::NTuple{manifold_dim, Float64}, - num_elements::NTuple{manifold_dim, Int}, - section_spaces::NTuple{manifold_dim, F}, - regularities::NTuple{manifold_dim, Int}, - num_subdivisions::NTuple{manifold_dim, Int}, - truncate::Bool, - simplified::Bool, - geometry::G, - ) where { - manifold_dim, - F <: FunctionSpaces.AbstractCanonicalSpace, - G <: Geometry.AbstractGeometry{manifold_dim}, - } + create_hierarchical_de_rham_complex( + starting_points::NTuple{manifold_dim, Float64}, + box_sizes::NTuple{manifold_dim, Float64}, + num_elements::NTuple{manifold_dim, Int}, + section_spaces::NTuple{manifold_dim, F}, + regularities::NTuple{manifold_dim, Int}, + num_subdivisions::NTuple{manifold_dim, Int}, + truncate::Bool, + simplified::Bool, + geometry::G, + ) where { + manifold_dim, + F <: FunctionSpaces.AbstractCanonicalSpace, + G <: Geometry.AbstractGeometry{manifold_dim}, + } Construct a hierarchical discrete de Rham complex of finite element spaces over a tensor-product geometry, equivalent to a Cartesian grid, in `manifold_dim` dimensions. @@ -419,7 +431,8 @@ See also [`create_tensor_product_bspline_de_rham_complex`](@ref) and [`FunctionSpaces.HierarchicalFiniteElementSpace`](@ref). # Returns -- A tuple with the `manifold_dim + 1` spaces that form the de Rham complex. + + - A tuple with the `manifold_dim + 1` spaces that form the de Rham complex. """ function create_hierarchical_de_rham_complex( starting_points::NTuple{manifold_dim, Float64}, @@ -510,9 +523,9 @@ function create_hierarchical_de_rham_complex( end """ - update_hierarchical_de_rham_complex( - complex::C, data - ) where {num_forms, C <: NTuple{num_forms, AbstractFormSpace}} + update_hierarchical_de_rham_complex( + complex::C, data + ) where {num_forms, C <: NTuple{num_forms, AbstractFormSpace}} Returns a refined hierarchical de Rham complex, based on the given `complex` and refinement `data`. The input `data` should have a dedicated method in @@ -521,14 +534,16 @@ Returns a refined hierarchical de Rham complex, based on the given `complex` and See also [`FunctionSpaces.refine_space`](@ref). # Arguments -- `complex::C`: The hierarchical B-spline de Rham complex. -- `data`: The information used for refinement. Examples include domains denoting active + + - `complex::C`: The hierarchical B-spline de Rham complex. + - `data`: The information used for refinement. Examples include domains denoting active elements, of type [`Hierarchy.ActiveInfo`](@ref), or elements marked for refinement, of type `Vector{Vector{Int}}`. # Returns -- `new_complex<:NTuple{num_forms, AbstractFormSpace}`:A tuple with the `manifold_dim + 1` - refined spaces that form the de Rham complex. + + - `new_complex<:NTuple{num_forms, AbstractFormSpace}`:A tuple with the `manifold_dim + 1` + refined spaces that form the de Rham complex. """ function update_hierarchical_de_rham_complex( complex::C, data @@ -541,7 +556,7 @@ function update_hierarchical_de_rham_complex( comp_spaces = FunctionSpaces.get_component_spaces(get_fe_space(complex[k])) new_space = FunctionSpaces.DirectSumSpace( ntuple(num_components) do c - FunctionSpaces.refine_space(comp_spaces[c], data) + return FunctionSpaces.refine_space(comp_spaces[c], data) end, ) end @@ -569,17 +584,19 @@ end Create a polar B-spline de Rham complex. # Arguments -- `num_elements::NTuple{2, Int}`: the number of elements in each direction. -- `degrees::NTuple{2, Int}`: the degrees of the B-spline spaces. -- `regularities::NTuple{2, Int}`: the regularities of the B-spline spaces. -- `R::Float64`: the radius of the domain. -- `refine::Bool=false`: whether to refine the domain. -- `geom_coeffs_tp::Union{Nothing, Array{Float64,3}}=nothing`: the geometry coefficients. + + - `num_elements::NTuple{2, Int}`: the number of elements in each direction. + - `degrees::NTuple{2, Int}`: the degrees of the B-spline spaces. + - `regularities::NTuple{2, Int}`: the regularities of the B-spline spaces. + - `R::Float64`: the radius of the domain. + - `refine::Bool=false`: whether to refine the domain. + - `geom_coeffs_tp::Union{Nothing, Array{Float64,3}}=nothing`: the geometry coefficients. # Returns -- `::Vector{AbstractFormSpace}`: the 3 form spaces of the complex. -- `::Vector{NTuple{N,SparseMatrixCSC{Float64,Int}} where {N}}`: the global extraction operators. -- `::NTuple{2, Array{Float64,3}}`: the geometry coefficients for the underlying tensor-product B-spline spaces. + + - `::Vector{AbstractFormSpace}`: the 3 form spaces of the complex. + - `::Vector{NTuple{N,SparseMatrixCSC{Float64,Int}} where {N}}`: the global extraction operators. + - `::NTuple{2, Array{Float64,3}}`: the geometry coefficients for the underlying tensor-product B-spline spaces. """ function create_polar_spline_de_rham_complex( num_elements::NTuple{2, Int}, @@ -616,17 +633,19 @@ end Create a polar B-spline de Rham complex. # Arguments -- `num_elements::NTuple{2, Int}`: the number of elements in each direction. -- `section_spaces::F`: the section spaces. -- `regularities::NTuple{2, Int}`: the regularities of the B-spline spaces. -- `R::Float64`: the radius of the domain. -- `refine::Bool=false`: whether to refine the domain. -- `geom_coeffs_tp::Union{Nothing, Array{Float64,3}}=nothing`: the geometry coefficients. + + - `num_elements::NTuple{2, Int}`: the number of elements in each direction. + - `section_spaces::F`: the section spaces. + - `regularities::NTuple{2, Int}`: the regularities of the B-spline spaces. + - `R::Float64`: the radius of the domain. + - `refine::Bool=false`: whether to refine the domain. + - `geom_coeffs_tp::Union{Nothing, Array{Float64,3}}=nothing`: the geometry coefficients. # Returns -- `::Vector{AbstractFormSpace}`: the 3 form spaces of the complex. -- `::Vector{NTuple{N,SparseMatrixCSC{Float64,Int}} where {N}}`: the global extraction operators. -- `::NTuple{2, Array{Float64,3}}`: the geometry coefficients for the underlying tensor-product B-spline spaces. + + - `::Vector{AbstractFormSpace}`: the 3 form spaces of the complex. + - `::Vector{NTuple{N,SparseMatrixCSC{Float64,Int}} where {N}}`: the global extraction operators. + - `::NTuple{2, Array{Float64,3}}`: the geometry coefficients for the underlying tensor-product B-spline spaces. """ function create_polar_spline_de_rham_complex( num_elements::NTuple{2, Int}, @@ -708,11 +727,13 @@ end Creates a dictionary of Dirichlet boundary conditions for a given form space. # Arguments -- `form::AbstractFormSpace`: The form for which to compute the boundary conditions. -- `value::Float64`: The value of the Dirichlet boundary condition. + + - `form::AbstractFormSpace`: The form for which to compute the boundary conditions. + - `value::Float64`: The value of the Dirichlet boundary condition. # Returns -- `::Dict{Int, Float64}`: The dictionary of Dirichlet boundary conditions. + + - `::Dict{Int, Float64}`: The dictionary of Dirichlet boundary conditions. """ function set_dirichlet_boundary_conditions(form::AbstractFormSpace, value::Float64) return Dict{Int, Float64}(i => value for i in trace_basis_idxs(form)) @@ -726,10 +747,12 @@ end Creates a list of basis function idxs which control the trace of the form on the boundary. # Arguments -- `form::AbstractForm`: The form for which to compute the boundary conditions. + + - `form::AbstractForm`: The form for which to compute the boundary conditions. # Returns -- `Vector{Int}`: The list of basis idxs. + + - `Vector{Int}`: The list of basis idxs. """ function trace_basis_idxs( form::AbstractForm{manifold_dim, form_rank, expression_rank} diff --git a/src/FunctionSpaces/AdaptiveRefinement/L-Chain.jl b/src/FunctionSpaces/AdaptiveRefinement/L-Chain.jl index bef83ddb5..ed545ae77 100644 --- a/src/FunctionSpaces/AdaptiveRefinement/L-Chain.jl +++ b/src/FunctionSpaces/AdaptiveRefinement/L-Chain.jl @@ -1,7 +1,7 @@ """ - update_domains_with_lchains!( - space::HierarchicalFiniteElementSpace{2}, marked_els::Vector{Vector{Int}} - ) + update_domains_with_lchains!( + space::HierarchicalFiniteElementSpace{2}, marked_els::Vector{Vector{Int}} + ) Returns a hierarchically nested set of `domains`, associated with an exact de Rham complex. The `domains` are computed from the original domains of the given `space`, and refinement @@ -11,12 +11,14 @@ Note that the bang (!) is used to denote an in-place change, despite returning ` which is not an argument. This is because `domains` is a field of the `space` structure. # Arguments -- `space::HierarchicalFiniteElementSpace{2}`: The space to be refined. -- `marked_els::Vector{Vector{Int}}`: The elements used for refinement. + + - `space::HierarchicalFiniteElementSpace{2}`: The space to be refined. + - `marked_els::Vector{Vector{Int}}`: The elements used for refinement. # Returns -- `domains::Hierarchy.ActiveInfo`: The hierarchically nested domains after refinement. This - is also a field of `space`, which is modified in-place. + + - `domains::Hierarchy.ActiveInfo`: The hierarchically nested domains after refinement. This + is also a field of `space`, which is modified in-place. """ function update_domains_with_lchains!( space::HierarchicalFiniteElementSpace{2}, marked_els::Vector{Vector{Int}} @@ -24,7 +26,7 @@ function update_domains_with_lchains!( previous_parents = Int[] L = get_num_levels(space) domains = get_nested_domains(space) - two_scale_operators = copy(get_two_scale_operators(space)) + two_scale_operators = copy(get_two_scale_operators(space)) spaces = get_spaces(space) for level in L:-1:1 level_marked_els = marked_els[level] @@ -91,13 +93,14 @@ function update_domains_with_lchains!( end """ - get_Blk(space::HierarchicalFiniteElementSpace, l::Int, k::Int) + get_Blk(space::HierarchicalFiniteElementSpace, l::Int, k::Int) Returns the basis indices the original space at level `l` whose support is contained in the domain `Ωₖ`. # Returns -- `Vector{Int}`: The basis contained in the domain `Ωₖ`. + + - `Vector{Int}`: The basis contained in the domain `Ωₖ`. """ function get_Blk(space::HierarchicalFiniteElementSpace, l::Int, k::Int) if k < l @@ -129,18 +132,19 @@ end get_Blk(space::HierarchicalFiniteElementSpace, l::Int) = get_Blk(space, l, l + 1) """ - initiate_pairs( - space::HierarchicalFiniteElementSpace{2}, - level::Int, - Blk::Vector{Int}, - marked_els::Vector{Int}, - ) + initiate_pairs( + space::HierarchicalFiniteElementSpace{2}, + level::Int, + Blk::Vector{Int}, + marked_els::Vector{Int}, + ) Generates all the possibly problematic pairs at `level` that need to be checked for problems. # Returns -- `Vector{Tuple{Int, Int}}`: The pairs that need to be checked for problems. + + - `Vector{Tuple{Int, Int}}`: The pairs that need to be checked for problems. See also [`update_domains_with_lchains!`](@ref), [`get_Blk`](@ref) and [`get_local_pairs`](@ref). @@ -166,14 +170,15 @@ function initiate_pairs( end """ - is_resolved( - space::HierarchicalFiniteElementSpace{2}, level::Int, Blk::Vector{Int}, βᵢ::Int - ) + is_resolved( + space::HierarchicalFiniteElementSpace{2}, level::Int, Blk::Vector{Int}, βᵢ::Int + ) Checks whether the basis function `βᵢ` at `level` is resolved or not. # Returns -- `Bool`: Whether `βᵢ` is resolved. + + - `Bool`: Whether `βᵢ` is resolved. """ function is_resolved( space::HierarchicalFiniteElementSpace{2}, level::Int, Blk::Vector{Int}, βᵢ::Int @@ -212,21 +217,23 @@ function is_resolved( end """ - get_local_pairs( - space::HierarchicalFiniteElementSpace{2}, - level::Int, - Blk::Vector{Int}, - unchecked::Vector{Int}, - ) + get_local_pairs( + space::HierarchicalFiniteElementSpace{2}, + level::Int, + Blk::Vector{Int}, + unchecked::Vector{Int}, + ) Returns a list of pairs of basis functions that need to be checked for problems from a set of `unchecked` basis functions. # Arguments -- `unchecked::Vector{Int}`: A list of unresolved basis functions. + + - `unchecked::Vector{Int}`: A list of unresolved basis functions. # Returns -- `Vector{Tuple{Int, Int}}`: The list of possibly problematic pairs. + + - `Vector{Tuple{Int, Int}}`: The list of possibly problematic pairs. See also [`initiate_pairs`](@ref) and [`is_resolved`](@ref). """ @@ -254,15 +261,16 @@ function get_local_pairs( end """ - get_interaction_box( - space::HierarchicalFiniteElementSpace{2}, level::Int, Blk::Vector{Int}, βᵢ::Int - ) + get_interaction_box( + space::HierarchicalFiniteElementSpace{2}, level::Int, Blk::Vector{Int}, βᵢ::Int + ) Returns a list of basis functions that are at most `p[k]+1` away from `βᵢ` in index space for each manifold dimension `k`, where `p[k]` is the polynomial degree. # Returns -- `Vector{Int}`: The list of basis functions interacting with `βᵢ`. + + - `Vector{Int}`: The list of basis functions interacting with `βᵢ`. """ function get_interaction_box( space::HierarchicalFiniteElementSpace{2}, level::Int, Blk::Vector{Int}, βᵢ::Int @@ -294,17 +302,18 @@ function get_interaction_box( end """ - is_problematic( - space::HierarchicalFiniteElementSpace{2}, - level::Int, - Blk::Vector{Int}, - (βᵢ, βⱼ)::Tuple{Int, Int}, - ) + is_problematic( + space::HierarchicalFiniteElementSpace{2}, + level::Int, + Blk::Vector{Int}, + (βᵢ, βⱼ)::Tuple{Int, Int}, + ) Checks whether a `(βᵢ, βⱼ)` is a problematic pair. # Returns -- `Bool`: Whether the pair is problematic. + + - `Bool`: Whether the pair is problematic. """ function is_problematic( space::HierarchicalFiniteElementSpace{2}, @@ -317,14 +326,15 @@ function is_problematic( end """ - has_minimal_intersection( - space::HierarchicalFiniteElementSpace{2}, level::Int, (βᵢ, βⱼ)::Tuple{Int, Int} - ) + has_minimal_intersection( + space::HierarchicalFiniteElementSpace{2}, level::Int, (βᵢ, βⱼ)::Tuple{Int, Int} + ) Checks whether a `(βᵢ, βⱼ)` share a minimal-(l+1) intersection. # Returns -- `Bool`: Whether the pair shares a minimal intersection. + + - `Bool`: Whether the pair shares a minimal intersection. """ function has_minimal_intersection( space::HierarchicalFiniteElementSpace{2}, level::Int, (βᵢ, βⱼ)::Tuple{Int, Int} @@ -351,17 +361,18 @@ function has_minimal_intersection( end """ - get_contained_knot_vector( - boundary_breakpoints::NTuple{2, Int}, - ts::AbstractTwoScaleOperator, - fine_space::BSplineSpace, - ) + get_contained_knot_vector( + boundary_breakpoints::NTuple{2, Int}, + ts::AbstractTwoScaleOperator, + fine_space::BSplineSpace, + ) Returns a `KnotVector` corresponding to the largest subset of the knot-vector defining `fine_space` that is contained between `boundary_breakpoints`. # Returns -- `KnotVector`: The largest knot-vector contained between `boundary_breakpoints`. + + - `KnotVector`: The largest knot-vector contained between `boundary_breakpoints`. """ function get_contained_knot_vector( boundary_breakpoints::NTuple{2, Int}, @@ -395,17 +406,18 @@ function get_contained_knot_vector( end """ - has_shortest_chain( - space::HierarchicalFiniteElementSpace{2}, - level::Int, - Blk::Vector{Int}, - (βᵢ, βⱼ)::Tuple{Int, Int}, - ) + has_shortest_chain( + space::HierarchicalFiniteElementSpace{2}, + level::Int, + Blk::Vector{Int}, + (βᵢ, βⱼ)::Tuple{Int, Int}, + ) Checks whether a `(βᵢ, βⱼ)` have a shortest chain between them. # Returns -- `Bool`: Whether `(βᵢ, βⱼ)` have a shortest chain between them. + + - `Bool`: Whether `(βᵢ, βⱼ)` have a shortest chain between them. """ function has_shortest_chain( space::HierarchicalFiniteElementSpace{2}, @@ -449,18 +461,19 @@ function has_shortest_chain( end """ - get_lchain_corner( - space::HierarchicalFiniteElementSpace{2}, - level::Int, - Blk::Vector{Int}, - (βᵢ, βⱼ)::Tuple{Int, Int}, - ) + get_lchain_corner( + space::HierarchicalFiniteElementSpace{2}, + level::Int, + Blk::Vector{Int}, + (βᵢ, βⱼ)::Tuple{Int, Int}, + ) Returns a corner basis function of an L-chain between `βᵢ` and `βⱼ`, with preference for resolved corners. # Returns -- `Int`: The id of the corner basis function. + + - `Int`: The id of the corner basis function. """ function get_lchain_corner( space::HierarchicalFiniteElementSpace{2}, @@ -481,12 +494,13 @@ function get_lchain_corner( end """ - get_parent_function(space::HierarchicalFiniteElementSpace, level::Int, βᵢ::Int) + get_parent_function(space::HierarchicalFiniteElementSpace, level::Int, βᵢ::Int) Returns the first basis function of `βᵢ`. # Returns -- `Int`: The id of the parent basis function. + + - `Int`: The id of the parent basis function. """ function get_parent_function(space::HierarchicalFiniteElementSpace, level::Int, βᵢ::Int) operator = get_twoscale_operator(space, level - 1) diff --git a/src/FunctionSpaces/AdaptiveRefinement/Mark.jl b/src/FunctionSpaces/AdaptiveRefinement/Mark.jl index f02d3b1f9..146fced3d 100644 --- a/src/FunctionSpaces/AdaptiveRefinement/Mark.jl +++ b/src/FunctionSpaces/AdaptiveRefinement/Mark.jl @@ -5,12 +5,14 @@ Computes the indices of elements with at least `dorfler_parameter*100`% of the h in `element_errors`. # Arguments -- `element_errors::Vector{Float64}`: element-wise errors. -- `dorfler_parameter::Float64`: dorfler parameter determing how many elements are selected. + + - `element_errors::Vector{Float64}`: element-wise errors. + - `dorfler_parameter::Float64`: dorfler parameter determing how many elements are selected. # Returns -- `::Vector{Int}`: indices of elements with at least `dorfler_parameter*100`% of the highest - error. + + - `::Vector{Int}`: indices of elements with at least `dorfler_parameter*100`% of the highest + error. """ function get_dorfler_marking(element_errors::Vector{Float64}, dorfler_parameter::Float64) if !(0.0 <= dorfler_parameter < 1.0) @@ -28,22 +30,24 @@ function get_dorfler_marking(element_errors::Vector{Float64}, dorfler_parameter: end """ - add_padding!( - marked_elements_per_level::Vector{Vector{Int}}, space::HierarchicalFiniteElementSpace - ) + add_padding!( + marked_elements_per_level::Vector{Vector{Int}}, space::HierarchicalFiniteElementSpace + ) For each `level` of the hierarchical `space`, adds a padding to `marked_elements_per_level[level]`. The padding consists of the support of all basis functions whose supports intersect the original `marked_elements_per_level`. # Arguments -- `marked_elements_per_level::Vector{Vector{Int}}`: The level-wise indexing of marked - elements. -- `space::HierarchicalFiniteElementSpace`: The hierarchical finite element space. + + - `marked_elements_per_level::Vector{Vector{Int}}`: The level-wise indexing of marked + elements. + - `space::HierarchicalFiniteElementSpace`: The hierarchical finite element space. # Returns -- `marked_elements_per_level::Vector{Vector{Int}}`: The padded level-wise indexing of marked - elements. + + - `marked_elements_per_level::Vector{Vector{Int}}`: The padded level-wise indexing of marked + elements. """ function add_padding!( marked_elements_per_level::Vector{Vector{Int}}, space::HierarchicalFiniteElementSpace @@ -70,9 +74,9 @@ function add_padding!( end """ - get_padding_per_level( - space::HierarchicalFiniteElementSpace, marked_elements::Vector{Int}; - ) + get_padding_per_level( + space::HierarchicalFiniteElementSpace, marked_elements::Vector{Int}; + ) Separates a list of `marked_elements` in hierarchical indexing into level-wise indexing, and adds a padding to each level. @@ -80,11 +84,13 @@ adds a padding to each level. See also [`convert_element_vector_to_elements_per_level`](@ref) and [`add_padding!`](@ref). # Arguments -- `space::HierarchicalFiniteElementSpace`: The hierarchical finite element space. -- `marked_elements::Vector{Int};`: The list of marked elements in hierarchical indexing. + + - `space::HierarchicalFiniteElementSpace`: The hierarchical finite element space. + - `marked_elements::Vector{Int};`: The list of marked elements in hierarchical indexing. # Returns -- `element_ids_per_level::Vector{Vector{Int}}`: Level-wise indexing of marked elements. + + - `element_ids_per_level::Vector{Vector{Int}}`: Level-wise indexing of marked elements. """ function get_padding_per_level( space::HierarchicalFiniteElementSpace, marked_elements::Vector{Int}; diff --git a/src/FunctionSpaces/CanonicalSpaces/BernsteinPolynomials.jl b/src/FunctionSpaces/CanonicalSpaces/BernsteinPolynomials.jl index 6097375a0..6c0cd8d99 100644 --- a/src/FunctionSpaces/CanonicalSpaces/BernsteinPolynomials.jl +++ b/src/FunctionSpaces/CanonicalSpaces/BernsteinPolynomials.jl @@ -5,7 +5,8 @@ Concrete type for Bernstein polynomials; see [DeBoor1978](@cite). # Fields -- `p::Int`: Degree of the Bernstein polynomial. + + - `p::Int`: Degree of the Bernstein polynomial. """ struct Bernstein <: AbstractCanonicalSpace p::Int @@ -27,13 +28,15 @@ Compute derivatives up to order `nderivatives` for all Bernstein polynomials of at `ξ` for ``\\xi \\in [0.0, 1.0]``. # Arguments -- `polynomial::Bernstein`: Bernstein polynomial. -- `ξ::Vector{Float64}`: Vector of evaluation points ``\\in [0.0, 1.0]``. -- `nderivatives::Int=0`: Maximum order of derivatives to be computed (`nderivatives` + + - `polynomial::Bernstein`: Bernstein polynomial. + - `ξ::Vector{Float64}`: Vector of evaluation points ``\\in [0.0, 1.0]``. + - `nderivatives::Int=0`: Maximum order of derivatives to be computed (`nderivatives` ``\\leq p``). Defaults to `0`, i.e., only the values of the polynomials are computed. # Returns -- `::Vector{Vector{Matrix{Float64}}}`: Nested vector containing the values. + + - `::Vector{Vector{Matrix{Float64}}}`: Nested vector containing the values. """ Memoization.@memoize function evaluate( polynomials::Bernstein, xi::Points.AbstractPoints{1}, nderivatives::Int=0 @@ -113,7 +116,8 @@ a polynomial in terms of the monomial basis into coefficients of in terms of the Bernstein basis. # Arguments -- `polynomial::Bernstein`: Bernstein polynomial + + - `polynomial::Bernstein`: Bernstein polynomial """ function extract_monomial_to_bernstein(polynomial::Bernstein) # degree diff --git a/src/FunctionSpaces/CanonicalSpaces/CanonicalSpaces.jl b/src/FunctionSpaces/CanonicalSpaces/CanonicalSpaces.jl index 94d5709d6..c38d0bce3 100644 --- a/src/FunctionSpaces/CanonicalSpaces/CanonicalSpaces.jl +++ b/src/FunctionSpaces/CanonicalSpaces/CanonicalSpaces.jl @@ -9,10 +9,12 @@ include("ECTSpaces/ECTSpaces.jl") Returns the polynomial degree of the element-local basis. # Arguments -- `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. + + - `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. # Returns -- `::Int`: The polynomial degree of the element-local basis. + + - `::Int`: The polynomial degree of the element-local basis. """ function get_polynomial_degree(elem_loc_basis::AbstractCanonicalSpace) return elem_loc_basis.p @@ -26,10 +28,12 @@ element-local basis. This method should be overloaded for element-local bases th satisfy this property or those that need additional parameters; e.g., ECT spaces. # Arguments -- `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. + + - `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. # Returns -- `::AbstractCanonicalSpace`: The element-local basis of one degree lower than the given + + - `::AbstractCanonicalSpace`: The element-local basis of one degree lower than the given element-local basis. """ function get_derivative_space(elem_loc_basis::AbstractCanonicalSpace) @@ -44,10 +48,12 @@ for element-local bases that do not satisfy this property or those that need add parameters; e.g., ECT spaces. # Arguments -- `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. + + - `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. # Returns -- `::AbstractCanonicalSpace`: The input element-local basis. + + - `::AbstractCanonicalSpace`: The input element-local basis. """ function get_bisected_canonical_space(elem_loc_basis::AbstractCanonicalSpace) return elem_loc_basis @@ -61,11 +67,13 @@ for element-local bases that do not satisfy this property or those that need add parameters; e.g., ECT spaces. # Arguments -- `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. -- `num_sub_elements::Int`: The number of sub-elements to divide the canonical space into. + + - `elem_loc_basis::AbstractCanonicalSpace`: An element-local basis. + - `num_sub_elements::Int`: The number of sub-elements to divide the canonical space into. # Returns -- `::AbstractCanonicalSpace`: The input element-local basis. + + - `::AbstractCanonicalSpace`: The input element-local basis. """ function get_child_canonical_space( elem_loc_basis::AbstractCanonicalSpace, num_sub_elements::Int @@ -82,12 +90,14 @@ Evaluates all derivatives upto order `nderivatives` for all basis functions of `canonical_space` at a given point `xi`. # Arguments -- `canonical_space::AbstractCanonicalSpace`: A canonical space. -- `xi::Float64`: The point where all global basis functiuons are evaluated. -- `nderivatives::Int`: The order upto which derivatives need to be computed. + + - `canonical_space::AbstractCanonicalSpace`: A canonical space. + - `xi::Float64`: The point where all global basis functiuons are evaluated. + - `nderivatives::Int`: The order upto which derivatives need to be computed. # Returns -- `::SparseMatrixCSC{Float64}`: Global basis functions, size = n_dofs x nderivatives+1 + + - `::SparseMatrixCSC{Float64}`: Global basis functions, size = n_dofs x nderivatives+1 """ function _evaluate_all_at_point( canonical_space::AbstractCanonicalSpace, xi::Float64, nderivatives::Int @@ -120,11 +130,13 @@ It returns a global subdivision matrix that maps the global basis functions of t canonical space to the global basis functions of the subspaces. # Arguments -- `canonical_space::AbstractCanonicalSpace`: A canonical space. -- `num_sub_elements::Int`: The number of subspaces to divide the canonical space into. + + - `canonical_space::AbstractCanonicalSpace`: A canonical space. + - `num_sub_elements::Int`: The number of subspaces to divide the canonical space into. # Returns -- `::SparseMatrixCSC{Float64}`: A global subdivision matrix that maps the global basis + + - `::SparseMatrixCSC{Float64}`: A global subdivision matrix that maps the global basis functions of the canonical space to the global basis functions of the subspaces. """ function build_two_scale_matrix( @@ -162,7 +174,7 @@ function build_two_scale_matrix( subdivision_matrix = SparseArrays.sparse(Matrix(LinearAlgebra.I, p + 1, p + 1)) for i in 1:num_ref subdivision_matrix = - SparseArrays.blockdiag([bisection_matrix for i in 1:(2^(i - 1))]...) * subdivision_matrix + SparseArrays.blockdiag([bisection_matrix for i in 1:(2 ^ (i - 1))]...) * subdivision_matrix end return subdivision_matrix diff --git a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/ECTSpaces.jl b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/ECTSpaces.jl index aa50a74c2..40db0e6e0 100644 --- a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/ECTSpaces.jl +++ b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/ECTSpaces.jl @@ -6,17 +6,21 @@ Compute derivatives up to order `nderivatives` for all basis functions of degree for ``\\xi \\in [0.0, 1.0]``. # Arguments -- `ect_space::AbstractECTSpaces`: ECT section space. -- `ξ::Points.AbstractPoints{1}`: vector of evaluation points ``\\in [0.0, 1.0]``. -- `nderivatives::Int`: maximum order of derivatives to be computed (nderivatives + + - `ect_space::AbstractECTSpaces`: ECT section space. + - `ξ::Points.AbstractPoints{1}`: vector of evaluation points ``\\in [0.0, 1.0]``. + - `nderivatives::Int`: maximum order of derivatives to be computed (nderivatives ``\\leq p``). Defaults to `0`, i.e., only the values are computed. """ Memoization.@memoize function evaluate( ect_space::AbstractECTSpaces, ξ::Points.AbstractPoints{1}, nderivatives::Int=0 ) - if nderivatives >= ect_space.p - throw(ArgumentError("Cannot compute derivatives of order higher than $(ect_space.p-1).")) + throw( + ArgumentError( + "Cannot compute derivatives of order higher than $(ect_space.p-1)." + ), + ) end neval = Points.get_num_points(ξ) # allocate space for derivatives @@ -27,10 +31,10 @@ Memoization.@memoize function evaluate( ders[j + 1][1] = zeros(Float64, neval, ect_space.p + 1) end # loop over the evaluation points and evaluate all derivatives at each point - for i = 1:neval + for i in 1:neval tmp = _evaluate(ect_space, ξ[i][1], nderivatives) - for j = 0:nderivatives - ders[j+1][1][i,:] .= tmp[1,:,j+1] + for j in 0:nderivatives + ders[j + 1][1][i, :] .= tmp[1, :, j + 1] end end return ders diff --git a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedExponential.jl b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedExponential.jl index 74820e6ac..4c054f9d3 100644 --- a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedExponential.jl +++ b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedExponential.jl @@ -6,12 +6,13 @@ Concrete type for Generalized Exponential section space spanned by `<1, x, ..., x^(p-2), cosh(wx), sinh(wx)>`, on `[0,l]`; see [Schumaker2007](@cite). # Fields -- `p::Int`: Degree of the space. -- `w::Float64`: Weight parameter for the space. -- `l::Float64`: Length of the interval. GExp space is not scale-invariant. -- `t::Bool`: flag to indicate if critical length is exceeded. -- `m::Int`: number of terms from the infinite sum used to build the basis. -- `C::Matrix{Float64}`: representation matrix for the local basis. + + - `p::Int`: Degree of the space. + - `w::Float64`: Weight parameter for the space. + - `l::Float64`: Length of the interval. GExp space is not scale-invariant. + - `t::Bool`: flag to indicate if critical length is exceeded. + - `m::Int`: number of terms from the infinite sum used to build the basis. + - `C::Matrix{Float64}`: representation matrix for the local basis. """ struct GeneralizedExponential <: AbstractECTSpaces p::Int @@ -32,8 +33,8 @@ struct GeneralizedExponential <: AbstractECTSpaces if p < 1 throw(ArgumentError("Degree p must be a positive integer.")) end - new(p, w, l, t, m, gexp_representation(p, w, l, t, m), endpoint_tol) - end + return new(p, w, l, t, m, gexp_representation(p, w, l, t, m), endpoint_tol) + end end function _evaluate(ect_space::GeneralizedExponential, xi::Float64, nderivatives::Int) @@ -50,23 +51,24 @@ function _evaluate(ect_space::GeneralizedExponential, xi::Float64, nderivatives: # scale the point to lie in the interval [0, l] xi = ect_space.l * xi if ect_space.t - for k = 0:nderivatives + for k in 0:nderivatives wxl = ect_space.w * xi - E = [1.0; cumprod((1.0 ./ (1:ect_space.p-k)) * wxl)] - E[ect_space.p-k, :] .= exp(wxl); - E[ect_space.p+1-k, :] .= (-1)^k * exp(-wxl); + E = [1.0; cumprod((1.0 ./ (1:(ect_space.p - k))) * wxl)] + E[ect_space.p - k, :] .= exp(wxl); + E[ect_space.p + 1 - k, :] .= (-1)^k * exp(-wxl); # rescale the derivative to map back from [0, l] -> [0, 1] - M[1, :, k+1] = (ect_space.w^k) * (ect_space.C[:,k+1:end] * E) * (ect_space.l^k) + M[1, :, k + 1] = + (ect_space.w^k) * (ect_space.C[:, (k + 1):end] * E) * (ect_space.l^k) end else - for k = 0:nderivatives + for k in 0:nderivatives ww = [1; cumprod(repeat([ect_space.w * ect_space.w], ect_space.m))] - Ef = [1.0; cumprod((1.0 ./ (1:ect_space.p-k+2*ect_space.m)) * xi)] - E = Ef[1:ect_space.p+1-k] - E[ect_space.p-k, :] = Ef[ect_space.p-k:2:end, :]' * ww - E[ect_space.p-k+1, :] = Ef[ect_space.p-k+1:2:end, :]' * ww + Ef = [1.0; cumprod((1.0 ./ (1:(ect_space.p - k + 2 * ect_space.m))) * xi)] + E = Ef[1:(ect_space.p + 1 - k)] + E[ect_space.p - k, :] = Ef[(ect_space.p - k):2:end, :]' * ww + E[ect_space.p - k + 1, :] = Ef[(ect_space.p - k + 1):2:end, :]' * ww # rescale the derivative to map back from [0, l] -> [0, 1] - M[1, :, k+1] = ect_space.C[:,k+1:end] * E * (ect_space.l^k) + M[1, :, k + 1] = ect_space.C[:, (k + 1):end] * E * (ect_space.l^k) end end @@ -85,8 +87,9 @@ end Compute all basis function values at `ξ` in ``[0.0, 1.0]``. # Arguments -- `ect_space::GeneralizedExponential`: Generalized Exponential section space. -- `xi::Vector{Float64}`: vector of evaluation points ``in [0.0, 1.0]``. + + - `ect_space::GeneralizedExponential`: Generalized Exponential section space. + - `xi::Vector{Float64}`: vector of evaluation points ``in [0.0, 1.0]``. """ function evaluate(ect_space::GeneralizedExponential, xi::Vector{Float64}) return evaluate(ect_space, xi, 0) @@ -103,14 +106,16 @@ Build representation matrix for Generalized Exponential section space of degree `w`, and length `l`. # Arguments -- `p::Int`: Degree of the space. -- `w::Float64`: Weight parameter for the space. -- `l::Float64`: Length of the interval. GExp space is not scale-invariant. -- `t::Bool`: flag to indicate if critical length is exceeded. -- `m::Int`: number of terms from the infinite sum used to build the basis. + + - `p::Int`: Degree of the space. + - `w::Float64`: Weight parameter for the space. + - `l::Float64`: Length of the interval. GExp space is not scale-invariant. + - `t::Bool`: flag to indicate if critical length is exceeded. + - `m::Int`: number of terms from the infinite sum used to build the basis. # Returns: -- `C::Matrix{Float64}`: representation matrix for the local basis. + + - `C::Matrix{Float64}`: representation matrix for the local basis. """ function gexp_representation(p::Int, w::Float64, l::Float64, t::Bool, m::Int) I = Matrix(1.0LinearAlgebra.I, p + 1, p + 1) @@ -156,14 +161,18 @@ Get the space of one degree lower than the input space. Assumes that the degree is at least 2. # Arguments -- `ect_space::GeneralizedExponential`: A generalized exponential space. + + - `ect_space::GeneralizedExponential`: A generalized exponential space. # Returns -- `::GeneralizedExponential`: A generalized exponential space of one degree lower than the input space. + + - `::GeneralizedExponential`: A generalized exponential space of one degree lower than the input space. """ function get_derivative_space(ect_space::GeneralizedExponential) if ect_space.p < 2 - throw(ArgumentError("Degree of the space must be at least 2 to get derivative space.")) + throw( + ArgumentError("Degree of the space must be at least 2 to get derivative space.") + ) end return GeneralizedExponential( ect_space.p - 1, ect_space.w, ect_space.l, ect_space.t, ect_space.m @@ -176,10 +185,12 @@ end Bisect the canonical space by dividing the length in half. # Arguments -- `ect_space::GeneralizedExponential`: A generalized exponential space. + + - `ect_space::GeneralizedExponential`: A generalized exponential space. # Returns -- `::GeneralizedExponential`: A generalized exponential space with the length divided by 2. + + - `::GeneralizedExponential`: A generalized exponential space with the length divided by 2. """ function get_bisected_canonical_space(ect_space::GeneralizedExponential) return GeneralizedExponential(ect_space.p, ect_space.w, ect_space.l / 2, ect_space.m) @@ -192,11 +203,13 @@ For number of sub-elements which is powers of 2, bisect the canonical space by d length in half for each power. # Arguments -- `ect_space::GeneralizedExponential`: A generalized exponential space. -- `num_sub_elements::Int`: Number of sub-elements to be created. + + - `ect_space::GeneralizedExponential`: A generalized exponential space. + - `num_sub_elements::Int`: Number of sub-elements to be created. # Returns -- `::GeneralizedExponential`: A generalized exponential space with the subdivided length. + + - `::GeneralizedExponential`: A generalized exponential space with the subdivided length. """ function get_child_canonical_space(ect_space::GeneralizedExponential, num_sub_elements::Int) num_ref = log2(num_sub_elements) diff --git a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedTrigonometric.jl b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedTrigonometric.jl index 85c4189f6..8216f39bb 100644 --- a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedTrigonometric.jl +++ b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/GeneralizedTrigonometric.jl @@ -5,12 +5,13 @@ Concrete type for Generalized Trignometric section space spanned by ``<1, x, ..., x^(p-2), cos(wx), sin(wx)>`` on ``[0,l]``; see [Schumaker2007](@cite). # Fields -- `p::Int`: Degree of the space. -- `w::Float64`: Weight parameter for the space. -- `l::Float64`: Length of the interval. GTrig space is not scale-invariant. -- `t::Bool`: flag to indicate if critical length is exceeded. -- `m::Int`: number of terms from the infinite sum used to build the basis. -- `C::Matrix{Float64}`: representation matrix for the local basis. + + - `p::Int`: Degree of the space. + - `w::Float64`: Weight parameter for the space. + - `l::Float64`: Length of the interval. GTrig space is not scale-invariant. + - `t::Bool`: flag to indicate if critical length is exceeded. + - `m::Int`: number of terms from the infinite sum used to build the basis. + - `C::Matrix{Float64}`: representation matrix for the local basis. """ struct GeneralizedTrigonometric <: AbstractECTSpaces p::Int @@ -31,7 +32,7 @@ struct GeneralizedTrigonometric <: AbstractECTSpaces if p < 1 throw(ArgumentError("Degree p must be a positive integer.")) end - new(p, w, l, t, m, gtrig_representation(p, w, l, t, m), endpoint_tol) + return new(p, w, l, t, m, gtrig_representation(p, w, l, t, m), endpoint_tol) end end @@ -49,34 +50,35 @@ function _evaluate(ect_space::GeneralizedTrigonometric, xi::Float64, nderivative # scale the point to lie in the interval [0, l] xi = ect_space.l * xi if ect_space.t - for k = 0:nderivatives + for k in 0:nderivatives wxl = ect_space.w * xi - E = [1.0; cumprod((1.0 ./ (1:ect_space.p-k)) * wxl)] + E = [1.0; cumprod((1.0 ./ (1:(ect_space.p - k))) * wxl)] if mod(k, 4) == 0 - E[ect_space.p-k] = cos(wxl); - E[ect_space.p+1-k] = sin(wxl); + E[ect_space.p - k] = cos(wxl); + E[ect_space.p + 1 - k] = sin(wxl); elseif mod(k, 4) == 1 - E[ect_space.p-k] = -sin(wxl); - E[ect_space.p+1-k] = cos(wxl); + E[ect_space.p - k] = -sin(wxl); + E[ect_space.p + 1 - k] = cos(wxl); elseif mod(k, 4) == 2 - E[ect_space.p-k] = -cos(wxl); - E[ect_space.p+1-k] = -sin(wxl); + E[ect_space.p - k] = -cos(wxl); + E[ect_space.p + 1 - k] = -sin(wxl); elseif mod(k, 4) == 3 - E[ect_space.p-k] = sin(wxl); - E[ect_space.p+1-k] = -cos(wxl); + E[ect_space.p - k] = sin(wxl); + E[ect_space.p + 1 - k] = -cos(wxl); end # rescale the derivative to map back from [0, l] -> [0, 1] - M[1, :, k+1] = (ect_space.w^k) * (ect_space.C[:,k+1:end] * E) * (ect_space.l^k) + M[1, :, k + 1] = + (ect_space.w^k) * (ect_space.C[:, (k + 1):end] * E) * (ect_space.l^k) end else - for k = 0:nderivatives + for k in 0:nderivatives ww = [1; cumprod(repeat([-ect_space.w * ect_space.w], ect_space.m))] - Ef = [1.0; cumprod((1.0 ./ (1:ect_space.p-k+2*ect_space.m)) * xi)] - E = Ef[1:ect_space.p+1-k] - E[ect_space.p-k, :] = Ef[ect_space.p-k:2:end, :]' * ww - E[ect_space.p-k+1, :] = Ef[ect_space.p-k+1:2:end, :]' * ww + Ef = [1.0; cumprod((1.0 ./ (1:(ect_space.p - k + 2 * ect_space.m))) * xi)] + E = Ef[1:(ect_space.p + 1 - k)] + E[ect_space.p - k, :] = Ef[(ect_space.p - k):2:end, :]' * ww + E[ect_space.p - k + 1, :] = Ef[(ect_space.p - k + 1):2:end, :]' * ww # rescale the derivative to map back from [0, l] -> [0, 1] - M[1, :, k+1] = ect_space.C[:,k+1:end] * E * (ect_space.l^k) + M[1, :, k + 1] = ect_space.C[:, (k + 1):end] * E * (ect_space.l^k) end end @@ -95,8 +97,9 @@ end Compute all basis function values at `ξ` in ``[0.0, 1.0]``. # Arguments -- `ect_space::GeneralizedTrigonometric`: Generalized Trigonometric section space. -- `xi::Vector{Float64}`: vector of evaluation points in ``[0.0, 1.0]``. + + - `ect_space::GeneralizedTrigonometric`: Generalized Trigonometric section space. + - `xi::Vector{Float64}`: vector of evaluation points in ``[0.0, 1.0]``. See also [`evaluate(ect_space::GeneralizedTrigonometric, xi::Vector{Float64}, nderivatives::Int64)`](@ref). """ @@ -175,14 +178,18 @@ Get the space of one degree lower than the input space. Assumes that the degree is at least 2. # Arguments -- `ect_space::GeneralizedTrigonometric`: A generalized trigonometric space. + + - `ect_space::GeneralizedTrigonometric`: A generalized trigonometric space. # Returns -- `::GeneralizedTrigonometric`: A generalized trigonometric space of one degree lower than the input space. + + - `::GeneralizedTrigonometric`: A generalized trigonometric space of one degree lower than the input space. """ function get_derivative_space(ect_space::GeneralizedTrigonometric) if ect_space.p < 2 - throw(ArgumentError("Degree of the space must be at least 2 to get derivative space.")) + throw( + ArgumentError("Degree of the space must be at least 2 to get derivative space.") + ) end return GeneralizedTrigonometric( ect_space.p - 1, ect_space.w, ect_space.l, ect_space.t, ect_space.m @@ -195,10 +202,12 @@ end Bisect the canonical space by dividing the length in half. # Arguments -- `ect_space::GeneralizedTrigonometric`: A generalized trigonometric space. + + - `ect_space::GeneralizedTrigonometric`: A generalized trigonometric space. # Returns -- `::GeneralizedTrigonometric`: A generalized trigonometric space with the length divided by 2. + + - `::GeneralizedTrigonometric`: A generalized trigonometric space with the length divided by 2. """ function get_bisected_canonical_space(ect_space::GeneralizedTrigonometric) return GeneralizedTrigonometric(ect_space.p, ect_space.w, ect_space.l / 2, ect_space.m) @@ -211,11 +220,13 @@ For number of sub-elements which is powers of 2, bisect the canonical space by d length in half for each power. # Arguments -- `ect_space::GeneralizedTrigonometric`: A generalized trigonometric space. -- `num_sub_elements::Int`: Number of sub-elements to be created. + + - `ect_space::GeneralizedTrigonometric`: A generalized trigonometric space. + - `num_sub_elements::Int`: Number of sub-elements to be created. # Returns -- `::GeneralizedTrigonometric`: A generalized trigonometric space with the subdivided length. + + - `::GeneralizedTrigonometric`: A generalized trigonometric space with the subdivided length. """ function get_child_canonical_space( ect_space::GeneralizedTrigonometric, num_sub_elements::Int diff --git a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/Tchebycheff.jl b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/Tchebycheff.jl index af5c7601c..62dd8aeab 100644 --- a/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/Tchebycheff.jl +++ b/src/FunctionSpaces/CanonicalSpaces/ECTSpaces/Tchebycheff.jl @@ -3,22 +3,24 @@ A Tchebycheffian section space. The parameters are the roots of a differential operator with constant coefficients; see [Schumaker2007](@cite). -* A complex root `α + iβ`, `β ≠ 0`, of multiplicity `m` contributes `2m` basis functions of - the form (upto scaling): + + - A complex root `α + iβ`, `β ≠ 0`, of multiplicity `m` contributes `2m` basis functions of + the form (upto scaling): `x^i e^(αx) cos(βx)`, `x^i e^(αx) sin(βx)`, `i = 0, ..., m-1` -* A real root `α` of multiplicity `m` contributes `m` basis functions of the - form (upto scaling): + - A real root `α` of multiplicity `m` contributes `m` basis functions of the + form (upto scaling): `x^i e^(αx)`, `i = 0, ..., m-1` # Fields -- `p::Int`: Degree of the space. -- `roots::Matrix{Float64}`: (real, imag) pairs of roots. -- `root_mult::Vector{Int}`: Multiplicities of roots. -- `root_type::Vector{Int}`: Types of roots: 0=zero, 1=real, 2=imaginary, 3=complex. -- `l::Float64`: Length of the interval. Tchebycheff space is not scale-invariant. -- `mu::Vector{Int}`: Cumulative dimension for the roots. -- `C::Matrix{Float64}`: Representation matrix for the local basis. -- `endpoint_tol::Float64`: Tolerance to determine if a point is at an endpoint. + + - `p::Int`: Degree of the space. + - `roots::Matrix{Float64}`: (real, imag) pairs of roots. + - `root_mult::Vector{Int}`: Multiplicities of roots. + - `root_type::Vector{Int}`: Types of roots: 0=zero, 1=real, 2=imaginary, 3=complex. + - `l::Float64`: Length of the interval. Tchebycheff space is not scale-invariant. + - `mu::Vector{Int}`: Cumulative dimension for the roots. + - `C::Matrix{Float64}`: Representation matrix for the local basis. + - `endpoint_tol::Float64`: Tolerance to determine if a point is at an endpoint. """ struct Tchebycheff <: AbstractECTSpaces p::Int @@ -32,46 +34,56 @@ struct Tchebycheff <: AbstractECTSpaces function Tchebycheff( p::Int, - roots::Matrix{Float64} = [0.0 0.0], - l::Float64 = 1.0, - root_mult::Vector{Int} = ones(Int, size(roots, 1)), - endpoint_tol::Float64 = 1e-12, + roots::Matrix{Float64}=[0.0 0.0], + l::Float64=1.0, + root_mult::Vector{Int}=ones(Int, size(roots, 1)), + endpoint_tol::Float64=1e-12, ) if any(root_mult .< 1) throw(ArgumentError("All multiplicities must be positive integers.")) end if size(roots, 1) != length(root_mult) - throw(ArgumentError("The number of roots must match the number of multiplicities.")) + throw( + ArgumentError( + "The number of roots must match the number of multiplicities." + ), + ) end # make all imaginary parts non-negative roots[:, 2] .= abs.(roots[:, 2]) # total multiplicity: each real root counts once, each complex root counts twice - m = sum(((roots[:,2] .!= 0) .+ 1) .* root_mult) + m = sum(((roots[:, 2] .!= 0) .+ 1) .* root_mult) if p+1 != m if p+1 > m roots = vcat(roots, [0.0 0.0]) push!(root_mult, p+1 - m) else - throw(ArgumentError("The local dimension, $(p+1), must be greater than or equal to the number of roots (counting multiplicities), $(m).")) + throw( + ArgumentError( + "The local dimension, $(p+1), must be greater than or equal to the number of roots (counting multiplicities), $(m).", + ), + ) end end # find unique roots and their indices - roots_i = unique(i -> roots[i, :], 1:size(roots,1)) + roots_i = unique(i -> roots[i, :], 1:size(roots, 1)) roots_unq = roots[roots_i, :] # find multiplicities of unique roots root_mult_unq = similar(root_mult[roots_i]) count = 0 for ru in eachrow(roots_unq) - root_mult_unq[count+1] = sum(root_mult[findall(row -> all(row .== ru), eachrow(roots))]) + root_mult_unq[count + 1] = sum( + root_mult[findall(row -> all(row .== ru), eachrow(roots))] + ) count += 1 end # store types of roots root_type = zeros(Int, length(root_mult_unq)) - root_type[findall(roots_unq[:,1] .!= 0.0)] .= 1 - root_type[findall(roots_unq[:,2] .!= 0.0)] .= 2 - root_type[findall(all(roots_unq .!= 0, dims=2))] .= 3 + root_type[findall(roots_unq[:, 1] .!= 0.0)] .= 1 + root_type[findall(roots_unq[:, 2] .!= 0.0)] .= 2 + root_type[findall(all(roots_unq .!= 0; dims=2))] .= 3 # number of basis functions for each unique root mu = [0, cumsum(((roots_unq[:, 2] .!= 0) .+ 1) .* root_mult_unq)...] @@ -84,7 +96,7 @@ struct Tchebycheff <: AbstractECTSpaces l, mu, tcheb_representation(p, roots_unq, root_mult_unq, root_type, l, mu), - endpoint_tol + endpoint_tol, ) end end @@ -102,7 +114,7 @@ function _evaluate(ect_space::Tchebycheff, xi::Float64, nderivatives::Int) # scale the point to lie in the interval [0, l] xi = ect_space.l * xi - for r = 0:nderivatives + for r in 0:nderivatives zero_v = zeros(1) E = zeros(ect_space.p+1) m = maximum(ect_space.root_mult) - 1 @@ -110,12 +122,12 @@ function _evaluate(ect_space::Tchebycheff, xi::Float64, nderivatives::Int) for i in eachindex(ect_space.root_mult) if ect_space.root_type[i] == 0 Ei = zeros(ect_space.root_mult[i]) - Ei[r+1:end] .= X[1:ect_space.root_mult[i]-r] + Ei[(r + 1):end] .= X[1:(ect_space.root_mult[i] - r)] elseif ect_space.root_type[i] == 1 ewxl = exp(ect_space.roots[i, 1] * xi) Ei = X[1:ect_space.root_mult[i]] .* ewxl for _ in 1:r - Ei .= vcat(zero_v, Ei[1:end-1]) .+ ect_space.roots[i, 1] * Ei + Ei .= vcat(zero_v, Ei[1:(end - 1)]) .+ ect_space.roots[i, 1] * Ei end elseif ect_space.root_type[i] == 2 cwxl = cos(ect_space.roots[i, 2] * xi) @@ -124,8 +136,8 @@ function _evaluate(ect_space::Tchebycheff, xi::Float64, nderivatives::Int) Esi = X[1:ect_space.root_mult[i]] .* swxl for _ in 1:r Ezi = copy(Eci) - Eci .= vcat(zero_v, Eci[1:end-1]) .- ect_space.roots[i, 2] * Esi - Esi .= vcat(zero_v, Esi[1:end-1]) .+ ect_space.roots[i, 2] * Ezi + Eci .= vcat(zero_v, Eci[1:(end - 1)]) .- ect_space.roots[i, 2] * Esi + Esi .= vcat(zero_v, Esi[1:(end - 1)]) .+ ect_space.roots[i, 2] * Ezi end Ei = vcat(Eci, Esi) elseif ect_space.root_type[i] == 3 @@ -136,15 +148,19 @@ function _evaluate(ect_space::Tchebycheff, xi::Float64, nderivatives::Int) Esi = X[1:ect_space.root_mult[i]] .* eswxl for _ in 1:r Ezi = copy(Eci) - Eci .= vcat(zero_v, Eci[1:end-1]) .+ ect_space.roots[i, 1] * Eci .- ect_space.roots[i, 2] * Esi - Esi .= vcat(zero_v, Esi[1:end-1]) .+ ect_space.roots[i, 1] * Esi .+ ect_space.roots[i, 2] * Ezi + Eci .= + vcat(zero_v, Eci[1:(end - 1)]) .+ ect_space.roots[i, 1] * Eci .- + ect_space.roots[i, 2] * Esi + Esi .= + vcat(zero_v, Esi[1:(end - 1)]) .+ ect_space.roots[i, 1] * Esi .+ + ect_space.roots[i, 2] * Ezi end Ei = vcat(Eci, Esi) end - E[ect_space.mu[i]+1:ect_space.mu[i+1]] .= Ei + E[(ect_space.mu[i] + 1):ect_space.mu[i + 1]] .= Ei end # rescale the derivative to map back from [0, l] -> [0, 1] - M[1, :, r+1] = ect_space.C * E * (ect_space.l^r) + M[1, :, r + 1] = ect_space.C * E * (ect_space.l^r) end if left @@ -162,8 +178,9 @@ end Compute all basis function values at `ξ` in ``[0.0, 1.0]``. # Arguments -- `ect_space::Tchebycheff`: Tchebycheff section space. -- `xi::Vector{Float64}`: vector of evaluation points ``in [0.0, 1.0]``. + + - `ect_space::Tchebycheff`: Tchebycheff section space. + - `xi::Vector{Float64}`: vector of evaluation points ``in [0.0, 1.0]``. """ function evaluate(ect_space::Tchebycheff, xi::Vector{Float64}) return evaluate(ect_space, xi, 0) @@ -181,15 +198,17 @@ Build representation matrix for Tchebycheff section space of degree `p`, roots ` `root_mult`, root types `root_type`, length `l`, and cumulative dimensions `mu`. # Arguments -- `p::Int`: Degree of the space. -- `roots::Matrix{Float64}`: (real, imag) pairs of roots. -- `root_mult::Vector{Int}`: Multiplicities of roots. -- `root_type::Vector{Int}`: Types of roots: 0=zero, 1=real, 2=imaginary, 3=complex. -- `l::Float64`: Length of the interval. -- `mu::Vector{Int}`: Cumulative dimension for the roots. + + - `p::Int`: Degree of the space. + - `roots::Matrix{Float64}`: (real, imag) pairs of roots. + - `root_mult::Vector{Int}`: Multiplicities of roots. + - `root_type::Vector{Int}`: Types of roots: 0=zero, 1=real, 2=imaginary, 3=complex. + - `l::Float64`: Length of the interval. + - `mu::Vector{Int}`: Cumulative dimension for the roots. # Returns -- `C::Matrix{Float64}`: Representation matrix for the local basis. + + - `C::Matrix{Float64}`: Representation matrix for the local basis. """ function tcheb_representation( p::Int, @@ -206,7 +225,7 @@ function tcheb_representation( I = 1.0LinearAlgebra.I for i in eachindex(root_mult) r_ind = root_type[i] - idx = (mu[i]+1):mu[i+1] + idx = (mu[i] + 1):mu[i + 1] if r_ind == 0 M0i = Matrix(I, root_mult[i], p+1) M1i = Matrix(ToeplitzMatrices.Toeplitz(X[1:root_mult[i]], [1.0; zeros(p)])) @@ -217,8 +236,9 @@ function tcheb_representation( M1i[:, 1] .= X[1:root_mult[i]] * ewl for k in 1:p ki = min(k, root_mult[i]) - 1 - M0i[1:ki+1, k+1] .= vcat(0.0, M0i[1:ki, k]) + roots[i, 1] * M0i[1:ki+1, k] - M1i[:, k+1] .= vcat(0.0, M1i[1:end-1, k]) + roots[i, 1] * M1i[:, k] + M0i[1:(ki + 1), k + 1] .= + vcat(0.0, M0i[1:ki, k]) + roots[i, 1] * M0i[1:(ki + 1), k] + M1i[:, k + 1] .= vcat(0.0, M1i[1:(end - 1), k]) + roots[i, 1] * M1i[:, k] end elseif r_ind == 2 cwl = cos(roots[i, 2] * l) @@ -232,10 +252,12 @@ function tcheb_representation( M1si[:, 1] .= X[1:n4] * swl for k in 1:p ki = min(k, n4) - 1 - M0ci[1:ki+1, k+1] .= vcat(0.0, M0ci[1:ki, k]) - roots[i, 2] * M0si[1:ki+1, k] - M0si[1:ki+1, k+1] .= vcat(0.0, M0si[1:ki, k]) + roots[i, 2] * M0ci[1:ki+1, k] - M1ci[:, k+1] .= vcat(0.0, M1ci[1:end-1, k]) - roots[i, 2] * M1si[:, k] - M1si[:, k+1] .= vcat(0.0, M1si[1:end-1, k]) + roots[i, 2] * M1ci[:, k] + M0ci[1:(ki + 1), k + 1] .= + vcat(0.0, M0ci[1:ki, k]) - roots[i, 2] * M0si[1:(ki + 1), k] + M0si[1:(ki + 1), k + 1] .= + vcat(0.0, M0si[1:ki, k]) + roots[i, 2] * M0ci[1:(ki + 1), k] + M1ci[:, k + 1] .= vcat(0.0, M1ci[1:(end - 1), k]) - roots[i, 2] * M1si[:, k] + M1si[:, k + 1] .= vcat(0.0, M1si[1:(end - 1), k]) + roots[i, 2] * M1ci[:, k] end M0i = vcat(M0ci, M0si) M1i = vcat(M1ci, M1si) @@ -251,14 +273,20 @@ function tcheb_representation( M1si[:, 1] .= X[1:n4] * eswl for k in 1:p ki = min(k, n4) - 1 - M0ci[1:ki+1, k+1] .= vcat(0.0, M0ci[1:ki, k]) + - roots[i, 1] * M0ci[1:ki+1, k] - roots[i, 2] * M0si[1:ki+1, k] - M0si[1:ki+1, k+1] .= vcat(0.0, M0si[1:ki, k]) + - roots[i, 1] * M0si[1:ki+1, k] + roots[i, 2] * M0ci[1:ki+1, k] - M1ci[:, k+1] .= vcat(0.0, M1ci[1:end-1, k]) + - roots[i, 1] * M1ci[:, k] - roots[i, 2] * M1si[:, k] - M1si[:, k+1] .= vcat(0.0, M1si[1:end-1, k]) + - roots[i, 1] * M1si[:, k] + roots[i, 2] * M1ci[:, k] + M0ci[1:(ki + 1), k + 1] .= + vcat(0.0, M0ci[1:ki, k]) + roots[i, 1] * M0ci[1:(ki + 1), k] - + roots[i, 2] * M0si[1:(ki + 1), k] + M0si[1:(ki + 1), k + 1] .= + vcat(0.0, M0si[1:ki, k]) + + roots[i, 1] * M0si[1:(ki + 1), k] + + roots[i, 2] * M0ci[1:(ki + 1), k] + M1ci[:, k + 1] .= + vcat(0.0, M1ci[1:(end - 1), k]) + roots[i, 1] * M1ci[:, k] - + roots[i, 2] * M1si[:, k] + M1si[:, k + 1] .= + vcat(0.0, M1si[1:(end - 1), k]) + + roots[i, 1] * M1si[:, k] + + roots[i, 2] * M1ci[:, k] end M0i = vcat(M0ci, M0si) M1i = vcat(M1ci, M1si) @@ -269,12 +297,12 @@ function tcheb_representation( cs = zeros(p+1) C = zeros(p+1, p+1) # Solve for last row - C[p+1, :] = (Matrix(I, 1, p+1)) / hcat(M1[:, 1], M0[:, 1:p]) + C[p + 1, :] = (Matrix(I, 1, p+1)) / hcat(M1[:, 1], M0[:, 1:p]) for i in 2:p - cs .+= C[p+1-i+2, :] + cs .+= C[p + 1 - i + 2, :] cc = zeros(1, p+1) cc[i] = reduce(+, -cs .* M1[:, i]) - C[p+1-i+1, :] = cc / hcat(M1[:, 1:i], M0[:, 1:p+1-i]) + C[p + 1 - i + 1, :] = cc / hcat(M1[:, 1:i], M0[:, 1:(p + 1 - i)]) end C[1, :] = (Matrix(I, 1, p+1)) / hcat(M0[:, 1], M1[:, 1:p]) @@ -288,13 +316,15 @@ Get the space of one degree lower than the input space. Requires that the input the zero root with multiplicity at least 1. # Arguments -- `ect_space::Tchebycheff`: A Tchebycheff space. + + - `ect_space::Tchebycheff`: A Tchebycheff space. # Returns -- `::Tchebycheff`: A Tchebycheff space of one degree lower than the input space. + + - `::Tchebycheff`: A Tchebycheff space of one degree lower than the input space. """ function get_derivative_space(ect_space::Tchebycheff) - zero_i = findall(all(ect_space.roots .== 0.0, dims=2)) + zero_i = findall(all(ect_space.roots .== 0.0; dims=2)) if length(zero_i) == 0 throw(ArgumentError("The zero root must exist for the input Tchebycheff space.")) end @@ -309,9 +339,7 @@ function get_derivative_space(ect_space::Tchebycheff) new_root_mult[zero_i[1]] -= 1 new_root_type = copy(ect_space.root_type) end - return Tchebycheff( - ect_space.p-1, new_roots, ect_space.l, new_root_mult - ) + return Tchebycheff(ect_space.p-1, new_roots, ect_space.l, new_root_mult) end """ @@ -320,10 +348,12 @@ end Bisect the canonical space by dividing the length in half. # Arguments -- `ect_space::Tchebycheff`: A Tchebycheff space. + + - `ect_space::Tchebycheff`: A Tchebycheff space. # Returns -- `::Tchebycheff`: A Tchebycheff space with the length divided by 2. + + - `::Tchebycheff`: A Tchebycheff space with the length divided by 2. """ function get_bisected_canonical_space(ect_space::Tchebycheff) return Tchebycheff(ect_space.p, ect_space.roots, ect_space.l/2, ect_space.root_mult) @@ -335,11 +365,13 @@ end Bisect the canonical space by dividing the length in half for each power. # Arguments -- `ect_space::Tchebycheff`: A Tchebycheff space. -- `num_sub_elements::Int`: Number of sub-elements to be created. + + - `ect_space::Tchebycheff`: A Tchebycheff space. + - `num_sub_elements::Int`: Number of sub-elements to be created. # Returns -- `::Tchebycheff`: A Tchebycheff space with the subdivided length. + + - `::Tchebycheff`: A Tchebycheff space with the subdivided length. """ function get_finer_canonical_space(ect_space::Tchebycheff, num_sub_elements::Int) num_ref = log2(num_sub_elements) diff --git a/src/FunctionSpaces/CanonicalSpaces/LagrangePolynomials.jl b/src/FunctionSpaces/CanonicalSpaces/LagrangePolynomials.jl index 5bfdafe6a..321a87af5 100644 --- a/src/FunctionSpaces/CanonicalSpaces/LagrangePolynomials.jl +++ b/src/FunctionSpaces/CanonicalSpaces/LagrangePolynomials.jl @@ -5,10 +5,11 @@ Lagrange interpolating polynomials. # Fields -- `p::Int`: Degree of the Lagrange polynomial. -- `nodes::NT`: Points at which the polynomial should be interpolating. The length of the + + - `p::Int`: Degree of the Lagrange polynomial. + - `nodes::NT`: Points at which the polynomial should be interpolating. The length of the `nodes` vector dictates the degree. -- `barycentric_weights::Vector{T}`: Barycentric weights. `T` is `eltype(nodes)`. + - `barycentric_weights::Vector{T}`: Barycentric weights. `T` is `eltype(nodes)`. """ struct Lagrange{NT, T} <: AbstractLagrangePolynomials p::Int @@ -146,23 +147,28 @@ Edge histapolant polynomials of degree `p`. The ``j``-th edge basis polynomial, ``e_{j}(\\xi)``, is given by, see [Gerritsma2011](@cite), + ```math e_{j}(\\xi) = -\\sum_{k=1}^{j} \\frac{\\mathrm{d} h_{k}(\\xi)}{\\mathrm{d}\\xi}, j = 1 , \\dots, p+1\\,. ``` + where ``h_{k}(\\xi)`` is the ``k``-th Lagrange polynomial of degree ``(p+1)`` over a given set of nodes. If ``\\xi_{i}`` are the given ``(p+1)`` nodes, then + ```math \\int_{\\xi_{i}}^{\\xi_{i+1}} e_{j}(\\xi)\\,\\mathrm{d}\\xi = \\delta_{i,j}, \\qquad i,j = 1, \\dots, p\\,, ``` + i.e., they satisfy an integral Kronecker-``\\delta`` property. See [Gerritsma2011](@cite) for more details. # Fields -- `p::Int`: Degree of the Edge polynomial. -- `nodes::NT`: Nodes between which the polynomial should be histapolating. The length of + + - `p::Int`: Degree of the Edge polynomial. + - `nodes::NT`: Nodes between which the polynomial should be histapolating. The length of the `nodes` vector dictates the degree. -- `lagrange_polynomial::Lagrange{NT, T}`: The underlying Lagrange polynomial. See + - `lagrange_polynomial::Lagrange{NT, T}`: The underlying Lagrange polynomial. See [Lagrange](@ref) for the details. """ struct Edge{NT, T} <: AbstractEdgePolynomials @@ -237,6 +243,7 @@ d_{k,j} = \\left\\{ \\end{aligned} \\right. ``` + with ```math @@ -249,21 +256,25 @@ the polynomials, ``B_{j}``, of order ``p`` ```math D_{k,j} = \\frac{\\mathrm{d}B_{j}(x_{k})}{\\mathrm{d}x} ``` + # Arguments -- `nodes::Vector{Float64}`: ``(p+1)`` nodes that define a set of Lagrange polynomials of - degree ``p``, ``B_{j}^{p}(\\xi)``, for which to compute the derivative matrix. Note that - the polynomials are such that ``B_{j}^{p}(\\xi_{i}) = \\delta_{j,i}`` with ``j,i = 1, \\dots, p+1``, - `\\xi_{i} \\in [0.0, 1.0]`. + + - `nodes::Vector{Float64}`: ``(p+1)`` nodes that define a set of Lagrange polynomials of + degree ``p``, ``B_{j}^{p}(\\xi)``, for which to compute the derivative matrix. Note that + the polynomials are such that ``B_{j}^{p}(\\xi_{i}) = \\delta_{j,i}`` with ``j,i = 1, \\dots, p+1``, + `\\xi_{i} \\in [0.0, 1.0]`. # Keyword arguments -- `algorithm::Int`: Flag to specify the algorithm to use + + - `algorithm::Int`: Flag to specify the algorithm to use 1: Stable algorithm using Eq. (7) in [1]. 2: Direct computation using Eq. (4) in [1]. # Returns -- `D::Array{Float64, 2}` :: The derivatives of the `(p+1)` polynomials evaluated at the `(p+1)` nodal points. - ``D_{k,j} = \\frac{\\mathrm{d}B_{j}(x_{k})}{\\mathrm{d}x}``. - (size: [p+1, p+1]) + + - `D::Array{Float64, 2}` :: The derivatives of the `(p+1)` polynomials evaluated at the `(p+1)` nodal points. # Revisions: 2009-11-25 (apalha) First implementation. + ``D_{k,j} = \\frac{\\mathrm{d}B_{j}(x_{k})}{\\mathrm{d}x}``. # 2014-12-03 (apalha) Removed pre-allocation of result. + (size: [p+1, p+1]) # Replaced repmats by bsxfun for smaller """ function _derivative_matrix(nodes::AbstractVector{Float64}; algorithm::Int=1) # Revisions: 2009-11-25 (apalha) First implementation. @@ -366,22 +377,22 @@ compute the derivative of order `(n+1)`. We follow the algorithm proposed in section 4 of [Costa2000](@cite). # Arguments -- `D_m::Array{float64, 2}`: Derivative matrix of order `m` for the ``(p+1)`` `polynomials`` of degree ``p``, - evaluated at the `(p+1)` nodal points, following the same format as the derivative matrix `D` below. - (size: [p+1, p+1]) -- `D::Array{Float64, 2}` :: The derivatives of the `(p+1)` polynomials evaluated at the `(p+1)` nodal points. - ``D_{k,j} = \\frac{\\mathrm{d}B_{j}(x_{k})}{\\mathrm{d}x}``. - (size: [p+1, p+1]) -- `nodes::Vector{Float64}`: ``(p+1)`` nodes that define a set of Lagrange polynomials of - degree ``p``, ``B_{j}^{p}(\\xi)``, for which to compute the derivative matrix. Note that - the polynomials are such that ``B_{j}^{p}(\\xi_{i}) = \\delta_{j,i}`` with ``j,i = 1, \\dots, p+1``, - `\\xi_{i} \\in [0.0, 1.0]`. + + - `D_m::Array{float64, 2}`: Derivative matrix of order `m` for the ``(p+1)`` ```polynomials`` of degree ``p``, evaluated at the ```(p+1)`nodal points, following the same format as the derivative matrix`D` below. + (size: [p+1, p+1]) + - `D::Array{Float64, 2}` :: The derivatives of the `(p+1)` polynomials evaluated at the `(p+1)` nodal points. + ``D_{k,j} = \\frac{\\mathrm{d}B_{j}(x_{k})}{\\mathrm{d}x}``. + (size: [p+1, p+1]) + - `nodes::Vector{Float64}`: ``(p+1)`` nodes that define a set of Lagrange polynomials of + degree ``p``, ``B_{j}^{p}(\\xi)``, for which to compute the derivative matrix. Note that + the polynomials are such that ``B_{j}^{p}(\\xi_{i}) = \\delta_{j,i}`` with ``j,i = 1, \\dots, p+1``, + `\\xi_{i} \\in [0.0, 1.0]`. # Returns -- `D_m::Array{Float64, 2}` :: The derivatives or degree `(m+1)` of the `(p+1)` polynomials evaluated at the `(p+1)` nodal points. - ``D^{(m)}_{k,j} = \\frac{\\mathrm{d}^{m}B_{j}(x_{k})}{\\mathrm{d}x^{m}}``. `D_m` given as input argument is updated with the new value. - (size: [p+1, p+1]) + - `D_m::Array{Float64, 2}` :: The derivatives or degree `(m+1)` of the `(p+1)` polynomials evaluated at the `(p+1)` nodal points. + ``D^{(m)}_{k,j} = \\frac{\\mathrm{d}^{m}B_{j}(x_{k})}{\\mathrm{d}x^{m}}``. `D_m` given as input argument is updated with the new value. + (size: [p+1, p+1]) """ function _derivative_matrix_next!( D_m::Array{Float64, 2}, m::Int, D::Array{Float64, 2}, nodes::AbstractVector{Float64} @@ -419,12 +430,14 @@ global subdivision matrix that maps the global basis functions of the ECT space global basis functions of the subspaces. # Arguments -- `ect_space::AbstractECTSpaces`: A ect space. -- `num_sub_elements::Int`: The number of subspaces to divide the EC T space into. + + - `ect_space::AbstractECTSpaces`: A ect space. + - `num_sub_elements::Int`: The number of subspaces to divide the EC T space into. # Returns -- `::SparseMatrixCSC{Float64}`: A global subdivision matrix that maps the global basis -functions of the ECT space to the global basis functions of the subspaces. + + - `::SparseMatrixCSC{Float64}`: A global subdivision matrix that maps the global basis + functions of the ECT space to the global basis functions of the subspaces. """ function build_two_scale_matrix( polynomials::AbstractLagrangePolynomials, num_sub_elements::Int diff --git a/src/FunctionSpaces/FiniteElementSpaces/ExtractionOperator.jl b/src/FunctionSpaces/FiniteElementSpaces/ExtractionOperator.jl index dbb514fcf..b737d5e01 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/ExtractionOperator.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/ExtractionOperator.jl @@ -5,9 +5,10 @@ Stores basis indices for an `ExtractionOperator`. # Fields -- `I::TI`: Global basis indices per element. Should be a vector-like type with integer + + - `I::TI`: Global basis indices per element. Should be a vector-like type with integer elements. -- `J::NTuple{num_components, TJ}`: 'Permutation' of the basis indices. This + - `J::NTuple{num_components, TJ}`: 'Permutation' of the basis indices. This tells the `evaluate` function to which basis on an element the evaluations correspond. """ struct Indices{num_components, TI, TJ} @@ -22,29 +23,29 @@ struct Indices{num_components, TI, TJ} throw(ArgumentError("The elements in Indices.J must be of type Int.")) end - new{num_components, TI, TJ}(I, J) + return new{num_components, TI, TJ}(I, J) end end -get_num_components(::Indices{num_components, TI, TJ}) where {num_components, TI, TJ} = num_components +get_num_components(::Indices{num_components, TI, TJ}) where {num_components, TI, TJ} = + num_components get_basis_indices(indices::Indices) = indices.I get_basis_permutation(indices::Indices, component_id::Int) = indices.J[component_id] - - """ ExtractionOperator{num_components, TE, TI, TJ} Stores extraction coefficients and basis indices for a function space. # Fields -- `extraction_coefficients::Vector{NTuple{num_components, TE}}`: A vector of extraction + + - `extraction_coefficients::Vector{NTuple{num_components, TE}}`: A vector of extraction coefficient matrices, where each matrix corresponds to an element. TE should be matrix- like. -- `basis_indices::Vector{TI}`: A vector of `Indices`. See [`Indices`](@ref) for more + - `basis_indices::Vector{TI}`: A vector of `Indices`. See [`Indices`](@ref) for more details. -- `num_elements::Int`: The number of elements. -- `num_basis::Int`: The (total) dimension of the function space. + - `num_elements::Int`: The number of elements. + - `num_basis::Int`: The (total) dimension of the function space. """ struct ExtractionOperator{num_components, TE, TI, TJ} extraction_coefficients::Vector{NTuple{num_components, TE}} @@ -58,31 +59,43 @@ struct ExtractionOperator{num_components, TE, TI, TJ} num_elements::Int, num_basis::Int, ) where {num_components, TE, TI, TJ} - if length(extraction_coefficients) != num_elements - throw(ArgumentError( - "Number of extraction coefficient matrices must match number of elements." - )) + throw( + ArgumentError( + "Number of extraction coefficient matrices must match number of elements.", + ), + ) end if length(basis_indices) != num_elements - throw(ArgumentError( - "Number of basis index vectors must match number of elements." - )) + throw( + ArgumentError( + "Number of basis index vectors must match number of elements." + ), + ) end - new{num_components, TE, TI, TJ}( + return new{num_components, TE, TI, TJ}( extraction_coefficients, basis_indices, num_elements, num_basis ) - end end -get_num_components(::ExtractionOperator{num_components, TE, TI, TJ}) where {num_components, TE, TI, TJ} = num_components -get_EIJ_types(::ExtractionOperator{num_components, TE, TI, TJ}) where {num_components, TE, TI, TJ} = (TE, TI, TJ) -get_extraction_type(::ExtractionOperator{num_components, TE, TI, TJ}) where {num_components, TE, TI, TJ} = TE -get_index_type(::ExtractionOperator{num_components, TE, TI, TJ}) where {num_components, TE, TI, TJ} = TI -get_permutation_type(::ExtractionOperator{num_components, TE, TI, TJ}) where {num_components, TE, TI, TJ} = TJ +get_num_components( + ::ExtractionOperator{num_components, TE, TI, TJ} +) where {num_components, TE, TI, TJ} = num_components +get_EIJ_types( + ::ExtractionOperator{num_components, TE, TI, TJ} +) where {num_components, TE, TI, TJ} = (TE, TI, TJ) +get_extraction_type( + ::ExtractionOperator{num_components, TE, TI, TJ} +) where {num_components, TE, TI, TJ} = TE +get_index_type( + ::ExtractionOperator{num_components, TE, TI, TJ} +) where {num_components, TE, TI, TJ} = TI +get_permutation_type( + ::ExtractionOperator{num_components, TE, TI, TJ} +) where {num_components, TE, TI, TJ} = TJ function _get_index_operator(extraction_op::ExtractionOperator, element_id::Int) return extraction_op.basis_indices[element_id] @@ -117,7 +130,7 @@ function get_extraction( ) return ( get_extraction_coefficients(extraction_op, element_id, component_id), - get_basis_permutation(extraction_op, element_id, component_id) + get_basis_permutation(extraction_op, element_id, component_id), ) end diff --git a/src/FunctionSpaces/FiniteElementSpaces/FiniteElementSpaces.jl b/src/FunctionSpaces/FiniteElementSpaces/FiniteElementSpaces.jl index c94bf7f5f..96e2cdcf4 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/FiniteElementSpaces.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/FiniteElementSpaces.jl @@ -8,10 +8,12 @@ general case, the component spaces are not necessarily single-patch spaces, and component spaces may contribute to multiple components. # Arguments -- `space::AbstractFESpace`: A (multi-)component finite element space. + + - `space::AbstractFESpace`: A (multi-)component finite element space. # Returns -- `component_spaces::Tuple{AbstractFESpace}`: Tuple of single-component spaces. + + - `component_spaces::Tuple{AbstractFESpace}`: Tuple of single-component spaces. """ function get_component_spaces(space::AbstractFESpace) return space.component_spaces @@ -30,17 +32,21 @@ end Returns the extraction operator of the given `space`. !!! warning "Not all AbstractFESpaces have an extraction operator." + Having an explicitly defined extraction operator is not required for building a finite element space. # Arguments -- `space::AbstractFESpace`: A finite element space. + + - `space::AbstractFESpace`: A finite element space. # Returns -- `::ExtractionOperator`: The extraction operator. + + - `::ExtractionOperator`: The extraction operator. # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no explicitly defined + + - Error "no field 'extraction_op'": This error is thrown if no explicitly defined extraction operator is present for this `space`. """ function get_extraction_operator(space::AbstractFESpace) @@ -54,18 +60,21 @@ Returns the extraction coefficients and indices of the supported basis functions given element `element_id` for the given component `component_id`. # Arguments -- `space::AbstractFESpace`: A finite element space. -- `element_id::Int`: Identifier of the element. -- `component_id::Int=1`: The component ID. This is only relevant for multi-component + + - `space::AbstractFESpace`: A finite element space. + - `element_id::Int`: Identifier of the element. + - `component_id::Int=1`: The component ID. This is only relevant for multi-component spaces, and thus defaults to 1. While it is not needed for single-component spaces, it is still required for the function signature. # Returns -- `::Matrix{Float64}`: The extraction coefficients. -- `::Vector{Int}`: The (global) basis functions supported on this element. + + - `::Matrix{Float64}`: The extraction coefficients. + - `::Vector{Int}`: The (global) basis functions supported on this element. # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no extraction operator is + + - Error "no field 'extraction_op'": This error is thrown if no extraction operator is defined for `space`, and there is no specific `get_extraction` method for `space` either. """ @@ -81,17 +90,20 @@ end Returns the extraction coefficients on the given element. # Arguments -- `space::AbstractFESpace`: A finite element space. -- `element_id::Int`: Identifier of the element. -- `component_id::Int=1`: The component ID. This is only relevant for multi-component + + - `space::AbstractFESpace`: A finite element space. + - `element_id::Int`: Identifier of the element. + - `component_id::Int=1`: The component ID. This is only relevant for multi-component spaces, and thus defaults to 1. While it is not needed for single-component spaces, it is still required for the function signature. # Returns -- `::Matrix{Float64}`: The extraction coefficients on the requested element. + + - `::Matrix{Float64}`: The extraction coefficients on the requested element. # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no extraction operator is + + - Error "no field 'extraction_op'": This error is thrown if no extraction operator is defined for `space`, and there is no specific `get_extraction_coefficients` method for `space` either. """ @@ -109,17 +121,19 @@ end Get the global indices of the basis functions of `space` on element `element_id`. # Arguments -- `space::AbstractFESpace`: Finite element space. -- `element_id::Int`: Identifier of the element. + + - `space::AbstractFESpace`: Finite element space. + - `element_id::Int`: Identifier of the element. # Returns -- `::TI`: Global indices of the basis functions supported on this element. The type `TI` + + - `::TI`: Global indices of the basis functions supported on this element. The type `TI` is an vector-like object with integer type elements. See the documentation of `space` or [`Indices`](@ref) for more details. - # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no extraction operator is + + - Error "no field 'extraction_op'": This error is thrown if no extraction operator is defined for `space`, and there is no specific `get_basis_indices` method for `space` either. """ @@ -134,20 +148,22 @@ Get the permutation' of the basis indices. This tells the `evaluate` function to on an element the evaluations correspond. # Arguments -- `space::AbstractFESpace`: Finite element space. -- `element_id::Int`: Identifier of the element. -- `component_id::Int=1`: The component ID. This is only relevant for multi-component + + - `space::AbstractFESpace`: Finite element space. + - `element_id::Int`: Identifier of the element. + - `component_id::Int=1`: The component ID. This is only relevant for multi-component spaces, and thus defaults to 1. While it is not needed for single-component spaces, it is still required for the function signature. # Returns -- `::TJ`: Permutations of indices of the basis functions supported on this element. The type + + - `::TJ`: Permutations of indices of the basis functions supported on this element. The type `TJ` is a vector-like object with integer type elements. See the documentation of `space` or [`Indices`](@ref) for more details. - # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no extraction operator is + + - Error "no field 'extraction_op'": This error is thrown if no extraction operator is defined for `space`, and there is no specific `get_basis_indices` method for `space` either. """ @@ -161,13 +177,16 @@ end Returns the number of basis functions of the finite element space `space`. # Arguments -- `space::AbstractFESpace`: Finite element space. + + - `space::AbstractFESpace`: Finite element space. # Returns -- `::Int`: Number of basis functions spanning the finite element space. + + - `::Int`: Number of basis functions spanning the finite element space. # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no extraction operator is + + - Error "no field 'extraction_op'": This error is thrown if no extraction operator is defined for `space`, and there is no specific `get_num_basis` method for `space` either. """ @@ -182,14 +201,17 @@ Get the number of basis functions of the finite element space `space` supported `element_id`. # Arguments -- `space::AbstractFESpace`: Finite element space. -- `element_id::Int`: Indentifier of the element. + + - `space::AbstractFESpace`: Finite element space. + - `element_id::Int`: Indentifier of the element. # Returns -- `::Int`: Number of basis functions supported on the given element. + + - `::Int`: Number of basis functions supported on the given element. # Exceptions -- Error "no field 'extraction_op'": This error is thrown if no extraction operator is + + - Error "no field 'extraction_op'": This error is thrown if no extraction operator is defined for `space`, and there is no specific `get_num_basis` method for `space` either. """ @@ -203,10 +225,12 @@ end Retrieve and return the degrees of freedom (d.o.f.s) partition for `space`. # Arguments -- `space::AbstractFESpace`: Single-component finite element space. + + - `space::AbstractFESpace`: Single-component finite element space. # Returns -- `::Vector{Vector{Vector{Int}}}`: Nested d.o.f. partition. The first level of nesting + + - `::Vector{Vector{Vector{Int}}}`: Nested d.o.f. partition. The first level of nesting corresponds to the patch. The second level corresponds to the division (see ... for its definition). The third level corresponds to the individual d.o.f.s. """ @@ -227,13 +251,16 @@ and returning the length of the largest union. For single-component spaces, there is no general fallback. # Arguments -- `space::AbstractFESpace`: A finite element space. + + - `space::AbstractFESpace`: A finite element space. # Returns -- `::Int`: The element-local upper bound. + + - `::Int`: The element-local upper bound. # Exceptions -- Error "'get_max_local_dim' not implemented": This error is thrown if no 'get_max_local_dim' + + - Error "'get_max_local_dim' not implemented": This error is thrown if no 'get_max_local_dim' method is defined for a single-component space (which can be a component of a multi- component space). """ @@ -258,10 +285,12 @@ end Get the geometry underlying the given `space`. # Arguments -- `space::AbstractFESpace`: A finite element space. + + - `space::AbstractFESpace`: A finite element space. # Returns -- `::AbstractGeometry`: The underlying geometry. + + - `::AbstractGeometry`: The underlying geometry. """ function get_geometry(space::AbstractFESpace) return space.geometry @@ -273,10 +302,12 @@ end Get the parametric geometry underlying the given `space`. # Arguments -- `space::AbstractFESpace`: A finite element space. + + - `space::AbstractFESpace`: A finite element space. # Returns -- `::AbstractGeometry`: The underlying parametric geometry. + + - `::AbstractGeometry`: The underlying parametric geometry. """ function get_parametric_geometry(space::AbstractFESpace) return space.parametric_geometry @@ -288,10 +319,12 @@ end Returns the total number of elements of the geometry on which the `space` is build. # Arguments -- `space::AbstractFESpace`: A finite element space. + + - `space::AbstractFESpace`: A finite element space. # Returns -- `::Int`: The number of elements. + + - `::Int`: The number of elements. """ function get_num_elements(space::AbstractFESpace) return Geometry.get_num_elements(get_geometry(space)) @@ -303,10 +336,12 @@ end Get the number of elements per patch of the underlying geometry. # Arguments -- `space::AbstractFESpace`: A finite element space. + + - `space::AbstractFESpace`: A finite element space. # Returns -- `::NTuple{num_patches, Int}`: The number of elements per patch. + + - `::NTuple{num_patches, Int}`: The number of elements per patch. """ function get_num_elements_per_patch(space::AbstractFESpace) return Geometry.get_num_elements_per_patch(get_geometry(space)) @@ -319,11 +354,13 @@ Get the ID of the patch of the underlying geometry to which the specified global belongs. # Arguments -- `space::AbstractFESpace`: A finite element space. -- `element_id::Int`: The global element ID. + + - `space::AbstractFESpace`: A finite element space. + - `element_id::Int`: The global element ID. # Returns -- `::Int`: ID of the patch to which the element belongs. + + - `::Int`: ID of the patch to which the element belongs. """ function get_patch_id(space::AbstractFESpace, element_id::Int) return Geometry.get_patch_id(get_geometry(space), element_id) @@ -336,12 +373,14 @@ Get the constituent patch ID and local element ID of the underlying geometry for specified global element ID. # Arguments -- `space::AbstractFESpace`: A finite element space. -- `element_id::Int`: The global element ID. + + - `space::AbstractFESpace`: A finite element space. + - `element_id::Int`: The global element ID. # Returns -- `patch_id::Int`: The patch ID -- `local_element_id::Int`: The local element ID. + + - `patch_id::Int`: The patch ID + - `local_element_id::Int`: The local element ID. """ function get_patch_and_local_element_id(space::AbstractFESpace, element_id::Int) return Geometry.get_patch_and_local_element_id(get_geometry(space), element_id) @@ -354,12 +393,14 @@ Get the global element ID of the underlying geometry for the specified constitue and local element ID. # Arguments -- `space::AbstractFESpace`: A finite element space. -- `patch_id::Int`: The constituent patch ID. -- `local_element_id::Int`: The local element ID. + + - `space::AbstractFESpace`: A finite element space. + - `patch_id::Int`: The constituent patch ID. + - `local_element_id::Int`: The local element ID. # Returns -- `::Int`: The global element ID. + + - `::Int`: The global element ID. """ function get_global_element_id(space::AbstractFESpace, patch_id::Int, local_element_id::Int) return Geometry.get_global_element_id(get_geometry(space), patch_id, local_element_id) @@ -372,11 +413,13 @@ Computes the measure of the element given by `element_id` of the geometry on whi `space` is build. # Arguments -- 'space::AbstractFESpace': A finite element space. -- 'element_id::Int': Index of the element being considered. + + - 'space::AbstractFESpace': A finite element space. + - 'element_id::Int': Index of the element being considered. # Returns -- '<:Number': The measure of the element. + + - '<:Number': The measure of the element. """ function get_element_measure(space::AbstractFESpace, element_id::Int) return Geometry.get_element_measure(get_geometry(space), element_id) @@ -389,11 +432,13 @@ Computes the length, in each manifold dimension, of the element given by `elemen the geometry on which the `space` is build. # Arguments -- 'space::AbstractFESpace': A finite element space. -- 'element_id::Int': Index of the element being considered. + + - 'space::AbstractFESpace': A finite element space. + - 'element_id::Int': Index of the element being considered. # Returns -- '<:NTuple{manifold_dim, Number}': The element's lengths. + + - '<:NTuple{manifold_dim, Number}': The element's lengths. """ function get_element_lengths(space::AbstractFESpace, element_id::Int) return Geometry.get_element_lengths(get_geometry(space), element_id) @@ -406,11 +451,13 @@ Computes the vertices, in each manifold dimension, of the element given by `elem the geometry on which the `space` is build. # Arguments -- 'space::AbstractFESpace': A finite element space. -- 'element_id::Int': Index of the element being considered. + + - 'space::AbstractFESpace': A finite element space. + - 'element_id::Int': Index of the element being considered. # Returns -- '<:NTuple{manifold_dim, NTuple{2, Number}}': The element's vertices per manifold dim. For + + - '<:NTuple{manifold_dim, NTuple{2, Number}}': The element's vertices per manifold dim. For example, for the unit cube [0.0, 1.0]^3 this will be: ((0.0, 1.0), (0.0, 1.0), (0.0, 1.0)). """ @@ -433,20 +480,22 @@ default for multi-component spaces are the component spaces. There is no default component spaces. Single-component spaces are expected to have a specialised implementation. # Arguments -- `space::AbstractFESpace{manifold_dim, num_components, num_patches}`: A finite element + + - `space::AbstractFESpace{manifold_dim, num_components, num_patches}`: A finite element space. -- `element_id::Int`: The indentifier of the element. -- `xi::Points.AbstractPoints{manifold_dim}`: The coordinates at which to evaluate the + - `element_id::Int`: The indentifier of the element. + - `xi::Points.AbstractPoints{manifold_dim}`: The coordinates at which to evaluate the basis functions. These coordinates are in the **canonical** domain, and thus always lie in the interval `[0, 1]`. The coordinates have to be given per dimension. Multi- dimensional spaces are evaluated on the tensor product of the given coordinates. -- `nderivatives::Int`: The number of derivatives to compute. -- `component_id::Int=1`: The component ID. This is only relevant for multi-component + - `nderivatives::Int`: The number of derivatives to compute. + - `component_id::Int=1`: The component ID. This is only relevant for multi-component spaces, and thus defaults to 1. While it is not needed for single-component spaces, it is still required for the function signature. # Returns -- `::Vector{Vector{Vector{Matrix{Float64}}}}`: A nested vector structure containing the + + - `::Vector{Vector{Vector{Matrix{Float64}}}}`: A nested vector structure containing the local basis functions and their derivatives. The first level of nesting corresponds to the order of the derivatives. The second level corresponds to a specific derivative. The third level corresponds to the component. The matrix contains the actual evaluations. @@ -485,24 +534,27 @@ evaluate method actually returns the values/derivatives of `f∘Φᵢ`. Consider `f₀ := f∘Φᵢ`. Then, `FunctionSpaces.evaluate(f)` returns `evaluations::Vector{Vector{Vector{Matrix{Float64}}}` where `evaluations[i][j][k][a,b]` is the evaluation of: -- the `j`-th mixed derivative ... -- of order `i-1` ... -- for the `b`-th basis function ... -- of the `k`-th component ... -- at the `a`-th evaluation point ... -- for `f₀`. -See [`get_derivative_idx`](@ref GeneralHelpers.get_derivative_idx) for more details on -the order in which all the mixed derivatives of order `i-1` are stored. + + - the `j`-th mixed derivative ... + - of order `i-1` ... + - for the `b`-th basis function ... + - of the `k`-th component ... + - at the `a`-th evaluation point ... + - for `f₀`. + See [`get_derivative_idx`](@ref GeneralHelpers.get_derivative_idx) for more details on + the order in which all the mixed derivatives of order `i-1` are stored. # Arguments -- `space::AbstractFESpace{manifold_dim, num_components, num_patches}`: Finite element space. -- `element_id::Int`: Index of the element. -- `xi::Points.AbstractPoints{manifold_dim}`: Point on the element in canonical coordinates. -- `nderivatives::Int=0`: Order of the derivatives. Default is 0 (i.e., function evaluation). + + - `space::AbstractFESpace{manifold_dim, num_components, num_patches}`: Finite element space. + - `element_id::Int`: Index of the element. + - `xi::Points.AbstractPoints{manifold_dim}`: Point on the element in canonical coordinates. + - `nderivatives::Int=0`: Order of the derivatives. Default is 0 (i.e., function evaluation). # Returns -- `evaluation::Vector{Vector{Vector{Matrix{Float64}}}}`: Values of the basis functions. -- `basis_indices::TI`: Global indices of the basis functions. The type `TI` is an vector- + + - `evaluation::Vector{Vector{Vector{Matrix{Float64}}}}`: Values of the basis functions. + - `basis_indices::TI`: Global indices of the basis functions. The type `TI` is an vector- like object with integer type elements. See the documentation of `space` or [`Indices`](@ref) for more details. """ @@ -561,16 +613,17 @@ Evaluate the basis functions with coefficients of the finite element space `spac point `xi` on element `element_id` up to order `nderivatives`. See [`evaluate`](@ref) for more details. - # Arguments -- `space::AbstractFESpace{manifold_dim, num_components, num_patches}`: Finite element space. -- `element_id::Int`: Index of the element. -- `xi::Points.AbstractPoints{manifold_dim}`: Point on the element in canonical coordinates. -- `nderivatives::Int=0`: Order of the derivatives. Default is 0 (i.e., function evaluation). -- `coefficients::Vector{Float64}`: Coefficients. + + - `space::AbstractFESpace{manifold_dim, num_components, num_patches}`: Finite element space. + - `element_id::Int`: Index of the element. + - `xi::Points.AbstractPoints{manifold_dim}`: Point on the element in canonical coordinates. + - `nderivatives::Int=0`: Order of the derivatives. Default is 0 (i.e., function evaluation). + - `coefficients::Vector{Float64}`: Coefficients. # Returns -- `evaluation::Vector{Vector{Vector{Vector{Float64}}}}`: Values at the points. + + - `evaluation::Vector{Vector{Vector{Vector{Float64}}}}`: Values at the points. """ function evaluate( space::AbstractFESpace{manifold_dim, num_components, num_patches}, @@ -609,13 +662,15 @@ coefficients from each element on each patch. The global extraction matrix maps degrees of freedom to the local degrees of freedom. # Arguments -- `space::AbstractFESpace`: The finite element space. + + - `space::AbstractFESpace`: The finite element space. # Returns -- `SparseMatrixCSC{Float64}`: The global extraction matrix that maps global dofs to local dofs + + - `SparseMatrixCSC{Float64}`: The global extraction matrix that maps global dofs to local dofs """ function assemble_global_extraction_matrix(space::AbstractFESpace) - throw( + return throw( ArgumentError( "'assemble_global_extraction_matrix' not implemented for $(typeof(space))" ), @@ -634,13 +689,15 @@ Evaluates all derivatives up to order `nderivatives` for all basis functions of at a given point `xi` in the element `element_id`. # Arguments -- `fem_space::AbstractFESpace{1, num_components}`: A univariate FEM space. -- `element_id::Int`: The id of the element. -- `xi::Float64`: The point where all global basis functiuons are evaluated. -- `nderivatives::Int`: The order upto which derivatives need to be computed. + + - `fem_space::AbstractFESpace{1, num_components}`: A univariate FEM space. + - `element_id::Int`: The id of the element. + - `xi::Float64`: The point where all global basis functiuons are evaluated. + - `nderivatives::Int`: The order upto which derivatives need to be computed. # Returns -- `::SparseMatrixCSC{Float64}`: Global basis functions, size = n_dofs x nderivatives+1 + + - `::SparseMatrixCSC{Float64}`: Global basis functions, size = n_dofs x nderivatives+1 """ function _evaluate_all_at_point( fem_space::AbstractFESpace{1, num_components, num_patches}, @@ -682,11 +739,13 @@ This function calculates the coefficients necessary for representing the paramet of a finite element space. # Arguments -- `fem_space::F`: The finite element space for which to compute the parametric geometry + + - `fem_space::F`: The finite element space for which to compute the parametric geometry coefficients. # Returns -- `coeffs::Matrix{Float64}`: The coefficients necessary for representing the parametric + + - `coeffs::Matrix{Float64}`: The coefficients necessary for representing the parametric geometry. The size of the matrix is (num_basis x manifold_dim). """ function _compute_parametric_geometry_coeffs( @@ -751,4 +810,3 @@ include("UnstructuredSpaces/PolarSplines.jl") include("TwoScaleRelations/AbstractTwoScaleRelations.jl") include("Hierarchical/Hierarchical.jl") - diff --git a/src/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalFiniteElementSpaces.jl b/src/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalFiniteElementSpaces.jl index d2f1f23cf..b1995d79e 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalFiniteElementSpaces.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalFiniteElementSpaces.jl @@ -3,9 +3,9 @@ ############################################################################################ """ - HierarchicalFiniteElementSpace{ - manifold_dim, num_components, num_patches, S, T, G, GP - } <: AbstractFESpace{manifold_dim, num_components, num_patches} + HierarchicalFiniteElementSpace{ + manifold_dim, num_components, num_patches, S, T, G, GP + } <: AbstractFESpace{manifold_dim, num_components, num_patches} A hierarchical space that is built from a nested hierarchy of `manifold_dim`-variate function `spaces` and `nested_domains`. At each level of the hierarchy, a certain set of @@ -15,34 +15,35 @@ the rest is inactive. See [Giannelli2013](@cite) for more information. # Fields -- `geometry::G`: The hierarchical geometry associated with the hierarchical space. Will be a + + - `geometry::G`: The hierarchical geometry associated with the hierarchical space. Will be a subtype of [`Geometry.HierarchicalGeometry`](@ref). -- `parametric_geometry::GP`: The parametic hierarchical geometry associated with the + - `parametric_geometry::GP`: The parametic hierarchical geometry associated with the hierarchy of parametic geometries from each level. Will be a subtype of [`Geometry.HierarchicalGeometry`](@ref). -- `spaces::Vector{S} `: Collection of `L` `manifold_dim`-variate function spaces, where `L` + - `spaces::Vector{S} `: Collection of `L` `manifold_dim`-variate function spaces, where `L` is the total number of levels. -- `two_scale_operators::Vector{T}`: Collection of `L-1` two-scale operators relating each + - `two_scale_operators::Vector{T}`: Collection of `L-1` two-scale operators relating each consecutive pair of finite element spaces, where `L` is the total number of levels. See [`AbstractTwoScaleOperator`](@ref). -- `active_elements::Hierarchy.ActiveInfo`: Information about the active elements at each + - `active_elements::Hierarchy.ActiveInfo`: Information about the active elements at each level. See [`Hierarchy.ActiveInfo`](@ref). -- `active_basis::Hierarchy.ActiveInfo`: Information about the active basis at each level. + - `active_basis::Hierarchy.ActiveInfo`: Information about the active basis at each level. See [`Hierarchy.ActiveInfo`](@ref). -- `nested_domains::Hierarchy.ActiveInfo`: Information about the nested domains at each + - `nested_domains::Hierarchy.ActiveInfo`: Information about the nested domains at each level. This is the usual definition of Ωₗ in the literature. See [`Hierarchy.ActiveInfo`](@ref). -- `multilevel_elements::SparseArrays.SparseVector{Int, Int}`: Elements where basis from + - `multilevel_elements::SparseArrays.SparseVector{Int, Int}`: Elements where basis from multiple levels have non-empty support. -- `multilevel_extraction_coeffs::Vector{NTuple{num_components, Matrix{Float64}}}`: + - `multilevel_extraction_coeffs::Vector{NTuple{num_components, Matrix{Float64}}}`: Extraction coefficients of active basis functions in `multilevel_elements`. -- `multilevel_basis_indices::Vector{Vector{Int}}`: Indices of active basis in + - `multilevel_basis_indices::Vector{Vector{Int}}`: Indices of active basis in `multilevel_elements`, in hierarchical indexing. -- `num_subdivisions::NTuple{manifold_dim, Int}`: Number of subdivisions per `manifold_dim`, + - `num_subdivisions::NTuple{manifold_dim, Int}`: Number of subdivisions per `manifold_dim`, per level for the hierarchical mesh. -- `truncated::Bool`: Flag for truncated hierarchical spaces. -- `simplified::Bool`: Flag for simplified hierarchical spaces. -- `dof_partition::Vector{Vector{Vector{Int}}}`: The degree-of-freedom partitioning of the + - `truncated::Bool`: Flag for truncated hierarchical spaces. + - `simplified::Bool`: Flag for simplified hierarchical spaces. + - `dof_partition::Vector{Vector{Vector{Int}}}`: The degree-of-freedom partitioning of the hierarchical space, in hierarchical indexing. """ struct HierarchicalFiniteElementSpace{ @@ -141,20 +142,20 @@ struct HierarchicalFiniteElementSpace{ end """ - HierarchicalFiniteElementSpace( - spaces::Vector{S}, - two_scale_operators::Vector{T}, - domains::Hierarchy.ActiveInfo, - num_subdivisions::NTuple{manifold_dim, Int}, - truncated::Bool=true, - simplified::Bool=false, - ) where { - manifold_dim, - num_components, - num_patches, - S <: AbstractFESpace{manifold_dim, num_components, num_patches}, - T <: AbstractTwoScaleOperator, - } + HierarchicalFiniteElementSpace( + spaces::Vector{S}, + two_scale_operators::Vector{T}, + domains::Hierarchy.ActiveInfo, + num_subdivisions::NTuple{manifold_dim, Int}, + truncated::Bool=true, + simplified::Bool=false, + ) where { + manifold_dim, + num_components, + num_patches, + S <: AbstractFESpace{manifold_dim, num_components, num_patches}, + T <: AbstractTwoScaleOperator, + } Constructor that generates multilevel information. """ @@ -207,20 +208,20 @@ function HierarchicalFiniteElementSpace( end """ - HierarchicalFiniteElementSpace( - spaces::Vector{S}, - two_scale_operators::Vector{T}, - domains_per_level::Vector{Vector{Int}}, - num_subdivisions::NTuple{manifold_dim, Int}, - truncated::Bool=true, - simplified::Bool=false, - ) where { - manifold_dim, - num_components, - num_patches, - S <: AbstractFESpace{manifold_dim, num_components, num_patches}, - T <: AbstractTwoScaleOperator, - } + HierarchicalFiniteElementSpace( + spaces::Vector{S}, + two_scale_operators::Vector{T}, + domains_per_level::Vector{Vector{Int}}, + num_subdivisions::NTuple{manifold_dim, Int}, + truncated::Bool=true, + simplified::Bool=false, + ) where { + manifold_dim, + num_components, + num_patches, + S <: AbstractFESpace{manifold_dim, num_components, num_patches}, + T <: AbstractTwoScaleOperator, + } Constructor for domains given in a per-level vector. """ @@ -246,12 +247,12 @@ function HierarchicalFiniteElementSpace( end """ - HierarchicalFiniteElementSpace( - space::S, - num_subdivisions::NTuple{manifold_dim, Int}, - truncated::Bool=true, - simplified::Bool=false, - ) where {manifold_dim, S <: AbstractFESpace{manifold_dim}} + HierarchicalFiniteElementSpace( + space::S, + num_subdivisions::NTuple{manifold_dim, Int}, + truncated::Bool=true, + simplified::Bool=false, + ) where {manifold_dim, S <: AbstractFESpace{manifold_dim}} Constructor for a Hierarchical space with no refinement. This is useful for initializating a hierarchical space that will later be refined. @@ -276,11 +277,11 @@ end """ get_active_objects_and_nested_domains( - spaces::Vector{S}, - two_scale_operators::Vector{T}, - domains::Hierarchy.ActiveInfo, - simplified::Bool, - ) where {S <: AbstractFESpace, T <: AbstractTwoScaleOperator} + spaces::Vector{S}, + two_scale_operators::Vector{T}, + domains::Hierarchy.ActiveInfo, + simplified::Bool, + ) where {S <: AbstractFESpace, T <: AbstractTwoScaleOperator} Computes the active elements and basis on each level based on `spaces`, `two_scale_operators` and the set of nested `domains`. @@ -292,17 +293,19 @@ deactivated basis, based on wheter the space is `simplified` or not. A similar l applied to determine the active elements. # Arguments -- `spaces::Vector{AbstractFESpace{manifold_dim, num_components, num_patches}}`: Finite - element spaces at each level. -- `two_scale_operators::Vector{AbstractTwoScaleOperator}`: Two scale operators relating the + + - `spaces::Vector{AbstractFESpace{manifold_dim, num_components, num_patches}}`: Finite + element spaces at each level. + - `two_scale_operators::Vector{AbstractTwoScaleOperator}`: Two scale operators relating the finite element spaces at each level. -- `domains::Hierarchy.ActiveInfo`: Nested domains where the support of active basis is + - `domains::Hierarchy.ActiveInfo`: Nested domains where the support of active basis is determined. # Returns -- `active_elements::Hierarchy.ActiveInfo`: Active elements at each level. -- `active_basis::Hierarchy.ActiveInfo`: Active basis at each level. -- `nested_domains::Hierarchy.ActiveInfo`: Information about the nested domains at each + + - `active_elements::Hierarchy.ActiveInfo`: Active elements at each level. + - `active_basis::Hierarchy.ActiveInfo`: Active basis at each level. + - `nested_domains::Hierarchy.ActiveInfo`: Information about the nested domains at each level. This is the usual definition of Ωₗ in the literature. See [`Hierarchy.ActiveInfo`](@ref). """ @@ -447,19 +450,21 @@ and active basis indices. The extraction coefficients depend on whether the hierarchical space is `truncated` or not. # Arguments -- `spaces::Vector{AbstractFESpace{manifold_dim, num_components, num_patches}}`: finite element spaces at each level. -- `two_scale_operators::Vector{AbstractTwoScaleOperator}`: two scale operators relating the + + - `spaces::Vector{AbstractFESpace{manifold_dim, num_components, num_patches}}`: finite element spaces at each level. + - `two_scale_operators::Vector{AbstractTwoScaleOperator}`: two scale operators relating the finite element spaces at each level. -- `active_elements::Hierarchy.ActiveInfo`: active elements on each level. -- `active_basis::Hierarchy.ActiveInfo`: active basis on each level. -- `truncated`: flag for a truncated hierarchical space. + - `active_elements::Hierarchy.ActiveInfo`: active elements on each level. + - `active_basis::Hierarchy.ActiveInfo`: active basis on each level. + - `truncated`: flag for a truncated hierarchical space. # Returns -- `multilevel_elements::SparseArrays.SparseVector{Int, Int}`: elements where basis from + + - `multilevel_elements::SparseArrays.SparseVector{Int, Int}`: elements where basis from multiple levels have non-empty support. -- `multilevel_extraction_coeffs::Vector{Matrix{Float64}}`: extraction coefficients of + - `multilevel_extraction_coeffs::Vector{Matrix{Float64}}`: extraction coefficients of active basis in `multilevel_elements`. -- `multilevel_basis_indices::Vector{Vector{Int}}`: indices of active basis in + - `multilevel_basis_indices::Vector{Vector{Int}}`: indices of active basis in `multilevel_elements`. """ function get_multilevel_extraction( @@ -561,14 +566,16 @@ multiple levels have non-empty support, as well as which basis from parentr leve active on those elements. # Arguments -- `spaces::Vector{AbstractFESpace{manifold_dim, num_components, num_patches}}`: finite element spaces at each level. -- `two_scale_operators::Vector{AbstractTwoScaleOperator}`: two scale operators relating the + + - `spaces::Vector{AbstractFESpace{manifold_dim, num_components, num_patches}}`: finite element spaces at each level. + - `two_scale_operators::Vector{AbstractTwoScaleOperator}`: two scale operators relating the finite element spaces at each level. -- `active_elements::Hierarchy.ActiveInfo`: active elements on each level. -- `active_basis::Hierarchy.ActiveInfo`: active basis on each level. + - `active_elements::Hierarchy.ActiveInfo`: active elements on each level. + - `active_basis::Hierarchy.ActiveInfo`: active basis on each level. # Returns -- `multilevel_information::Dict{Tuple{Int, Int}, Vector{Tuple{Int, Int}}}`: information + + - `multilevel_information::Dict{Tuple{Int, Int}, Vector{Tuple{Int, Int}}}`: information about multilevel elements. The key's two indices indicate the multilevel element's level and id and the and the key's value is a vector of tuples where the indices are the basis level and id (from parentr levels), respectively. @@ -721,12 +728,14 @@ Updates `refinement_matrix` by the rows of `active_indices` to zeros in lower le functions. # Arguments -- `refinement_matrix`: the refinement matrix to be updated. -- `active_indices::Vector{Int}`: element local indices of active basis functions from the + + - `refinement_matrix`: the refinement matrix to be updated. + - `active_indices::Vector{Int}`: element local indices of active basis functions from the highest refinement level. # Returns -- `refinement_matrix`: truncated refinement matrix. + + - `refinement_matrix`: truncated refinement matrix. """ function truncate_refinement_matrix!(refinement_matrix, active_indices::Vector{Int}) active_length = length(active_indices) @@ -796,8 +805,9 @@ function get_extraction( basis_indices = collect( get_basis_indices(get_space(space, element_level), element_level_id) ) - basis_indices .= - convert_to_basis_hier_id.(Ref(space), Ref(element_level), basis_indices) + basis_indices .= convert_to_basis_hier_id.( + Ref(space), Ref(element_level), basis_indices + ) else element_level, element_level_id = convert_to_element_level_and_level_id( space, hier_id @@ -872,18 +882,20 @@ end ############################################################################################ """ - refine_space(space::HierarchicalFiniteElementSpace, domains::Hierarchy.ActiveInfo) + refine_space(space::HierarchicalFiniteElementSpace, domains::Hierarchy.ActiveInfo) Returns a refined hierarchical space, given an original `space` and a set of hierarchically nested `domains`. # Arguments -- `space::HierarchicalFiniteElementSpace`: The original hierarchical space. -- `domains::Hierarchy.ActiveInfo`: Information about the nested domains at each level. This - is the usual definition of Ωₗ in the literature. See [`Hierarchy.ActiveInfo`](@ref). + + - `space::HierarchicalFiniteElementSpace`: The original hierarchical space. + - `domains::Hierarchy.ActiveInfo`: Information about the nested domains at each level. This + is the usual definition of Ωₗ in the literature. See [`Hierarchy.ActiveInfo`](@ref). # Returns -- `refine_space::HierarchicalFiniteElementSpace`: A refined hierarchical space. + + - `refine_space::HierarchicalFiniteElementSpace`: A refined hierarchical space. """ function refine_space(space::HierarchicalFiniteElementSpace, domains::Hierarchy.ActiveInfo) L = Hierarchy.get_num_levels(domains) @@ -910,21 +922,23 @@ function refine_space(space::HierarchicalFiniteElementSpace, domains::Hierarchy. end """ - refine_space( - space::HierarchicalFiniteElementSpace, marked_elements_per_level::Vector{Vector{Int}} - ) + refine_space( + space::HierarchicalFiniteElementSpace, marked_elements_per_level::Vector{Vector{Int}} + ) Returns a refined hierarchical space, given an original `space` and a set of `marked_elements_per_level`. The elements are used for refinement at each level by extracting their children, and consequently updating the `nested_domains` of `space`. # Arguments -- `space::HierarchicalFiniteElementSpace`: The original hierarchical space. -- `marked_elements_per_level::Vector{Vector{Int}}`: The elements marked for refinement at - each level, which will be used to refine the nested domains. + + - `space::HierarchicalFiniteElementSpace`: The original hierarchical space. + - `marked_elements_per_level::Vector{Vector{Int}}`: The elements marked for refinement at + each level, which will be used to refine the nested domains. # Returns -- `refine_space::HierarchicalFiniteElementSpace`: A refined hierarchical space. + + - `refine_space::HierarchicalFiniteElementSpace`: A refined hierarchical space. """ function refine_space( space::HierarchicalFiniteElementSpace, marked_elements_per_level::Vector{Vector{Int}} @@ -956,24 +970,26 @@ function refine_space( end """ - refine_domains!( - domains::Hierarchy.ActiveInfo, - two_scale_operators, - marked_elements_per_level::Vector{Vector{Int}}, - ) + refine_domains!( + domains::Hierarchy.ActiveInfo, + two_scale_operators, + marked_elements_per_level::Vector{Vector{Int}}, + ) Refines `domains` in-place, by extracting the children of the `marked_elements_per_level` using `two_scale_operators`. # Arguments -- `domains::Hierarchy.ActiveInfo`: The hierarchical domains to be refined. -- `two_scale_operators`: The two-scale operators used to extract the children of the marked - elements. -- `marked_elements_per_level::Vector{Vector{Int}}`: The elements marked for refinement at - each level, which will be used to refine the nested domains. + + - `domains::Hierarchy.ActiveInfo`: The hierarchical domains to be refined. + - `two_scale_operators`: The two-scale operators used to extract the children of the marked + elements. + - `marked_elements_per_level::Vector{Vector{Int}}`: The elements marked for refinement at + each level, which will be used to refine the nested domains. # Returns -- `domains::Hierarchy.ActiveInfo`: The refined `domains`. + + - `domains::Hierarchy.ActiveInfo`: The refined `domains`. """ function refine_domains!( domains::Hierarchy.ActiveInfo, @@ -991,7 +1007,7 @@ function refine_domains!( end """ - refine_domains!(domains, two_scale_operators, marked_elements, level) + refine_domains!(domains, two_scale_operators, marked_elements, level) Refines `domains` in-place, by extracting the children of the `marked_elements` using `two_scale_operators` at the given `level`. @@ -1185,19 +1201,21 @@ function convert_to_basis_level_and_level_id( end """ - convert_element_vector_to_elements_per_level( - space::HierarchicalFiniteElementSpace, hier_ids::Vector{Int} - ) + convert_element_vector_to_elements_per_level( + space::HierarchicalFiniteElementSpace, hier_ids::Vector{Int} + ) Separates a vector of `hier_ids` in hierarchical indexing into a set of level-wise indices. # Arguments -- `space::HierarchicalFiniteElementSpace`: The hierarchical finite element space. -- `hier_ids::Vector{Int}`: The list of hierarchical indices. + + - `space::HierarchicalFiniteElementSpace`: The hierarchical finite element space. + - `hier_ids::Vector{Int}`: The list of hierarchical indices. # Returns -- `Vector{Vector{Int}}`: The level-wise indices. The length of the outer vector is the - number of levels of `space`. + + - `Vector{Vector{Int}}`: The level-wise indices. The length of the outer vector is the + number of levels of `space`. """ function convert_element_vector_to_elements_per_level( space::HierarchicalFiniteElementSpace, hier_ids::Vector{Int} diff --git a/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/DirectSumSpace.jl b/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/DirectSumSpace.jl index 8b97ca7ca..fa9f794ac 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/DirectSumSpace.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/DirectSumSpace.jl @@ -9,11 +9,12 @@ block-diagonal matrix. Each scalar function space contributes to a separate comp the multi-component space. # Fields -- `component_spaces::F`: Tuple of `num_components` scalar function spaces -- `basis_offsets::NTuple{num_components, Int}`: Offsets of the basis functions of each + + - `component_spaces::F`: Tuple of `num_components` scalar function spaces + - `basis_offsets::NTuple{num_components, Int}`: Offsets of the basis functions of each component space to get the global basis functions numbers. -- `num_elements::Int`: Number of elements in the space. -- `space_dim::Int`: Dimension of the space, i.e., the number of global d.o.f.s. + - `num_elements::Int`: Number of elements in the space. + - `space_dim::Int`: Dimension of the space, i.e., the number of global d.o.f.s. """ struct DirectSumSpace{manifold_dim, num_components, num_patches, F} <: AbstractFESpace{manifold_dim, num_components, num_patches} diff --git a/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/RationalFESpaces.jl b/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/RationalFESpaces.jl index efbfa788f..947206413 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/RationalFESpaces.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/OtherSpaces/RationalFESpaces.jl @@ -7,8 +7,9 @@ by a fixed element from the same function space. The latter is defined with the specified weights. # Fields -- `function_space::F`: The underlying function space. -- `weights::Vector{Float64}`: The weights associated with the basis functions of the + + - `function_space::F`: The underlying function space. + - `weights::Vector{Float64}`: The weights associated with the basis functions of the function space. """ struct RationalFESpace{manifold_dim, F} <: AbstractFESpace{manifold_dim, 1, 1} @@ -56,10 +57,12 @@ Returns the polynomial degree (or the degree of the underlying function space) o rational finite element space for a specific element. # Arguments -- `space::RationalFESpace`: The rational finite element space. -- `element_id::Int`: The index of the element. + + - `space::RationalFESpace`: The rational finite element space. + - `element_id::Int`: The index of the element. # Returns + The polynomial degree (or the degree of the underlying function space) of the rational finite element space for the specified element. """ diff --git a/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/BSplines.jl b/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/BSplines.jl index 8ae09e760..a3dbf2efc 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/BSplines.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/BSplines.jl @@ -6,11 +6,13 @@ Compute the Greville points for a `TensorProductSpace` composed of `BSplineSpace`s. # Arguments -- `space::TensorProductSpace{manifold_dim, num_components, num_patches, num_spaces, T}`: + + - `space::TensorProductSpace{manifold_dim, num_components, num_patches, num_spaces, T}`: The tensor product space containing `BSplineSpace`s. # Returns -- `::NTuple{manifold_dim, Vector{Float64}}`: A tuple of vectors containing the Greville + + - `::NTuple{manifold_dim, Vector{Float64}}`: A tuple of vectors containing the Greville points for each dimension of the tensor product space. """ function get_greville_points( diff --git a/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductSpaces.jl b/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductSpaces.jl index 08ad108da..fee99d5ed 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductSpaces.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductSpaces.jl @@ -8,15 +8,16 @@ A structure representing a `TensorProductSpace`, defined by the tensor product o the sum of the constituent spaces' manifold dimensions. # Fields -- `constituent_spaces::T`: A tuple of constituent finite element spaces to be tensored. -- `geometry::G`: The underlying physical geometry. -- `parametric_geometry::GP`: The underlying parametric geometry. The function space is + + - `constituent_spaces::T`: A tuple of constituent finite element spaces to be tensored. + - `geometry::G`: The underlying physical geometry. + - `parametric_geometry::GP`: The underlying parametric geometry. The function space is defined with respect to this. -- `cart_num_basis::CIB`: To convert from tensor-product indexing to constituent-wise + - `cart_num_basis::CIB`: To convert from tensor-product indexing to constituent-wise indexing for basis functions. -- `lin_num_basis::LIB`: To convert from constituent-wise indexing to tensor-product indexing + - `lin_num_basis::LIB`: To convert from constituent-wise indexing to tensor-product indexing for basis functions. -- `dof_partition::D`: See [`get_dof_partition`](@ref). + - `dof_partition::D`: See [`get_dof_partition`](@ref). """ struct TensorProductSpace{ manifold_dim, num_components, num_patches, num_spaces, T, G, GP, CIB, LIB, D @@ -212,7 +213,7 @@ get_constituent_spaces(space::TensorProductSpace) = space.constituent_spaces get_num_basis(space::TensorProductSpace) = prod(get_constituent_num_basis(space)) """ - get_cart_num_elements(space::TensorProductSpace) + get_cart_num_elements(space::TensorProductSpace) See [`Geometry.get_cart_num_elements`](@ref). """ @@ -221,7 +222,7 @@ function get_cart_num_elements(space::TensorProductSpace) end """ - get_lin_num_elements(space::TensorProductSpace) + get_lin_num_elements(space::TensorProductSpace) See [`Geometry.get_lin_num_elements`](@ref). """ @@ -724,8 +725,8 @@ function evaluate( else eval[der_order + 1][der_id][1] = kron( ( - const_eval[space][space_der_order[space] + 1][space_der_id[space]][1] for - space in num_spaces:-1:1 + const_eval[space][space_der_order[space] + 1][space_der_id[space]][1] + for space in num_spaces:-1:1 )..., ) end diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/AbstractTwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/AbstractTwoScaleRelations.jl index 29749824b..033f29e08 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/AbstractTwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/AbstractTwoScaleRelations.jl @@ -15,11 +15,13 @@ abstract type AbstractTwoScaleOperator{manifold_dim, num_components, num_patches Retrieve and return the global subdivision matrix associated with a two-scale operator. # Arguments -- `operator::AbstractTwoScaleOperator`: The two-scale operator from which to + + - `operator::AbstractTwoScaleOperator`: The two-scale operator from which to retrieve the global subdivision matrix. # Returns -- `::SparseArrays.SparseMatrixCSC{Float64, Int}`: The global subdivision matrix associated + + - `::SparseArrays.SparseMatrixCSC{Float64, Int}`: The global subdivision matrix associated with the two-scale operator. """ get_global_subdiv_matrix(operator::AbstractTwoScaleOperator) = operator.global_subdiv_matrix @@ -30,24 +32,29 @@ get_global_subdiv_matrix(operator::AbstractTwoScaleOperator) = operator.global_s Retrieve and return the coarse space associated with a two-scale operator. # Arguments -- `operator::TwoScaleOperator`: The two-scale operator from which to retrieve the + + - `operator::TwoScaleOperator`: The two-scale operator from which to retrieve the coarse space. # Returns -- `::AbstractFESpace`: The coarse space associated with the two-scale operator. + + - `::AbstractFESpace`: The coarse space associated with the two-scale operator. """ get_parent_space(operator::AbstractTwoScaleOperator) = operator.parent_space """ get_child_space(operator::TwoScaleOperator) + Retrieve and return the fine space associated with a two-scale operator. # Arguments -- `operator::TwoScaleOperator`: The two-scale operator from which to retrieve the + + - `operator::TwoScaleOperator`: The two-scale operator from which to retrieve the fine space. # Returns -- `::AbstractFESpace`: The fine space associated with the two-scale operator. + + - `::AbstractFESpace`: The fine space associated with the two-scale operator. """ get_child_space(operator::AbstractTwoScaleOperator) = operator.child_space @@ -58,14 +65,16 @@ Compute and return the ancestor element ID for a given child element ID within a number of ancestor levels. # Arguments -- `two_scale_operators`: A collection of operators that define the parent-child + + - `two_scale_operators`: A collection of operators that define the parent-child relationships between elements at different levels. -- `child_element_id::Int`: The identifier of the child element w.r.t. `child_level`. -- `child_level::Int`: The level of the child element. -- `num_ancestor_levels::Int`: The number of ancestor levels to traverse. + - `child_element_id::Int`: The identifier of the child element w.r.t. `child_level`. + - `child_level::Int`: The level of the child element. + - `num_ancestor_levels::Int`: The number of ancestor levels to traverse. # Returns -- `ancestor_id::Int`: The identifier of the ancestor element. + + - `ancestor_id::Int`: The identifier of the ancestor element. """ function get_element_ancestor( two_scale_operators::Vector{TS}, @@ -88,13 +97,15 @@ Perform a change of basis using a two-scale operator from a coarse basis and ret resulting fine basis coefficients. # Arguments -- `coarse_basis_coeffs::Vector{Float64}`: A vector containing the coefficients of the + + - `coarse_basis_coeffs::Vector{Float64}`: A vector containing the coefficients of the coarse basis. -- `operator::T`: The two-scale operator that defines the subdivision process, + - `operator::T`: The two-scale operator that defines the subdivision process, where `T` is a subtype of `AbstractTwoScaleOperator`. # Returns -- `::Vector{Float64}`: A vector containing the coefficients of the fine basis after + + - `::Vector{Float64}`: A vector containing the coefficients of the fine basis after subdivision. """ function get_child_basis_coefficients( @@ -110,13 +121,15 @@ Retrieve and return the local subdivision matrix for a given pair of coarse and elements within a two-scale operator. # Arguments -- `operator::AbstractTwoScaleOperator`: The two-scale operator that defines the + + - `operator::AbstractTwoScaleOperator`: The two-scale operator that defines the subdivision process. -- `coarse_element_id::Int`: The identifier of the coarse element. -- `fine_element_id::Int`: The identifier of the fine element. + - `coarse_element_id::Int`: The identifier of the coarse element. + - `fine_element_id::Int`: The identifier of the fine element. # Returns -- `::Matrix{Float64}`: The local subdivision matrix corresponding to the specified coarse + + - `::Matrix{Float64}`: The local subdivision matrix corresponding to the specified coarse and fine elements. """ function get_local_subdiv_matrix( diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/GeometryTwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/GeometryTwoScaleRelations.jl index d585e70ff..21de7b1c9 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/GeometryTwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/GeometryTwoScaleRelations.jl @@ -1,14 +1,15 @@ """ - subdivide_geometry(parent_geo::Geometry.AbstractGeometry, num_subdivisions) + subdivide_geometry(parent_geo::Geometry.AbstractGeometry, num_subdivisions) Returns a refined version of `parent_geo` where each element is subdivided according to `num_subdivisions`. # Returns -- `child_geometry`: The geometry corresponding to the subdivision of `parent_geo`. + + - `child_geometry`: The geometry corresponding to the subdivision of `parent_geo`. """ function subdivide_geometry(parent_geo::Geometry.AbstractGeometry, num_subdivisions) - throw(MethodError(subdivide_geometry, (parent_geo, num_subdivisions))) + return throw(MethodError(subdivide_geometry, (parent_geo, num_subdivisions))) end function subdivide_geometry( @@ -92,10 +93,13 @@ Subdivides `parent_breakpoints` by uniformly subdiving each element `num_subdivi times. # Arguments -- `parent_breakpoints::AbstractVector`: Parent set of breakpoints. -- `num_subdivisions`: Number of times each element is subdivided. + + - `parent_breakpoints::AbstractVector`: Parent set of breakpoints. + - `num_subdivisions`: Number of times each element is subdivided. + # Returns -- `child_breakpoints::Vector{Float64}`: Child set of breakpoints. + + - `child_breakpoints::Vector{Float64}`: Child set of breakpoints. """ function subdivide_breakpoints(parent_breakpoints::AbstractVector, num_subdivisions) num_parent_breakpoints = length(parent_breakpoints) diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/PolarSplineTwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/PolarSplineTwoScaleRelations.jl index f11ae10a2..4497503b2 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/PolarSplineTwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/PolarSplineTwoScaleRelations.jl @@ -41,8 +41,7 @@ function build_two_scale_operator( end function build_two_scale_operator( - parent_space::PolarSplineSpace, - num_subdivisions::NTuple{1, NTuple{2, Int}}, + parent_space::PolarSplineSpace, num_subdivisions::NTuple{1, NTuple{2, Int}} ) return build_two_scale_operator( parent_space, subdivide_space(parent_space, num_subdivisions), num_subdivisions @@ -50,23 +49,17 @@ function build_two_scale_operator( end function build_two_scale_operator( - parent_space::PolarSplineSpace, - num_subdivisions::NTuple{2, Int}, + parent_space::PolarSplineSpace, num_subdivisions::NTuple{2, Int} ) return build_two_scale_operator(parent_space, (num_subdivisions,)) end -function build_two_scale_operator( - parent_space::PolarSplineSpace, num_subdivisions::Int -) - return build_two_scale_operator( - parent_space, (num_subdivisions, num_subdivisions) - ) +function build_two_scale_operator(parent_space::PolarSplineSpace, num_subdivisions::Int) + return build_two_scale_operator(parent_space, (num_subdivisions, num_subdivisions)) end function subdivide_space( - parent_space::PolarSplineSpace, - num_subdivisions::NTuple{1, NTuple{2, Int}}, + parent_space::PolarSplineSpace, num_subdivisions::NTuple{1, NTuple{2, Int}} ) ############################################ @@ -78,8 +71,7 @@ function subdivide_space( size_degen_cp_parent = size(degen_cp_parent) # refine degenerate mapping TS_tp, degen_space_child = build_two_scale_operator( - get_degenerate_space(parent_space), - num_subdivisions[1] + get_degenerate_space(parent_space), num_subdivisions[1] ) size_tp_child = FunctionSpaces.get_num_basis.( FunctionSpaces.get_constituent_spaces(degen_space_child) @@ -95,8 +87,7 @@ function subdivide_space( ############################################ patch_spaces_child = subdivide_space.( - get_patch_spaces(parent_space), - Ref(num_subdivisions[1]), + get_patch_spaces(parent_space), Ref(num_subdivisions[1]) ) ############################################ @@ -111,10 +102,7 @@ function subdivide_space( ) end -function subdivide_space( - parent_space::PolarSplineSpace, - num_subdivisions::NTuple{2, Int}, -) +function subdivide_space(parent_space::PolarSplineSpace, num_subdivisions::NTuple{2, Int}) return subdivide_space(parent_space, (num_subdivisions,)) end diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TensorProductTwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TensorProductTwoScaleRelations.jl index f4533956f..274a1f12c 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TensorProductTwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TensorProductTwoScaleRelations.jl @@ -5,11 +5,12 @@ A structure representing a two-scale operator for tensor product spaces, definin relationships between coarse and fine tensor product spaces. # Fields -- `coarse_space::TP`: The coarse tensor product space. -- `fine_space::TP`: The fine tensor product space. -- `global_subdiv_matrix::SparseArrays.SparseMatrixCSC{Float64, Int}`: The global + + - `coarse_space::TP`: The coarse tensor product space. + - `fine_space::TP`: The fine tensor product space. + - `global_subdiv_matrix::SparseArrays.SparseMatrixCSC{Float64, Int}`: The global subdivision matrix for the tensor product space. -- `twoscale_operators::TS`: A tuple of two-scale operators for each constituent space. + - `twoscale_operators::TS`: A tuple of two-scale operators for each constituent space. """ struct TensorProductTwoScaleOperator{ manifold_dim, num_components, num_patches, num_spaces, PTP, CTP, TS, R @@ -134,12 +135,14 @@ Retrieve and return the child element IDs for a given element ID within a tensor two-scale operator. # Arguments -- `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator + + - `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator that defines the parent-child relationships between elements. -- `element_id::Int`: The identifier of the element whose children are to be retrieved. + - `element_id::Int`: The identifier of the element whose children are to be retrieved. # Returns -- `::Vector{Int}`: A vector containing the identifiers of the child elements. + + - `::Vector{Int}`: A vector containing the identifiers of the child elements. """ function get_element_children(operator::TensorProductTwoScaleOperator, element_id::Int) const_element_children = get_constituent_element_children(operator, element_id) @@ -160,12 +163,14 @@ Retrieve and return the parent element ID for a given element ID within a tensor two-scale operator. # Arguments -- `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator + + - `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator that defines the parent-child relationships between elements. -- `element_id::Int`: The identifier of the element whose parent is to be retrieved. + - `element_id::Int`: The identifier of the element whose parent is to be retrieved. # Returns -- `::Int`: The identifier of the parent element. + + - `::Int`: The identifier of the parent element. """ function get_element_parent(operator::TensorProductTwoScaleOperator, element_id::Int) const_element_parent = get_constituent_element_parent(operator, element_id) @@ -181,12 +186,14 @@ Retrieve and return the child basis function IDs for a given basis function ID w tensor product two-scale operator. # Arguments -- `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator + + - `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator that defines the parent-child relationships between basis functions. -- `basis_id::Int`: The identifier of the basis function whose children are to be retrieved. + - `basis_id::Int`: The identifier of the basis function whose children are to be retrieved. # Returns -- `::Vector{Int}`: A vector containing the identifiers of the child basis functions. + + - `::Vector{Int}`: A vector containing the identifiers of the child basis functions. """ function get_basis_children(operator::TensorProductTwoScaleOperator, basis_id::Int) const_basis_children = get_constituent_basis_children(operator, basis_id) @@ -207,12 +214,14 @@ Retrieve and return the parent basis functions for a given basis function ID wit tensor product two-scale operator. # Arguments -- `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator + + - `operator::TensorProductTwoScaleOperator`: The tensor product two-scale operator that defines the parent-child relationships between basis functions. -- `basis_id::Int`: The identifier of the basis function whose parent is to be retrieved. + - `basis_id::Int`: The identifier of the basis function whose parent is to be retrieved. # Returns -- `::Vector{Int}`: The identifier of the parent basis functions. + + - `::Vector{Int}`: The identifier of the parent basis functions. """ function get_basis_parents(operator::TensorProductTwoScaleOperator, basis_id::Int) const_basis_parents = get_constituent_basis_parent(operator, basis_id) @@ -246,13 +255,15 @@ Subdivide the finite element spaces within a tensor product space and return the finer tensor product space. # Arguments -- `space::TensorProductSpace`: The tensor product space containing finite + + - `space::TensorProductSpace`: The tensor product space containing finite element spaces to be subdivided. -- `nsubdivisions::NTuple{num_spaces, Int}`: A tuple specifying the number of subdivisions + - `nsubdivisions::NTuple{num_spaces, Int}`: A tuple specifying the number of subdivisions for each finite element space. # Returns -- `::TensorProductSpace`: The resulting finer tensor product space after subdivision. + + - `::TensorProductSpace`: The resulting finer tensor product space after subdivision. """ function subdivide_space( space::TensorProductSpace{manifold_dim, num_components, num_patches, num_spaces}, @@ -283,13 +294,15 @@ Build a two-scale operator for a tensor product space by subdividing each consti finite element space. # Arguments -- `space::TensorProductSpace`: The tensor product space to be subdivided. -- `nsubdivisions::NTuple{num_spaces, Int}`: A tuple specifying the number of subdivisions + + - `space::TensorProductSpace`: The tensor product space to be subdivided. + - `nsubdivisions::NTuple{num_spaces, Int}`: A tuple specifying the number of subdivisions for each constituent finite element space. # Returns -- `::TensorProductTwoScaleOperator`: The two-scale operator. -- `::TensorProductSpace`: The resulting finer tensor product space after subdivision. + + - `::TensorProductTwoScaleOperator`: The two-scale operator. + - `::TensorProductSpace`: The resulting finer tensor product space after subdivision. """ function build_two_scale_operator( parent_space::TensorProductSpace{manifold_dim, num_components, num_patches, num_spaces}, diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TwoScaleRelations.jl index 3881cdbf1..bf30868e7 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/TwoScaleRelations.jl @@ -5,19 +5,20 @@ A structure representing a two-scale operator that dechilds the relationships be and child finite element spaces. # Fields -- `parent_space::S`: The parent finite element space. -- `child_space::S`: The child finite element space. -- `global_subdiv_matrix::SparseArrays.SparseMatrixCSC{Float64, Int}`: The global subdivision + + - `parent_space::S`: The parent finite element space. + - `child_space::S`: The child finite element space. + - `global_subdiv_matrix::SparseArrays.SparseMatrixCSC{Float64, Int}`: The global subdivision matrix. The size of this matrix is `(num_child_basis, num_parent_basis)` where `num_child_basis` is the dimension of `child_space` and `num_parent_basis` the dimension of `parent_space`. -- `parent_to_child_elements::Vector{Vector{Int}}`: A vector of vectors containing the child + - `parent_to_child_elements::Vector{Vector{Int}}`: A vector of vectors containing the child element IDs for each parent element. -- `child_to_parent_elements::Vector{Int}`: A vector containing the parent element ID for + - `child_to_parent_elements::Vector{Int}`: A vector containing the parent element ID for each child element. -- `parent_to_child_basis::Vector{Vector{Int}}`: A vector of vectors containing the child + - `parent_to_child_basis::Vector{Vector{Int}}`: A vector of vectors containing the child basis function IDs for each parent basis function. -- `child_to_parent_basis::Vector{Vector{Int}}`: A vector of vectors containing the + - `child_to_parent_basis::Vector{Vector{Int}}`: A vector of vectors containing the parent basis function IDs for each child basis function. """ struct TwoScaleOperator{manifold_dim, num_components, num_patches, PS, CS, R} <: diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnivariateBSplineTwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnivariateBSplineTwoScaleRelations.jl index 3f0aeaf07..052322c34 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnivariateBSplineTwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnivariateBSplineTwoScaleRelations.jl @@ -12,12 +12,14 @@ The parent multiplicities are preserved in the `child_multiplicity_vector`, and inserted ones are given multiplicity `child_multiplicity`. # Arguments -- `parent_multiplicity::Vector{Int}`: parent multiplicity vector. -- `num_subdivisions::Int`: Number of times each element is subdivided. -- `child_multiplicity::Int`: Multiplicity of each new knot. + + - `parent_multiplicity::Vector{Int}`: parent multiplicity vector. + - `num_subdivisions::Int`: Number of times each element is subdivided. + - `child_multiplicity::Int`: Multiplicity of each new knot. # Returns -- `child_multiplicity_vector::Vector{Int}`: child multiplicity vector. + + - `child_multiplicity_vector::Vector{Int}`: child multiplicity vector. """ function subdivide_multiplicity_vector( parent_multiplicity::Vector{Int}, num_subdivisions::Int, child_multiplicity::Int=1 @@ -50,12 +52,14 @@ The parent multiplicities are preserved in the `child_multiplicity_vector`, and inserted ones are given multiplicity `child_multiplicity`. # Arguments -- `parent_knot_vector::KnotVector`: parent knot vector. -- `num_subdivisions::Int`: Number of times each element is subdivided. -- `child_multiplicity::Int`: Multiplicity of each new knot. + + - `parent_knot_vector::KnotVector`: parent knot vector. + - `num_subdivisions::Int`: Number of times each element is subdivided. + - `child_multiplicity::Int`: Multiplicity of each new knot. # Returns -- `::KnotVector`: child knot vector. + + - `::KnotVector`: child knot vector. """ function subdivide_knot_vector( parent_knot_vector::KnotVector, num_subdivisions::Int, child_multiplicity::Int @@ -79,12 +83,14 @@ The parent multiplicities are preserved in the final multiplicity vector, and ne ones are given multiplicity `child_multiplicity`. # Arguments -- `parent_bspline::BSplineSpace`: parent B-spline. -- `num_subdivisions::Int`: Number of times each element is subdivided. -- `child_multiplicity::Int`: Multiplicity of each new knot. + + - `parent_bspline::BSplineSpace`: parent B-spline. + - `num_subdivisions::Int`: Number of times each element is subdivided. + - `child_multiplicity::Int`: Multiplicity of each new knot. # Returns -- `::BSplineSpace`: refined B-spline space. + + - `::BSplineSpace`: refined B-spline space. """ function subdivide_space( parent_bspline::BSplineSpace, num_subdivisions::Int, child_multiplicity::Int=1 @@ -117,15 +123,16 @@ end ############################################################################################ # function build_two_scale_operator( - parent_bspline::BSplineSpace, num_subdivisions::Tuple{Int} + parent_bspline::BSplineSpace, num_subdivisions::Tuple{Int} ) - return build_two_scale_operator(parent_bspline, num_subdivisions[1]) + return build_two_scale_operator(parent_bspline, num_subdivisions[1]) end """ build_two_scale_operator( parent_bspline::BSplineSpace, num_subdivisions::Int, child_multiplicity::Int ) + Algorithm for the coefficients of a change of B-spline representation for knot insertion of multiple knots, recursively using `single_knot_insertion_oslo()`. The parent knot vector is `parent_bspline.knot_vector` and the inserted knots are given by `num_subdivisions`, @@ -135,12 +142,14 @@ with multiplicity 1. For more information, see [Dangella2018](@cite). # Arguments -- `parent_bspline::BSplineSpace`: parent B-spline. -- `num_subdivisions::Int`: Number of times each element is subdivided. -- `child_multiplicity::Int`: Multiplicity of each new knot in refined knot vector.\ + + - `parent_bspline::BSplineSpace`: parent B-spline. + - `num_subdivisions::Int`: Number of times each element is subdivided. + - `child_multiplicity::Int`: Multiplicity of each new knot in refined knot vector.\ # Returns -- `::FiniteElementSpaces.TwoScaleOperator, child_bspline::BSplineSpace`: Tuple with a + + - `::FiniteElementSpaces.TwoScaleOperator, child_bspline::BSplineSpace`: Tuple with a twoscale_operator and child B-spline space. """ function build_two_scale_operator( @@ -169,12 +178,14 @@ is `parent_bspline.knot_vector` and the inserted knots are given by For more information, see [Dangella2018](@cite). # Arguments -- `parent_bspline::BSplineSpace`: parent B-spline. -- `child_bspline::BSplineSpace`: child B-spline, with extra knots. -- `num_subdivisions::Int`: Number of times each element is subdivided. + + - `parent_bspline::BSplineSpace`: parent B-spline. + - `child_bspline::BSplineSpace`: child B-spline, with extra knots. + - `num_subdivisions::Int`: Number of times each element is subdivided. # Returns -- `::FiniteElementSpaces.TwoScaleOperator, child_bspline::BSplineSpace`: Tuple with a + + - `::FiniteElementSpaces.TwoScaleOperator, child_bspline::BSplineSpace`: Tuple with a twoscale_operator and child B-spline space. """ function build_two_scale_operator( @@ -228,11 +239,13 @@ is `parent_knot_vector` and the inserted knots are given by `child_knot_vector`. For more information, see [Dangella2018](@cite). # Arguments -- `parent_knot_vector::KnotVector`: parent knot vector. -- `child_knot_vector::KnotVector`: child knot vector, with the extra knots. + + - `parent_knot_vector::KnotVector`: parent knot vector. + - `child_knot_vector::KnotVector`: child knot vector, with the extra knots. # Returns -- `global_extraction_matrix`: Global subdivision matrix + + - `global_extraction_matrix`: Global subdivision matrix """ function build_two_scale_matrix( parent_knot_vector::KnotVector, child_knot_vector::KnotVector @@ -307,15 +320,16 @@ For more information, see [A note on the Oslo Algorithm](https://collections.lib.utah.edu/dl_files/66/d4/66d493df0f5c97cce67e0bc1294363d64dde7f06.pdf). # Arguments -- `parent_knot_vector::KnotVector`: parent knot vector. -- `child_knot_vector::KnotVector`: child knot vector, with the extra knot. -- `cf::Int`: Index of the parent knot vector. -- `rf::Int`: Index of the child knot vector such that - `get_knot_value(parent_knot_vector,cf) <= get_knot_value(child_knot_vector,rf) < - get_knot_value(parent_knot_vector,cf+1)`. + + - `parent_knot_vector::KnotVector`: parent knot vector. + - `child_knot_vector::KnotVector`: child knot vector, with the extra knot. + - `cf::Int`: Index of the parent knot vector. + - `rf::Int`: Index of the child knot vector such that + `get_knot_value(parent_knot_vector,cf) <= get_knot_value(child_knot_vector,rf) < get_knot_value(parent_knot_vector,cf+1)`. # Returns -- `b::Vector{Float64}`: Coefficients for the change of basis. + + - `b::Vector{Float64}`: Coefficients for the change of basis. """ function single_knot_insertion_oslo( parent_knot_vector::KnotVector, child_knot_vector::KnotVector, cf::Int, rf::Int diff --git a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelations.jl b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelations.jl index adf9146c3..af3a3f5e2 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelations.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelations.jl @@ -8,18 +8,21 @@ Build the two-scale operator for a general unstructured space. The fine space is assumed to be obtained from the coarse space by subdividing the `i`-th patch into `nsubdivisions[i]` sub-elements. -- It is assumed that the computation of subdivision matrices for the individual function + + - It is assumed that the computation of subdivision matrices for the individual function spaces that form the coarse unstructured space have already been implemented. -- The global subdivision matrix for the two scale operator is computed in a brute-force + - The global subdivision matrix for the two scale operator is computed in a brute-force manner by solving a least-squares problem. # Arguments -- `coarse_us_space::AbstractFESpace{manifold_dim,num_patches}`: The coarse unstructured space. -- `fine_us_space::AbstractFESpace{manifold_dim,num_patches}`: The fine unstructured space. -- `nsubdivisions::NTuple{num_patches, NTuple{manifold_dim,Int}}`: The number of subdivisions. + + - `coarse_us_space::AbstractFESpace{manifold_dim,num_patches}`: The coarse unstructured space. + - `fine_us_space::AbstractFESpace{manifold_dim,num_patches}`: The fine unstructured space. + - `nsubdivisions::NTuple{num_patches, NTuple{manifold_dim,Int}}`: The number of subdivisions. # Returns -- `two_scale_op::TwoScaleOperator`: The two-scale operator. + + - `two_scale_op::TwoScaleOperator`: The two-scale operator. """ function build_two_scale_operator( coarse_us_space::AbstractFESpace{manifold_dim, 1, num_patches}, diff --git a/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/BSplines.jl b/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/BSplines.jl index 1c8056c08..a33c18ac1 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/BSplines.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/BSplines.jl @@ -8,13 +8,14 @@ Note that while the section spaces on each element are the same, they don't nece to be polynomials; they are just named `polynomials` for convention. # Fields -- `geometry::G`: Physical geometry to which the B-Spline space is mapped. -- `knot_vector::KnotVector`: 1-dimensional knot vector defining the parametric geometry. See + + - `geometry::G`: Physical geometry to which the B-Spline space is mapped. + - `knot_vector::KnotVector`: 1-dimensional knot vector defining the parametric geometry. See [`KnotVector`](@ref) for more details. -- `extraction_op::ExtractionOperator`: Stores extraction coefficients and basis indices. -- `polynomials::F`: local section space F, named `polynomials` just for convention. Can be + - `extraction_op::ExtractionOperator`: Stores extraction coefficients and basis indices. + - `polynomials::F`: local section space F, named `polynomials` just for convention. Can be any [`AbstractCanonicalSpace`](@ref). -- `dof_partition::D`: Partitioning of the degrees of freedom into boundary and interior + - `dof_partition::D`: Partitioning of the degrees of freedom into boundary and interior dofs. The type will be similar to Vector{Vector{Vector{Int}}}, where the outer Vector will have length 1 (its for patches), the middle Vector will be of length 3 (1: left boundary, 2 interior, 3 right boundary), and the inner Vector will contain the global @@ -222,10 +223,12 @@ end Returns the knot vector object of the B-spline space `space`. # Arguments -- `space::BSplineSpace`: The B-spline space. + + - `space::BSplineSpace`: The B-spline space. # Returns -- `::KnotVector`: The knot vector of the B-spline space. + + - `::KnotVector`: The knot vector of the B-spline space. """ function get_knot_vector(space::BSplineSpace) return space.knot_vector @@ -237,10 +240,12 @@ end Returns the reference Bernstein polynomials of `space`. # Arguments -- `space::BSplineSpace`: A univariate B-Spline function space. + + - `space::BSplineSpace`: A univariate B-Spline function space. # Returns -- `::Bernstein`: Bernstein polynomials. + + - `::Bernstein`: Bernstein polynomials. """ function get_polynomials(space::BSplineSpace) return space.polynomials @@ -292,10 +297,12 @@ Returns the multiplicities of the knot vector associated with the univariate fun `space`. # Arguments -- `space::BSplineSpace`: The B-Spline function space. + + - `space::BSplineSpace`: The B-Spline function space. # Returns -- `::Vector{Int}`: The multiplicity of the knot vector associated with the B-Spline space. + + - `::Vector{Int}`: The multiplicity of the knot vector associated with the B-Spline space. """ function get_multiplicity_vector(space::BSplineSpace) return get_multiplicity(get_knot_vector(space)) @@ -307,11 +314,13 @@ end Returns the elements where the B-spline given by `basis_id` is supported. # Arguments -- `space::BSplineSpace`: The B-Spline function space. -- `basis_id::Int`: The id of the basis function. + + - `space::BSplineSpace`: The B-Spline function space. + - `basis_id::Int`: The id of the basis function. # Returns -- `::Vector{Int}`: The support of the basis function. + + - `::Vector{Int}`: The support of the basis function. """ function get_support(space::BSplineSpace, basis_id::Int) first_element = convert_knot_to_breakpoint_idx(get_knot_vector(space), basis_id) @@ -374,10 +383,12 @@ end Returns the derivative space of the B-spline space. # Arguments -- `space::BSplineSpace`: The B-spline space. + + - `space::BSplineSpace`: The B-spline space. # Returns -- `::BSplineSpace`: The derivative space. + + - `::BSplineSpace`: The derivative space. """ function get_derivative_space(space::BSplineSpace) # polynomial degree of derivative space diff --git a/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/KnotVector.jl b/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/KnotVector.jl index 286d4598c..8e5313b7a 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/KnotVector.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/KnotVector.jl @@ -4,9 +4,10 @@ 1-dimensional knot vector. # Fields -- `geometry::G`: A 1D geometry. -- `polynomial_degree::Int`: Polynomial degree. -- `multiplicity::TM`: Number of repetitions of each knot. + + - `geometry::G`: A 1D geometry. + - `polynomial_degree::Int`: Polynomial degree. + - `multiplicity::TM`: Number of repetitions of each knot. """ struct KnotVector{G, TM} geometry::G @@ -82,15 +83,17 @@ Creates a uniform knot vector corresponding to B-splines basis functions of poly degree `p` and continuity `regularity[i]` on `breakpoints[i]`. # Arguments -- `breakpoints::AbstractVector{NT}`: Location of each breakpoint. -- `p::Int`: Degree of the polynomial (``p ≥ 0``). -- `breakpoint_condition::AbstractVector{Int}`: Either the regularity or multiplicity of each + + - `breakpoints::AbstractVector{NT}`: Location of each breakpoint. + - `p::Int`: Degree of the polynomial (``p ≥ 0``). + - `breakpoint_condition::AbstractVector{Int}`: Either the regularity or multiplicity of each breakpoint. -- `condition_type::String`: Determines whether `breakpoint_condition` provides the + - `condition_type::String`: Determines whether `breakpoint_condition` provides the regularity or multiplicity. # Returns -- `::KnotVector`: Knot vector. + + - `::KnotVector`: Knot vector. """ function create_knot_vector( geometry::Geometry.AbstractGeometry{1}, @@ -131,10 +134,12 @@ end Returns the multiplicity of each knot in `knot_vector`. # Arguments -- `knot_vector::KnotVector`. + + - `knot_vector::KnotVector`. # Returns -- `<:AbstractVector{Int}`: Multiplicity of each knot. + + - `<:AbstractVector{Int}`: Multiplicity of each knot. """ function get_multiplicity(knot_vector::KnotVector) return knot_vector.multiplicity @@ -156,10 +161,12 @@ end Determines the length of `knot_vector` by summing the multiplicities of each knot vector. # Arguments -- `knot_vector::KnotVector`: Knot vector for length calculation. + + - `knot_vector::KnotVector`: Knot vector for length calculation. # Returns -- `::Int`: Length of the knot vector. + + - `::Int`: Length of the knot vector. """ function get_knot_vector_length(knot_vector::KnotVector) return sum(knot_vector.multiplicity) @@ -172,11 +179,13 @@ Retrieves the breakpoint index corresponding to `knot_vector` at `knot_index`, i index of the vector where every `breakpoint[i]` appears `knot_vector.multiplicity[i]`-times. # Arguments -- `knot_vector::KnotVector`: Knot vector for length calculation. -- `knot_index::Int`: Index in the knot vector. + + - `knot_vector::KnotVector`: Knot vector for length calculation. + - `knot_index::Int`: Index in the knot vector. # Returns -- `::Int`: Index of breakpoint corresponding to `knot_index`. + + - `::Int`: Index of breakpoint corresponding to `knot_index`. """ function convert_knot_to_breakpoint_idx(knot_vector::KnotVector, knot_index::Int) return findfirst(idx -> idx >= knot_index, cumsum(knot_vector.multiplicity)) @@ -188,11 +197,13 @@ end Get the index of the first knot corresponding to a given breakpoint. # Arguments -- `knot_vector::KnotVector`: The knot vector. -- `breakpoint_index::Int`: Index of the breakpoint. + + - `knot_vector::KnotVector`: The knot vector. + - `breakpoint_index::Int`: Index of the breakpoint. # Returns -- `::Int`: Index of the first knot for the given breakpoint. + + - `::Int`: Index of the first knot for the given breakpoint. """ function get_first_knot_index(knot_vector::KnotVector, breakpoint_index::Int) return cumsum(knot_vector.multiplicity)[breakpoint_index] - @@ -205,11 +216,13 @@ end Get the index of the last knot corresponding to a given breakpoint. # Arguments -- `knot_vector::KnotVector`: The knot vector. -- `breakpoint_index::Int`: Index of the breakpoint. + + - `knot_vector::KnotVector`: The knot vector. + - `breakpoint_index::Int`: Index of the breakpoint. # Returns -- `::Int`: Index of the last knot for the given breakpoint. + + - `::Int`: Index of the last knot for the given breakpoint. """ function get_last_knot_index(knot_vector::KnotVector, breakpoint_index::Int) return cumsum(knot_vector.multiplicity)[breakpoint_index] @@ -222,11 +235,13 @@ Retrieves the breakpoint corresponding to `knot_vector` at `knot_index`, i.e., t of the vector where every `breakpoint[i]` appears `knot_vector.multiplicity[i]`-times. # Arguments -- `knot_vector::KnotVector`: Knot vector for length calculation. -- `knot_index::Int`: Index in the knot vector. + + - `knot_vector::KnotVector`: Knot vector for length calculation. + - `knot_index::Int`: Index in the knot vector. # Returns -- `::Number`: Breakpoint corresponding to `knot_index`. + + - `::Number`: Breakpoint corresponding to `knot_index`. """ function get_knot_value(knot_vector::KnotVector, knot_index::Int) idx = convert_knot_to_breakpoint_idx(knot_vector, knot_index) @@ -241,11 +256,13 @@ Retrieves the multiplicity of the breakpoint corresponding to `knot_vector` at `knot_vector.multiplicity[i]`-times. # Arguments -- `knot_vector::KnotVector`: Knot vector for length calculation. -- `knot_index::Int`: Index in the knot vector. + + - `knot_vector::KnotVector`: Knot vector for length calculation. + - `knot_index::Int`: Index in the knot vector. # Returns -- `::Int`: Multiplicity of the breakpoint corresponding to `knot_index`. + + - `::Int`: Multiplicity of the breakpoint corresponding to `knot_index`. """ function get_knot_multiplicity(knot_vector::KnotVector, knot_index::Int) index = convert_knot_to_breakpoint_idx(knot_vector, knot_index) @@ -259,11 +276,13 @@ Returns the local knot vector necessary to characterize the B-spline identified `basis_id`. # Arguments -- `knot_vector::KnotVector`: The knot vector of the full B-spline basis. -- `basis_id::Int`: The id of the B-spline. + + - `knot_vector::KnotVector`: The knot vector of the full B-spline basis. + - `basis_id::Int`: The id of the B-spline. # Returns -- `::KnotVector`: The knot vector of the B-spline identified by `basis_id`. + + - `::KnotVector`: The knot vector of the B-spline identified by `basis_id`. """ function get_local_knot_vector(knot_vector::KnotVector, basis_id::Int) deg = get_polynomial_degree(knot_vector) @@ -290,10 +309,12 @@ end Compute the Greville points for the given knot vector. # Arguments -- `knot_vector::KnotVector`: The knot vector. + + - `knot_vector::KnotVector`: The knot vector. # Returns -- `::Tuple{Vector{Float64}}`: Vector of Greville points. + + - `::Tuple{Vector{Float64}}`: Vector of Greville points. """ function get_greville_points(knot_vector::KnotVector) p = get_polynomial_degree(knot_vector) diff --git a/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/UnivariateSplineExtractions.jl b/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/UnivariateSplineExtractions.jl index 0ca9c9368..40ccdc3ca 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/UnivariateSplineExtractions.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/UnivariateSplines/UnivariateSplineExtractions.jl @@ -8,14 +8,17 @@ Compute the extraction coefficients of B-Spline basis functions in terms of cano functions; see [Borden2011](@cite) [Toshniwal2020](@cite) [Hiemstra2020](@cite). # Arguments -- `knot_vector::KnotVector`: The knot vector defining the B-Spline basis. -- `polynomials::AbstractCanonicalSpace`: The canonical space to extract to. + + - `knot_vector::KnotVector`: The knot vector defining the B-Spline basis. + - `polynomials::AbstractCanonicalSpace`: The canonical space to extract to. # Returns -- `::ExtractionOperator{Indices{1, TE, TI, TJ}}`: See [`ExtractionOperator`](@ref) + + - `::ExtractionOperator{Indices{1, TE, TI, TJ}}`: See [`ExtractionOperator`](@ref) for the details. # Note + The extraction coefficients `E[el]` for element `el` contain the coefficients of the linear combination of reference Canonical polynomials determining the basis functions on that element. @@ -129,7 +132,7 @@ function extract_bspline_to_section_space( # Count forwrd multiplicity of knot at k_id_end t = k_id_end while k_id_end < num_knots && - convert_knot_to_breakpoint_idx(knot_vector, k_id_end + 1) == b_id_end + convert_knot_to_breakpoint_idx(knot_vector, k_id_end + 1) == b_id_end k_id_end += 1 end mult = k_id_end - t + 1 @@ -261,8 +264,9 @@ function extract_bspline_to_section_space( for el in 1:nel cols_el = (canonical_dims[el] + 1):canonical_dims[el + 1] # Matrix of coefficients - extraction_coefficients[el] = - Matrix(H[get_basis_indices(basis_indices[el]), cols_el])' + extraction_coefficients[el] = Matrix( + H[get_basis_indices(basis_indices[el]), cols_el] + )' end Etup = [(extraction_coefficients[el],) for el in eachindex(extraction_coefficients)] @@ -279,11 +283,13 @@ Compute the extraction coefficients of GTB-Spline basis functions in terms of (r B-spline basis functions. # Arguments -- `spline_spaces::NTuple{m,F}`: Collection of (rational) B-spline spaces. -- `regularity::Vector{Int}`: Smoothness to be imposed at patch interfaces. + + - `spline_spaces::NTuple{m,F}`: Collection of (rational) B-spline spaces. + - `regularity::Vector{Int}`: Smoothness to be imposed at patch interfaces. # Returns -- `ExtractionOperator{Indices{1, TE, TI, TJ}}`: The extraction operator containing the + + - `ExtractionOperator{Indices{1, TE, TI, TJ}}`: The extraction operator containing the coefficients. See [`ExtractionOperator`](@ref) for the details. """ function extract_gtbspline_to_bspline( @@ -422,14 +428,16 @@ end Build the sparsest possible nullspace of a constraint vector with no zero entries. # Arguments -- `constraint::SparseArrays.SparseVector{Float64}`: The constraint vector. + + - `constraint::SparseArrays.SparseVector{Float64}`: The constraint vector. # Returns -- `::SparseMatrixCSC{Float64}`: The sparse nullspace matrix. + + - `::SparseMatrixCSC{Float64}`: The sparse nullspace matrix. """ function build_sparse_nullspace(constraint::SparseArrays.SparseVector{Float64}) q = length(constraint) - nz_flag = .!isapprox.(constraint, 0.0, atol=1e-14) + nz_flag = .!isapprox.(constraint, 0.0; atol=1e-14) i1 = findfirst(nz_flag) i2 = findlast(nz_flag) dd = zeros(Float64, q - 1, 2) diff --git a/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/GTBSplines.jl b/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/GTBSplines.jl index b4fa02f18..f5ddd97d6 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/GTBSplines.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/GTBSplines.jl @@ -6,27 +6,30 @@ regularity conditions. Periodic spaces are a special case of GTBSplines for `num_patches = 1` and `regularity = [r]` with `r > -1`; see [Hiemstra2020](@cite). # Fields -- `patch_spaces::NTuple{num_patches, T}`: A tuple of `num_patches` NURBS or B-spline spaces. -- `extraction_op::ExtractionOperator{1, TE, TI, TJ}`: The extraction operator for the + + - `patch_spaces::NTuple{num_patches, T}`: A tuple of `num_patches` NURBS or B-spline spaces. + - `extraction_op::ExtractionOperator{1, TE, TI, TJ}`: The extraction operator for the GTBSpline space. -- `dof_partition::Vector{Vector{Vector{Int}}}`: A vector of vectors of vectors of integers + - `dof_partition::Vector{Vector{Vector{Int}}}`: A vector of vectors of vectors of integers representing the degree of freedom partitioning. -- `regularity::Vector{Int}`: A vector of regularity conditions at the interfaces between + - `regularity::Vector{Int}`: A vector of regularity conditions at the interfaces between the spaces. # Arguments for constructor -- `patch_spaces::NTuple{num_patches, T}`: A tuple of `num_patches` NURBS or B-spline spaces. -- `regularity::Vector{Int}`: A vector of regularity conditions at the interfaces between + + - `patch_spaces::NTuple{num_patches, T}`: A tuple of `num_patches` NURBS or B-spline spaces. + - `regularity::Vector{Int}`: A vector of regularity conditions at the interfaces between the spaces. -- `num_dofs_left::Int`: The number of degrees of freedom at the left boundary. Default is + - `num_dofs_left::Int`: The number of degrees of freedom at the left boundary. Default is -1, which means it will be computed automatically. -- `num_dofs_right::Int`: The number of degrees of freedom at the right boundary. Default is + - `num_dofs_right::Int`: The number of degrees of freedom at the right boundary. Default is -1, which means it will be computed automatically. # Throws -- `ArgumentError`: If the number of regularity conditions does not match the number of + + - `ArgumentError`: If the number of regularity conditions does not match the number of interfaces. -- `ArgumentError`: If the minimal polynomial degree of any pair of adjacent spaces is less + - `ArgumentError`: If the minimal polynomial degree of any pair of adjacent spaces is less than the corresponding regularity condition. """ struct GTBSplineSpace{num_patches, T, G, GP, TE, TI, TJ} <: diff --git a/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/PolarSplines.jl b/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/PolarSplines.jl index 8e61890df..62e79c684 100644 --- a/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/PolarSplines.jl +++ b/src/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/PolarSplines.jl @@ -12,12 +12,14 @@ Build the extraction operator and dof partitioning for the polar spline space. # Arguments -- `tp_space::TensorProductSpace`: The tensor product space. -- `E::SparseMatrixCSC{Float64,Int}`: The extraction operator. -- `two_poles::Bool`: Whether the polar spline space has two poles. + + - `tp_space::TensorProductSpace`: The tensor product space. + - `E::SparseMatrixCSC{Float64,Int}`: The extraction operator. + - `two_poles::Bool`: Whether the polar spline space has two poles. # Returns -- `::Tuple{ExtractionOperator, Vector{Vector{Vector{Int}}}}`: The extraction operator and dof partitioning. + + - `::Tuple{ExtractionOperator, Vector{Vector{Vector{Int}}}}`: The extraction operator and dof partitioning. """ function _build_polar_extraction_and_dof_partition( tp_space::NTuple{num_components, TP}, E::NTuple{num_components, TE}, two_poles::Bool @@ -115,11 +117,13 @@ end Build a standard triangle that contains the input points. # Arguments -- `points::Matrix{Float64}`: The input points, where each row corresponds to a point. -- `origin::Vector{Float64}`: The point which is translated to the origin. + + - `points::Matrix{Float64}`: The input points, where each row corresponds to a point. + - `origin::Vector{Float64}`: The point which is translated to the origin. # Returns -- `::Matrix{Float64}`: The circumscribing triangle. + + - `::Matrix{Float64}`: The circumscribing triangle. """ function _get_circumscribing_triangle( points::Matrix{Float64}, origin::Vector{Float64}=[0.0, 0.0] @@ -138,13 +142,15 @@ Compute the barycentric coordinates of the input points w.r.t. the control trian points are translated by the offset before computing the barycentric coordinates. # Arguments -- `points::Matrix{Float64}`: The input points, where each row corresponds to a point. -- `tri::Matrix{Float64}`: Matrix with the coordinates of the `i`-th vertex of the control + + - `points::Matrix{Float64}`: The input points, where each row corresponds to a point. + - `tri::Matrix{Float64}`: Matrix with the coordinates of the `i`-th vertex of the control triangle in the `i`-th row. -- `origin::Vector{Float64}`: The point which is translated to the origin. + - `origin::Vector{Float64}`: The point which is translated to the origin. # Returns -- `::Matrix{Float64}`: The barycentric coordinates. + + - `::Matrix{Float64}`: The barycentric coordinates. """ function _get_barycentric_coordinates( points::Matrix{Float64}, tri::Matrix{Float64}, origin::Vector{Float64}=[0.0, 0.0] @@ -188,15 +194,17 @@ struct PolarSplineSpace{num_components, T, TD, TE, TI, TJ, G, GP} <: define the polar parametric domain); see [Toshniwal2017](@cite). # Arguments - - `patch_spaces::NTuple{1, TensorProductSpace{2, 1}}`: The tensor-product space defining + + - `patch_spaces::NTuple{1, TensorProductSpace{2, 1}}`: The tensor-product space defining the polar spline space. - - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. - - `degenerate_space::TensorProductSpace{2, 1}`: The degenerate tensor-product space. - - `two_poles::Bool=false`: Whether the polar spline space has two poles. - - `zero_at_poles::Bool=false`: Whether functions are constrained to zero at poles. + - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. + - `degenerate_space::TensorProductSpace{2, 1}`: The degenerate tensor-product space. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. + - `zero_at_poles::Bool=false`: Whether functions are constrained to zero at poles. # Returns - - `::PolarSplineSpace{1, typeof(patch_spaces), typeof(degenerate_space), TE..., TI..., TJ...}`: + + - `::PolarSplineSpace{1, typeof(patch_spaces), typeof(degenerate_space), TE..., TI..., TJ...}`: The scalar polar spline space. """ function PolarSplineSpace( @@ -314,15 +322,17 @@ struct PolarSplineSpace{num_components, T, TD, TE, TI, TJ, G, GP} <: define the polar parametric domain); see [Toshniwal2021](@cite). # Arguments - - `patch_spaces::NTuple{2, TensorProductSpace{2, 1}}`: The tensor-product spaces defining + + - `patch_spaces::NTuple{2, TensorProductSpace{2, 1}}`: The tensor-product spaces defining the polar spline space. - - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. - - `degenerate_space::TensorProductSpace{2, 1}`: The degenerate tensor-product space. - - `two_poles::Bool=false`: Whether the polar spline space has two poles. - - `::Bool=false`: Dummy argument to have a uniform constructor as the scalar case. + - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. + - `degenerate_space::TensorProductSpace{2, 1}`: The degenerate tensor-product space. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. + - `::Bool=false`: Dummy argument to have a uniform constructor as the scalar case. # Returns - - `::PolarSplineSpace{2, typeof(patch_spaces), typeof(degenerate_space), TE..., TI..., TJ...}`: + + - `::PolarSplineSpace{2, typeof(patch_spaces), typeof(degenerate_space), TE..., TI..., TJ...}`: The vector polar spline space. """ function PolarSplineSpace( @@ -490,14 +500,16 @@ Build the extraction operator to extract the polar spline basis functions from t product space. # Arguments -- `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. -- `num_basis_r::Int`: Number of radial basis functions. -- `two_poles::Bool=false`: Whether the polar spline space has two poles. -- `zero_at_poles::Bool=nothing`: Whether functions are constrained to zero at poles. + + - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. + - `num_basis_r::Int`: Number of radial basis functions. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. + - `zero_at_poles::Bool=nothing`: Whether functions are constrained to zero at poles. # Returns -- `E::SparseMatrixCSC{Float64,Int}`: The extraction operator. -- `tri::Matrix{Float64}`: The control triangle. + + - `E::SparseMatrixCSC{Float64,Int}`: The extraction operator. + - `tri::Matrix{Float64}`: The control triangle. """ function extract_scalar_polar_splines_to_tensorproduct( degenerate_control_points::Array{Float64, 3}, @@ -515,7 +527,7 @@ function extract_scalar_polar_splines_to_tensorproduct( if ~all( isapprox.( degenerate_control_points[:, 1, :], - degenerate_control_points[1:1, 1, :], + degenerate_control_points[1:1, 1, :]; atol=1e-12, ), ) @@ -591,14 +603,16 @@ build the scalar polar spline extraction operator, as well as the vector polar s extraction operator. # Arguments -- `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. -- `tri::Matrix{Float64}`: The control triangle. -- `origin::Vector{Float64}=[0.0, 0.0]`: The origin. -- `two_poles::Bool=false`: Whether the polar spline space has two poles. + + - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. + - `tri::Matrix{Float64}`: The control triangle. + - `origin::Vector{Float64}=[0.0, 0.0]`: The origin. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. # Returns -- `E0_1::SparseMatrixCSC{Float64,Int}`: The extraction sub-matrix for the first pole. -- `E0_2::SparseMatrixCSC{Float64,Int}`: The extraction sub-matrix for the second pole. + + - `E0_1::SparseMatrixCSC{Float64,Int}`: The extraction sub-matrix for the first pole. + - `E0_2::SparseMatrixCSC{Float64,Int}`: The extraction sub-matrix for the second pole. """ function _get_scalar_polar_extraction_submatrix( degenerate_control_points::Array{Float64, 3}, @@ -636,14 +650,16 @@ Build the extraction operator to extract the vector polar spline basis functions the tensor product basis functions. # Arguments -- `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. -- `num_basis_r::Int`: Number of radial basis functions. -- `two_poles::Bool=false`: Whether the polar spline space has two poles. + + - `degenerate_control_points::Array{Float64, 3}`: The degenerate control points. + - `num_basis_r::Int`: Number of radial basis functions. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. # Returns -- `E::Tuple{SparseMatrixCSC{Float64,Int},SparseMatrixCSC{Float64,Int}}`: The extraction operators. -The first element corresponds to the horizontal edges and the second element to the vertical edges. -- `tri::Matrix{Float64}`: The control triangle. + + - `E::Tuple{SparseMatrixCSC{Float64,Int},SparseMatrixCSC{Float64,Int}}`: The extraction operators. + The first element corresponds to the horizontal edges and the second element to the vertical edges. + - `tri::Matrix{Float64}`: The control triangle. """ function extract_vector_polar_splines_to_tensorproduct( degenerate_control_points::Array{Float64, 3}, num_basis_r::Int, two_poles::Bool=false @@ -658,7 +674,7 @@ function extract_vector_polar_splines_to_tensorproduct( if ~all( isapprox.( degenerate_control_points[:, 1, :], - degenerate_control_points[1:1, 1, :], + degenerate_control_points[1:1, 1, :]; atol=1e-12, ), ) diff --git a/src/FunctionSpaces/FunctionSpaceHelpers.jl b/src/FunctionSpaces/FunctionSpaceHelpers.jl index e52867837..9cad6393d 100644 --- a/src/FunctionSpaces/FunctionSpaceHelpers.jl +++ b/src/FunctionSpaces/FunctionSpaceHelpers.jl @@ -21,19 +21,22 @@ open knot vector, and returns the corresponding `BSplineSpace`. Optional argumen the number of degrees of freedom on the left and right boundaries. # Arguments -- `starting_point::Float64`: The coordinate of the starting point of the B-spline space. -- `box_size::Float64`: The size of the space along the single dimension. -- `num_elements::Int`: The number of elements along the dimension. -- `section_space::F`: The section space to use for the B-spline basis functions. -- `regularity::Int`: The regularity (smoothness) between consecutive B-spline basis + + - `starting_point::Float64`: The coordinate of the starting point of the B-spline space. + - `box_size::Float64`: The size of the space along the single dimension. + - `num_elements::Int`: The number of elements along the dimension. + - `section_space::F`: The section space to use for the B-spline basis functions. + - `regularity::Int`: The regularity (smoothness) between consecutive B-spline basis functions, typically between 0 and `degree - 1`. -- `n_dofs_left::Int`: The number of degrees of freedom on the left boundary. -- `n_dofs_right::Int`: The number of degrees of freedom on the right boundary. + - `n_dofs_left::Int`: The number of degrees of freedom on the left boundary. + - `n_dofs_right::Int`: The number of degrees of freedom on the right boundary. # Returns -- `::BSplineSpace`: The generated B-spline space. + + - `::BSplineSpace`: The generated B-spline space. # Examples + ```julia # Create a 1D B-spline space start_1d = 0.0 @@ -42,7 +45,9 @@ elements_1d = 100 degree = 3 regularity = 2 section_space = Bernstein(degree) -bspline_space_1d = create_bspline_space(start_1d, size_1d, elements_1d, section_space, regularity) +bspline_space_1d = create_bspline_space( + start_1d, size_1d, elements_1d, section_space, regularity +) ``` """ function create_bspline_space( @@ -89,17 +94,19 @@ ensure an open knot vector, and returns the corresponding `BSplineSpace`. Option arguments include the number of degrees of freedom on the left and right boundaries. # Arguments -- `starting_point::Float64`: The coordinate of the starting point of the B-spline space. -- `box_size::Float64`: The size of the space along the single dimension. -- `num_elements::Int`: The number of elements along the dimension. -- `degree::Int`: The polynomial degree on each element. -- `regularity::Int`: The regularity (smoothness) between consecutive B-spline basis + + - `starting_point::Float64`: The coordinate of the starting point of the B-spline space. + - `box_size::Float64`: The size of the space along the single dimension. + - `num_elements::Int`: The number of elements along the dimension. + - `degree::Int`: The polynomial degree on each element. + - `regularity::Int`: The regularity (smoothness) between consecutive B-spline basis functions, typically between 0 and `degree - 1`. -- `n_dofs_left::Int`: The number of degrees of freedom on the left boundary. -- `n_dofs_right::Int`: The number of degrees of freedom on the right boundary. + - `n_dofs_left::Int`: The number of degrees of freedom on the left boundary. + - `n_dofs_right::Int`: The number of degrees of freedom on the right boundary. # Returns -- `::BSplineSpace`: The generated B-spline space. + + - `::BSplineSpace`: The generated B-spline space. """ function create_bspline_space( starting_point::Float64, @@ -140,20 +147,23 @@ open knot vector, and returns the corresponding `BSplineSpace`. Optional argumen the number of degrees of freedom on the left and right boundaries. # Arguments -- `starting_point::NTuple{1, Float64}`: The coordinate of the starting point of the + + - `starting_point::NTuple{1, Float64}`: The coordinate of the starting point of the B-spline space. -- `box_size::NTuple{1, Float64}`: The size of the space along the single dimension. -- `num_elements::NTuple{1, Int}`: The number of elements along the dimension. -- `section_space::NTuple{1,F}`: The section space to use for the B-spline basis functions. -- `regularity::NTuple{1, Int}`: The regularity (smoothness) between consecutive B-spline + - `box_size::NTuple{1, Float64}`: The size of the space along the single dimension. + - `num_elements::NTuple{1, Int}`: The number of elements along the dimension. + - `section_space::NTuple{1,F}`: The section space to use for the B-spline basis functions. + - `regularity::NTuple{1, Int}`: The regularity (smoothness) between consecutive B-spline basis functions, typically between 0 and `degree - 1`. -- `n_dofs_left::NTuple{1,Int}`: The number of degrees of freedom on the left boundary. -- `n_dofs_right::NTuple{1,Int}`: The number of degrees of freedom on the right boundary. + - `n_dofs_left::NTuple{1,Int}`: The number of degrees of freedom on the left boundary. + - `n_dofs_right::NTuple{1,Int}`: The number of degrees of freedom on the right boundary. # Returns -- `::BSplineSpace`: The generated B-spline space. + + - `::BSplineSpace`: The generated B-spline space. # Examples + ```julia # Create a 1D B-spline space start_1d = 0.0 @@ -162,7 +172,9 @@ elements_1d = 100 degree = 3 regularity = 2 section_space = Bernstein(degree) -bspline_space_1d = create_bspline_space((start_1d,), (size_1d,), (elements_1d,), (section_space,), (regularity,)) +bspline_space_1d = create_bspline_space( + (start_1d,), (size_1d,), (elements_1d,), (section_space,), (regularity,) +) ``` """ function create_bspline_space( @@ -204,17 +216,19 @@ ensure an open knot vector, and returns the corresponding `BSplineSpace`. Option arguments include the number of degrees of freedom on the left and right boundaries. # Arguments -- `starting_point::Float64`: The coordinate of the starting point of the B-spline space. -- `box_size::Float64`: The size of the space along the single dimension. -- `num_elements::Int`: The number of elements along the dimension. -- `degree::Int`: The polynomial degree on each element. -- `regularity::Int`: The regularity (smoothness) between consecutive B-spline basis + + - `starting_point::Float64`: The coordinate of the starting point of the B-spline space. + - `box_size::Float64`: The size of the space along the single dimension. + - `num_elements::Int`: The number of elements along the dimension. + - `degree::Int`: The polynomial degree on each element. + - `regularity::Int`: The regularity (smoothness) between consecutive B-spline basis functions, typically between 0 and `degree - 1`. -- `n_dofs_left::Int`: The number of degrees of freedom on the left boundary. -- `n_dofs_right::Int`: The number of degrees of freedom on the right boundary. + - `n_dofs_left::Int`: The number of degrees of freedom on the left boundary. + - `n_dofs_right::Int`: The number of degrees of freedom on the right boundary. # Returns -- `::BSplineSpace`: The generated B-spline space. + + - `::BSplineSpace`: The generated B-spline space. """ function create_bspline_space( starting_point::NTuple{1, Float64}, @@ -250,17 +264,19 @@ end Create a tensor product B-spline space based on the specified parameters for each dimension. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline + + - `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline space in each dimension. -- `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. -- `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. -- `section_spaces::NTuple{manifold_dim, AbstractCanonicalSpace}`: The section spaces for + - `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. + - `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. + - `section_spaces::NTuple{manifold_dim, AbstractCanonicalSpace}`: The section spaces for each dimension. -- `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each + - `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each dimension. # Returns -- `::TensorProductSpace`: The resulting tensor product B-spline space. + + - `::TensorProductSpace`: The resulting tensor product B-spline space. """ function create_bspline_space( starting_points::NTuple{manifold_dim, Float64}, @@ -299,16 +315,18 @@ Create `manifold_dim` univariate B-spline spaces based on the specified paramete dimension. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline + + - `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline space in each dimension. -- `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. -- `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. -- `section_spaces::NTuple{manifold_dim, F}`: The section spaces for each dimension. -- `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each + - `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. + - `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. + - `section_spaces::NTuple{manifold_dim, F}`: The section spaces for each dimension. + - `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each dimension. # Returns -- `::Tuple{BSplineSpace}`: The resulting univariate B-spline spaces. + + - `::Tuple{BSplineSpace}`: The resulting univariate B-spline spaces. """ function create_dim_wise_bspline_spaces( starting_points::NTuple{manifold_dim, Float64}, @@ -320,7 +338,7 @@ function create_dim_wise_bspline_spaces( n_dofs_right::NTuple{manifold_dim, Int}=ntuple(i -> 1, manifold_dim), ) where {manifold_dim, F <: NTuple{manifold_dim, AbstractCanonicalSpace}} return ntuple(manifold_dim) do i - create_bspline_space( + return create_bspline_space( starting_points[i], box_sizes[i], num_elements[i], @@ -346,16 +364,18 @@ end Create a tensor product B-spline space based on the specified parameters for each dimension. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline + + - `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline space in each dimension. -- `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. -- `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. -- `degrees::NTuple{manifold_dim, Int}`: The polynomial degree in each dimension. -- `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each + - `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. + - `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. + - `degrees::NTuple{manifold_dim, Int}`: The polynomial degree in each dimension. + - `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each dimension. # Returns -- `::TensorProductSpace`: The resulting tensor product B-spline space. + + - `::TensorProductSpace`: The resulting tensor product B-spline space. """ function create_bspline_space( starting_points::NTuple{manifold_dim, Float64}, @@ -402,16 +422,18 @@ Create `manifold_dim` univariate B-spline spaces based on the specified paramete each dimension. # Arguments -- `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline + + - `starting_points::NTuple{manifold_dim, Float64}`: The starting point of the B-spline space in each dimension. -- `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. -- `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. -- `degrees::NTuple{manifold_dim, Int}`: The polynomial degree in each dimension. -- `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each + - `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box in each dimension. + - `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. + - `degrees::NTuple{manifold_dim, Int}`: The polynomial degree in each dimension. + - `regularities::NTuple{manifold_dim, Int}`: The regularities of the B-spline in each dimension. # Returns -- `::Tuple{BSplineSpace}`: The resulting univariate B-spline spaces. + + - `::Tuple{BSplineSpace}`: The resulting univariate B-spline spaces. """ function create_dim_wise_bspline_spaces( starting_points::NTuple{manifold_dim, Float64}, @@ -423,7 +445,7 @@ function create_dim_wise_bspline_spaces( n_dofs_right::NTuple{manifold_dim, Int}, ) where {manifold_dim} return ntuple(manifold_dim) do i - create_bspline_space( + return create_bspline_space( starting_points[i], box_sizes[i], num_elements[i], @@ -457,16 +479,18 @@ directions. Optional arguments include a flag to refine the geometry, the geomet coefficients, and whether the scalars are constrained to zero at poles. # Arguments -- `num_elements::NTuple{2, Int}`: The number of elements in the angular and radial directions. -- `degrees::NTuple{2, Int}`: The polynomial degrees in the angular and radial directions. -- `regularities::NTuple{2, Int}`: The regularities of the B-spline in the angular and radial directions. -- `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}=nothing`: The geometry coefficients for the polar spline space. -- `R::Float64=1.0`: The radius of the polar spline space. -- `two_poles::Bool=false`: Whether the polar spline space has two poles. -- `zero_at_poles::Bool=false`: Whether the scalars are constrained to zero at the poles. + + - `num_elements::NTuple{2, Int}`: The number of elements in the angular and radial directions. + - `degrees::NTuple{2, Int}`: The polynomial degrees in the angular and radial directions. + - `regularities::NTuple{2, Int}`: The regularities of the B-spline in the angular and radial directions. + - `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}=nothing`: The geometry coefficients for the polar spline space. + - `R::Float64=1.0`: The radius of the polar spline space. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. + - `zero_at_poles::Bool=false`: Whether the scalars are constrained to zero at the poles. # Returns -- `::PolarSplineSpace`: The resulting scalar polar spline space. + + - `::PolarSplineSpace`: The resulting scalar polar spline space. """ function create_scalar_polar_spline_space( num_elements::NTuple{2, Int}, @@ -509,16 +533,18 @@ directions. Optional arguments include the geometry coefficients, whether there and whether the scalars are constrained to zero at the poles. # Arguments -- `num_elements::NTuple{2, Int}`: The number of elements in the angular and radial directions. -- `section_spaces::F`: The section spaces for the angular and radial directions. -- `regularities::NTuple{2, Int}`: The regularities of the B-spline in the angular and radial directions. -- `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}=nothing`: The geometry coefficients for the polar spline space. -- `R::Float64=1.0`: The radius of the polar spline space. -- `two_poles::Bool=false`: Whether the polar spline space has two poles. -- `zero_at_poles::Bool=false`: Whether the scalars are constrained to zero at the poles. + + - `num_elements::NTuple{2, Int}`: The number of elements in the angular and radial directions. + - `section_spaces::F`: The section spaces for the angular and radial directions. + - `regularities::NTuple{2, Int}`: The regularities of the B-spline in the angular and radial directions. + - `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}=nothing`: The geometry coefficients for the polar spline space. + - `R::Float64=1.0`: The radius of the polar spline space. + - `two_poles::Bool=false`: Whether the polar spline space has two poles. + - `zero_at_poles::Bool=false`: Whether the scalars are constrained to zero at the poles. # Returns -- `::PolarSplineSpace`: The resulting scalar polar spline space. + + - `::PolarSplineSpace`: The resulting scalar polar spline space. """ function create_scalar_polar_spline_space( num_elements::NTuple{2, Int}, @@ -586,14 +612,16 @@ and the number of elements in the angular and radial directions. Optional argume the geometry coefficients, and whether there are two poles. # Arguments -- `num_elements::NTuple{2, Int}`: The number of elements in each direction. -- `degrees::NTuple{2, Int}`: The polynomial degrees in each direction. -- `regularities::NTuple{2, Int}`: The regularities in each direction. -- `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}`: The geometry coefficients. -- `R::Float64`: The radius of the domain. + + - `num_elements::NTuple{2, Int}`: The number of elements in each direction. + - `degrees::NTuple{2, Int}`: The polynomial degrees in each direction. + - `regularities::NTuple{2, Int}`: The regularities in each direction. + - `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}`: The geometry coefficients. + - `R::Float64`: The radius of the domain. # Returns -- `::PolarSplineSpace`: The resulting vector polar spline space. + + - `::PolarSplineSpace`: The resulting vector polar spline space. """ function create_vector_polar_spline_space( num_elements::NTuple{2, Int}, @@ -632,14 +660,16 @@ and the number of elements in the angular and radial directions. Optional argume the geometry coefficients, and whether there are two poles. # Arguments -- `num_elements::NTuple{2, Int}`: The number of elements in each direction. -- `degrees::NTuple{2, Int}`: The polynomial degrees in each direction. -- `regularities::NTuple{2, Int}`: The regularities in each direction. -- `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}`: The geometry coefficients. -- `R::Float64`: The radius of the domain. + + - `num_elements::NTuple{2, Int}`: The number of elements in each direction. + - `degrees::NTuple{2, Int}`: The polynomial degrees in each direction. + - `regularities::NTuple{2, Int}`: The regularities in each direction. + - `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}`: The geometry coefficients. + - `R::Float64`: The radius of the domain. # Returns -- `::PolarSplineSpace`: The resulting vector polar spline space. + + - `::PolarSplineSpace`: The resulting vector polar spline space. """ function create_vector_polar_spline_space( num_elements::NTuple{2, Int}, @@ -682,15 +712,17 @@ end Create all data required for creating a polar spline geometry. # Arguments -- `num_elements::NTuple{2, Int}`: The number of elements in each direction. -- `degrees::NTuple{2, Int}`: The polynomial degrees in each direction. -- `regularities::NTuple{2, Int}`: The regularities in each direction. -- `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}`: The geometry coefficients. -- `R::Float64`: The radius of the domain. + + - `num_elements::NTuple{2, Int}`: The number of elements in each direction. + - `degrees::NTuple{2, Int}`: The polynomial degrees in each direction. + - `regularities::NTuple{2, Int}`: The regularities in each direction. + - `geom_coeffs_tp::Union{Nothing, Array{Float64, 3}}`: The geometry coefficients. + - `R::Float64`: The radius of the domain. # Returns -- `P_geom`: The polar spline geometry space. -- `geom_coeffs_polar`: The polar geometry coefficients. + + - `P_geom`: The polar spline geometry space. + - `geom_coeffs_polar`: The polar geometry coefficients. """ function create_polar_geometry_data( num_elements::NTuple{2, Int}, @@ -732,13 +764,15 @@ end Refine the polar geometry data. # Arguments -- `P_geom`: The polar spline geometry space. -- `geom_coeffs_polar`: The polar geometry coefficients. -- `two_poles`: Whether the polar geometry contains two poles. + + - `P_geom`: The polar spline geometry space. + - `geom_coeffs_polar`: The polar geometry coefficients. + - `two_poles`: Whether the polar geometry contains two poles. # Returns -- `P_geom_ref`: The refined polar spline geometry space. -- `geom_coeffs_polar_tp_ref`: The refined polar geometry coefficients. + + - `P_geom_ref`: The refined polar spline geometry space. + - `geom_coeffs_polar_tp_ref`: The refined polar geometry coefficients. """ function refine_polar_geometry_data(P_geom, geom_coeffs_polar; two_poles=false) # refine the space and build two-scale operator @@ -758,14 +792,16 @@ end Generate degenerate control points for a given number of poloidal and radial divisions. # Arguments -- n_p::Int: Number of poloidal divisions. -- n_r::Int: Number of radial divisions. -- R::Float64: Radius of the polar domain. + + - n_p::Int: Number of poloidal divisions. + - n_r::Int: Number of radial divisions. + - R::Float64: Radius of the polar domain. # Returns -- degenerate_control_points::Array{Float64,3}: The degenerate control points. -- radii::Vector{Float64}: The radii values. -- theta::Vector{Float64}: The theta values. + + - degenerate_control_points::Array{Float64,3}: The degenerate control points. + - radii::Vector{Float64}: The radii values. + - theta::Vector{Float64}: The theta values. """ function _build_standard_degenerate_control_points(n_p::Int, n_r::Int, R::Float64) radii = LinRange(0.0, R, n_r) diff --git a/src/FunctionSpaces/FunctionSpaces.jl b/src/FunctionSpaces/FunctionSpaces.jl index 456d44adb..cfce94541 100644 --- a/src/FunctionSpaces/FunctionSpaces.jl +++ b/src/FunctionSpaces/FunctionSpaces.jl @@ -44,9 +44,10 @@ Supertype for all finite element spaces. These can be of any dimension, with any components, and on any number of patches. # Type parameters -- `manifold_dim::Int`: Dimension of the manifold. -- `num_components::Int`: Number of (output) components of the function space. -- `num_patches::Int`: Number of patches over which the function space is defined. + + - `manifold_dim::Int`: Dimension of the manifold. + - `num_components::Int`: Number of (output) components of the function space. + - `num_patches::Int`: Number of patches over which the function space is defined. """ abstract type AbstractFESpace{manifold_dim, num_components, num_patches} <: AbstractFunctionSpace end diff --git a/src/GeneralHelpers/GeneralHelpers.jl b/src/GeneralHelpers/GeneralHelpers.jl index b3d9d5238..15eb9690b 100644 --- a/src/GeneralHelpers/GeneralHelpers.jl +++ b/src/GeneralHelpers/GeneralHelpers.jl @@ -31,12 +31,14 @@ An optional `id` can be given to force the compilation of a new cache. This is u avoid clashes of dictionaries using the same types. !!! warning + As per Julia's documentation, it is not guaranteed that the dictionary is not wiped during runtime. Read [this](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions) for more information. # Examples + ```jldoctest julia> using Mantis @@ -56,7 +58,6 @@ Dict{Int64, String} with 1 entry: julia> new_dict = Mantis.GeneralHelpers.cache_dict(Int, String, Val(2)) Dict{Int64, String}() - ``` """ @generated function cache_dict(::Type{K}, ::Type{V}, id=Val{1}()) where {K, V} @@ -79,7 +80,7 @@ See also [`cache_dict`](@ref) and `get!`. function get_from_cache(::Type{K}, ::Type{V}, key, f, id=Val{1}()) where {K, V} dict = cache_dict(K, V, id) get!(dict, key) do - f(key) + return f(key) end end @@ -92,10 +93,11 @@ If `local_basis` corresponds to basis evaluations for some `manifold_dim`-variat space, then its `k`-th derivatives will all be stored in the location `local_basis[k+1]`. Moreover, the `k`-th derivative corresponding to the key `[i₁,i₂,...,iₙ]` in the location `local_basis[k+1][m]` where: -- `m = 1` when `iⱼ = 0` for all `j`, i.e., for basis function values; -- `m = 1+r` when `iⱼ = 0` for all `j` except for `j = r` and `iⱼ = 1`, i.e., + + - `m = 1` when `iⱼ = 0` for all `j`, i.e., for basis function values; + - `m = 1+r` when `iⱼ = 0` for all `j` except for `j = r` and `iⱼ = 1`, i.e., for the first derivative w.r.t. the `j`-th canonical coordinate; -- in all other cases (i.e., when `k>1`), the value of `m` is equal to `l` + - in all other cases (i.e., when `k>1`), the value of `m` is equal to `l` if `[i₁,i₂,...,iₙ]` is the `l`-th key returned by the function `integer_sums(k, Val(manifold_dim))`. @@ -107,10 +109,12 @@ An optional `id` can be given choose the used cache. This is useful to avoid cla dictionaries using the same types. See also [`cache_dict`](@ref). # Arguments -- `der_key::NTuple{manifold_dim, Int}`: A key for the desired derivative order. + + - `der_key::NTuple{manifold_dim, Int}`: A key for the desired derivative order. # Returns -- `::Int`: The linear index corresponding to the derivative's storage location in basis + + - `::Int`: The linear index corresponding to the derivative's storage location in basis evaluations. """ function get_derivative_idx( @@ -150,11 +154,13 @@ An optional `id` can be given choose the used cache. This is useful to avoid cla dictionaries using the same types. See also [`cache_dict`](@ref). # Arguments -- `sum_indices::Int`: The target sum of the integers in each combination. -- `num_indices::Val{N}`: The number of integers in each combination, given by `N`. + + - `sum_indices::Int`: The target sum of the integers in each combination. + - `num_indices::Val{N}`: The number of integers in each combination, given by `N`. # Returns -- `::Vector{NTuple{N, Int}}`: Each inner vector represents a combination of integers that + + - `::Vector{NTuple{N, Int}}`: Each inner vector represents a combination of integers that sum up to `sum_indices`. If no valid combinations exist, the vectors are empty. """ function integer_sums(sum_indices::Int, num_indices::Val{N}, id=Val{1}()) where {N} @@ -198,6 +204,7 @@ An optional `id` can be given choose the used cache. This is useful to avoid cla dictionaries using the same types. See also [`cache_dict`](@ref). # Examples + ```jldoctest julia> using Mantis; @@ -209,7 +216,6 @@ julia> Mantis.GeneralHelpers.integer_sums(0, 2, Val(2)) (0, 2) (1, 1) (2, 0) - ``` """ function integer_sums(init_sum::Int, final_sum::Int, num_indices::Val, id=Val{1}()) @@ -223,6 +229,7 @@ Return the number of distinct partial derivatives of order `d` in an `n`-variate assuming equality of mixed partial derivatives. # Examples + ```jldoctest julia> using Mantis @@ -231,7 +238,6 @@ julia> Mantis.GeneralHelpers.num_der_indices(1, 2) julia> Mantis.GeneralHelpers.num_der_indices(3, 2) 6 - ``` """ num_der_indices(n, d) = binomial(n + d - 1, n - 1) @@ -240,7 +246,7 @@ function Array{T, N}(::UndefInitializer, dims...) where {T, N} if !matches(Array{T, N}, dims...) throw( DimensionMismatch( - "$(Array{T, N}) incompatible with number of dimensions $(length.(dims)).", + "$(Array{T, N}) incompatible with number of dimensions $(length.(dims))." ), ) end @@ -316,14 +322,19 @@ end Create a directory (if needed) and return the path to the output file. # Arguments -- `output_directory_tree::Vector`: A vector representing the directory tree. -- `filename`: The name of the output file. + + - `output_directory_tree::Vector`: A vector representing the directory tree. + - `filename`: The name of the output file. # Example + ```julia julia> using Mantis -julia> output_file = Mantis.GeneralHelpers.export_path(["examples", "data", "output"], "output.vtu"); +julia> output_file = Mantis.GeneralHelpers.export_path( + ["examples", "data", "output"], "output.vtu" + ); + ``` """ function export_path(output_directory_tree::Vector, filename) diff --git a/src/Geometry/CartesianGeometry.jl b/src/Geometry/CartesianGeometry.jl index 817572ad1..aee1962a8 100644 --- a/src/Geometry/CartesianGeometry.jl +++ b/src/Geometry/CartesianGeometry.jl @@ -8,22 +8,15 @@ multiple patches, even though each patch is still a Cartesian grid. Note that th are not required to have a matching grid. # Fields -- `breakpoints::T`: A tuple of vectors defining the grid points in each dimension. -- `cart_num_elements::CI`: A (tuple of) `CartesianIndices` representing the indices of + + - `breakpoints::T`: A tuple of vectors defining the grid points in each dimension. + - `cart_num_elements::CI`: A (tuple of) `CartesianIndices` representing the indices of elements in the grid for each patch. # Constructors -- `CartesianGeometry( - breakpoints::T - ) where { - manifold_dim, - num_patches, - NT <: Number, - T <: NTuple{num_patches, NTuple{manifold_dim, AbstractVector{NT}}}, - }`: General constructor. -- `CartesianGeometry( - breakpoints::NTuple{manifold_dim, AbstractVector{NT}} - ) where {manifold_dim, NT <: Number}`: Single-patch convenience constructor. + + - `CartesianGeometry( breakpoints::T ) where { manifold_dim, num_patches, NT <: Number, T <: NTuple{num_patches, NTuple{manifold_dim, AbstractVector{NT}}}, }`: General constructor. + - `CartesianGeometry( breakpoints::NTuple{manifold_dim, AbstractVector{NT}} ) where {manifold_dim, NT <: Number}`: Single-patch convenience constructor. """ struct CartesianGeometry{manifold_dim, image_dim, num_patches, T, CI, LI} <: AbstractGeometry{manifold_dim, image_dim, num_patches} @@ -120,7 +113,7 @@ get_breakpoint(geometry::CartesianGeometry, patch_id::Int=1, dim::Int=1, point:: geometry.breakpoints[patch_id][dim][point] """ - get_cart_num_elements(geometry::CartesianGeometry, patch_id::Int=1) + get_cart_num_elements(geometry::CartesianGeometry, patch_id::Int=1) Returns a CartesianIndices iterator of all elements in the patch indicated by `patch_id`. """ @@ -128,7 +121,7 @@ get_cart_num_elements(geometry::CartesianGeometry, patch_id::Int=1) = geometry.cart_num_elements[patch_id] """ - get_lin_num_elements(geometry::CartesianGeometry, patch_id::Int=1) + get_lin_num_elements(geometry::CartesianGeometry, patch_id::Int=1) Returns a LinearIndices iterator of all elements in the patch indicated by `patch_id`. """ @@ -239,9 +232,7 @@ function hessian( ) where {manifold_dim, image_dim, num_patches} # The Hessian is zero for Cartesian geometries. num_points = Points.get_num_points(xi) - return [ - ntuple(image_dim) do _ - return zeros(SMatrix{manifold_dim, manifold_dim}) - end for _ in 1:num_points - ] + return [ntuple(image_dim) do _ + return zeros(SMatrix{manifold_dim, manifold_dim}) + end for _ in 1:num_points] end diff --git a/src/Geometry/DiscreteGeometry.jl b/src/Geometry/DiscreteGeometry.jl index 0c45e0076..a7b329e5a 100644 --- a/src/Geometry/DiscreteGeometry.jl +++ b/src/Geometry/DiscreteGeometry.jl @@ -11,18 +11,20 @@ A geometry defined in terms of an `evaluable_function` over a given number of el manifold dimension. # Fields -- `evaluable_function::F`: Function that, given an `element_id` and local coordinates `xi`, + + - `evaluable_function::F`: Function that, given an `element_id` and local coordinates `xi`, returns values and derivatives in the same nested format as a finite element space (see [FunctionSpaces](@ref)). In particular, the output structure must be compatible with `FunctionSpaces.AbstractFESpace` so that geometry evaluations and Jacobians can be computed element-wise. -- `num_elements::Int`: Total number of elements in the discrete geometry. + - `num_elements::Int`: Total number of elements in the discrete geometry. # Type parameters -- `manifold_dim`: Dimension of the reference (parametric) domain. -- `image_dim`: Dimension of the physical embedding space. -- `num_patches`: Number of patches of the geometry. -- `F`: The type of the evaluable function. + + - `manifold_dim`: Dimension of the reference (parametric) domain. + - `image_dim`: Dimension of the physical embedding space. + - `num_patches`: Number of patches of the geometry. + - `F`: The type of the evaluable function. """ struct DiscreteGeometry{manifold_dim, image_dim, num_patches, F, L, NP} <: AbstractGeometry{manifold_dim, image_dim, num_patches} diff --git a/src/Geometry/EvaluationMask/AffineEvaluationMask.jl b/src/Geometry/EvaluationMask/AffineEvaluationMask.jl index cf127b4a6..38e93842f 100644 --- a/src/Geometry/EvaluationMask/AffineEvaluationMask.jl +++ b/src/Geometry/EvaluationMask/AffineEvaluationMask.jl @@ -6,18 +6,20 @@ AffineEvaluationMask{manifold_dim, num_elements, num_elements_base, M, T, S} Given an object with d-dimensional elements 1:n, we construct: -* A new object with d-dimensional elements 1:m -* A function E: {1:m} -> {1:n} such that if E(i) = j, then the i-th element is nested -inside the j-th element -* A data structure F: {1:m} -> (x, c) \\in [0,1]^{2d} such that, after rescaling the j-th - element to a unit cell, a point y inside the i-th element can be located inside the j-th - element as: x + c * y. -The tuple (E, F) defines an evaluation mask. + + - A new object with d-dimensional elements 1:m + - A function E: {1:m} -> {1:n} such that if E(i) = j, then the i-th element is nested + inside the j-th element + - A data structure F: {1:m} -> (x, c) \\in [0,1]^{2d} such that, after rescaling the j-th + element to a unit cell, a point y inside the i-th element can be located inside the j-th + element as: x + c * y. + The tuple (E, F) defines an evaluation mask. # Fields -- `element_id_map::M`: The mapping from the evaluation elements to the base elements. -- `translations::Vector{T}`: The translations from base to evaluation elements. -- `scalings::Vector{S}`: The scalings of evalution elements relative to the base elements. + + - `element_id_map::M`: The mapping from the evaluation elements to the base elements. + - `translations::Vector{T}`: The translations from base to evaluation elements. + - `scalings::Vector{S}`: The scalings of evalution elements relative to the base elements. """ struct AffineEvaluationMask{manifold_dim, M, T, S} <: AbstractEvaluationMask{manifold_dim} element_id_map::M @@ -114,11 +116,13 @@ get_scalings(eval_mask::AffineEvaluationMask) = eval_mask.scalings Get the base element index. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id::Int`: The element index. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id::Int`: The element index. # Returns -- `element_id_base::Int`: The base element index. + + - `element_id_base::Int`: The base element index. """ function get_base_element(eval_mask::AffineEvaluationMask, element_id::Int) return get_element_id_map(eval_mask)(element_id) @@ -130,11 +134,13 @@ end Get the translation of the element. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id::Int`: The element index. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id::Int`: The element index. # Returns -- `translation<:NTuple{manifold_dim, Real}`: The translation. + + - `translation<:NTuple{manifold_dim, Real}`: The translation. """ function get_translation(eval_mask::AffineEvaluationMask, element_id::Int) return get_translations(eval_mask)(element_id) @@ -146,11 +152,13 @@ end Get the product of the scaling of the element. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id::Int`: The element index. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id::Int`: The element index. # Returns -- `scaling::Float64`: The scaling. + + - `scaling::Float64`: The scaling. """ function get_measure_scale(eval_mask::AffineEvaluationMask, element_id::Int) return prod(get_scaling(eval_mask, element_id)) @@ -162,11 +170,13 @@ end Get the scaling of the element. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id::Int`: The element index. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id::Int`: The element index. # Returns -- `scaling<:NTuple{manifold_dim, Real}`: The scaling. + + - `scaling<:NTuple{manifold_dim, Real}`: The scaling. """ function get_scaling(eval_mask::AffineEvaluationMask, element_id::Int) return get_scalings(eval_mask)(element_id) @@ -186,14 +196,16 @@ end Evaluate the mapping from the evaluation mask to the base. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id::Int`: The evaluation element index. -- `xi::Points.AbstractPointsP{manifold_dim}`: The canonical points for the evaluation mask. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id::Int`: The evaluation element index. + - `xi::Points.AbstractPointsP{manifold_dim}`: The canonical points for the evaluation mask. # Returns -- `element_id_base::Int`: The element index in the range geometry. -- `xi_base:Points.AbstractPoints{manifold_dim}`: The canonical points for the base. -- `scaling::NTuple{manifold_dim, Float64}`: The scaling of the element. + + - `element_id_base::Int`: The element index in the range geometry. + - `xi_base:Points.AbstractPoints{manifold_dim}`: The canonical points for the base. + - `scaling::NTuple{manifold_dim, Float64}`: The scaling of the element. """ function transform_evaluation_points( eval_mask::AffineEvaluationMask{manifold_dim, num_elements, num_elements_base}, @@ -229,11 +241,13 @@ end Get the element indices corresponding to a base element. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id_base::Int`: The base element index. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id_base::Int`: The base element index. # Returns -- `element_ids::Vector{Int}`: The element indices. + + - `element_ids::Vector{Int}`: The element indices. """ function get_element_ids(eval_mask::AffineEvaluationMask, element_id_base::Int) return findall(id -> id == element_id_base, get_element_id_map(eval_mask)) @@ -241,21 +255,23 @@ end """ compose_evaluation_masks( - eval_mask_1::AffineEvaluationMask{manifold_dim, num_elements, num_elements_base_1}, - eval_mask_2::AffineEvaluationMask{ - manifold_dim, num_elements_base_1, num_elements_base_2 - }, + eval_mask_1::AffineEvaluationMask{manifold_dim, num_elements, num_elements_base_1}, + eval_mask_2::AffineEvaluationMask{ + manifold_dim, num_elements_base_1, num_elements_base_2 + }, ) Compose two evaluation maskes. # Arguments -- `eval_mask_1::AffineEvaluationMask`: The first evaluation mask. -- `eval_mask_2::AffineEvaluationMask`: The second evaluation mask + + - `eval_mask_1::AffineEvaluationMask`: The first evaluation mask. + - `eval_mask_2::AffineEvaluationMask`: The second evaluation mask # Returns -- `eval_mask::AffineEvaluationMask`: The composed evaluation mask (E_2\\circ E_1), such that -the evaluation elements are nested inside the base elements of the second evaluation mask. + + - `eval_mask::AffineEvaluationMask`: The composed evaluation mask (E_2\\circ E_1), such that + the evaluation elements are nested inside the base elements of the second evaluation mask. """ function compose_evaluation_masks( eval_mask_1::AffineEvaluationMask{manifold_dim}, @@ -298,11 +314,13 @@ end Create a trivial evaluation mask. # Arguments -- `manifold_dim::Int`: The dimension of the manifold. -- `num_elements::Int`: The number of elements. + + - `manifold_dim::Int`: The dimension of the manifold. + - `num_elements::Int`: The number of elements. # Returns -- `eval_mask::AffineEvaluationMask`: The trivial evaluation mask. + + - `eval_mask::AffineEvaluationMask`: The trivial evaluation mask. """ function trivial_evaluation_mask(manifold_dim::Int, num_elements::Int) element_id_map(element_id) = element_id @@ -323,10 +341,12 @@ end Trim the evaluation mask by excluding elements. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. # Returns -- `eval_mask::AffineEvaluationMask`: The trimmed evaluation mask. + + - `eval_mask::AffineEvaluationMask`: The trimmed evaluation mask. """ function trim_evaluation_mask( eval_mask::AffineEvaluationMask{manifold_dim, num_elements, num_elements_base}, diff --git a/src/Geometry/EvaluationMask/EvaluationMask.jl b/src/Geometry/EvaluationMask/EvaluationMask.jl index ff9f96c55..760a7bad9 100644 --- a/src/Geometry/EvaluationMask/EvaluationMask.jl +++ b/src/Geometry/EvaluationMask/EvaluationMask.jl @@ -20,10 +20,12 @@ abstract type AbstractEvaluationMask{manifold_dim} end Get the dimension of the manifold. # Arguments -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. + + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. # Returns -- `manifold_dim::Int`: The dimension of the manifold. + + - `manifold_dim::Int`: The dimension of the manifold. """ function get_manifold_dim(::AbstractEvaluationMask{manifold_dim}) where {manifold_dim} return manifold_dim @@ -35,10 +37,12 @@ end Get the number of elements in the evaluation mask. # Arguments -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. + + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. # Returns -- `num_elements::Int`: The number of elements. + + - `num_elements::Int`: The number of elements. """ function get_num_elements(eval_mask::AbstractEvaluationMask) return eval_mask.num_elements @@ -50,10 +54,12 @@ end Get the number of elements in the base mesh. # Arguments -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. + + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. # Returns -- `num_elements_base::Int`: The number of elements in the base mesh. + + - `num_elements_base::Int`: The number of elements in the base mesh. """ function get_num_elements_base(eval_mask::AbstractEvaluationMask) return eval_mask.num_elements_base @@ -63,20 +69,22 @@ end transform_evaluation_points( eval_mask::AffineEvaluationMask{manifold_dim, num_elements, num_elements_base}, element_id::Int, - xi::Points.AbstractPoints{manifold_dim} + xi::Points.AbstractPoints{manifold_dim} ) Evaluate the mapping from the evaluation mask to the base. # Arguments -- `eval_mask::AffineEvaluationMask`: The evaluation mask. -- `element_id::Int`: The evaluation element index. -- `xi::Points.AbstractPoints{manifold_dim}`: The canonical points for the evaluation mask. + + - `eval_mask::AffineEvaluationMask`: The evaluation mask. + - `element_id::Int`: The evaluation element index. + - `xi::Points.AbstractPoints{manifold_dim}`: The canonical points for the evaluation mask. # Returns -- `element_id_base::Int`: The element index in the range geometry. -- `xi_base::NTuple{manifold_dim, T}`: The canonical points for the base. -- `scaling::NTuple{manifold_dim, Float64}`: The scaling of the element. + + - `element_id_base::Int`: The element index in the range geometry. + - `xi_base::NTuple{manifold_dim, T}`: The canonical points for the base. + - `scaling::NTuple{manifold_dim, Float64}`: The scaling of the element. """ function transform_evaluation_points( eval_mask::AbstractEvaluationMask{manifold_dim}, @@ -87,28 +95,30 @@ function transform_evaluation_points( end """ - transform_element_vertices( - eval_mask::AbstractEvaluationMask{manifold_dim}, - element_vertices_base::NTuple{T}, - element_id::Int, - ) where {manifold_dim, T <: AbstractVector} + transform_element_vertices( + eval_mask::AbstractEvaluationMask{manifold_dim}, + element_vertices_base::NTuple{T}, + element_id::Int, + ) where {manifold_dim, T <: AbstractVector} Transforms the `element_vertices_base`, corresponding to the element vertices at the base element of `element_id`, according to the transformation of `eval_mask`. # Arguments -- `eval_mask::AbstractEvaluationMask{manifold_dim}`: The evaluation mask. -- `element_vertices_base::NTuple{T}`: The element vertices of the base geometry, evaluated - at the base element corresponding to `element_id`. -- `element_id::Int`: The element of evaluation mask. + + - `eval_mask::AbstractEvaluationMask{manifold_dim}`: The evaluation mask. + - `element_vertices_base::NTuple{T}`: The element vertices of the base geometry, evaluated + at the base element corresponding to `element_id`. + - `element_id::Int`: The element of evaluation mask. # Returns -- `element_vertices::NTuple{T}`: The transformed element vertices corresponding to - `element_id`. + + - `element_vertices::NTuple{T}`: The transformed element vertices corresponding to + `element_id`. """ function transform_element_vertices( eval_mask::AbstractEvaluationMask{manifold_dim}, - element_vertices_base::NTuple{manifold_dim, NTuple{2, T}}, + element_vertices_base::NTuple{manifold_dim, NTuple{2, T}}, element_id::Int, ) where {manifold_dim, T <: Real} return throw( diff --git a/src/Geometry/EvaluationMask/EvaluationMaskHelpers.jl b/src/Geometry/EvaluationMask/EvaluationMaskHelpers.jl index 8dea9b83e..f092a828d 100644 --- a/src/Geometry/EvaluationMask/EvaluationMaskHelpers.jl +++ b/src/Geometry/EvaluationMask/EvaluationMaskHelpers.jl @@ -10,12 +10,14 @@ mesh into `nsubd[i][j]` elements in the `j`-th direction. The `elements_to_exclu is a vector of element indices to exclude from the mesh. # Arguments -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. -- `nsubd::Vector{NTuple{manifold_dim, Int}}`: The number of subdivisions in each direction. -- `elements_to_exclude::AbstractVector{Int}`: The elements to exclude. + + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. + - `nsubd::Vector{NTuple{manifold_dim, Int}}`: The number of subdivisions in each direction. + - `elements_to_exclude::AbstractVector{Int}`: The elements to exclude. # Returns -- `subdiv_eval_mask::AbstractEvaluationMask`: The subdivided evaluation mask. + + - `subdiv_eval_mask::AbstractEvaluationMask`: The subdivided evaluation mask. """ function subdivide_evaluation_mask( eval_mask::AbstractEvaluationMask{manifold_dim}, @@ -64,12 +66,14 @@ mesh into `nsubd[i][j]` elements in the `j`-th direction. The `elements_to_exclu argument is a vector of element indices to exclude from the mesh. # Arguments -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. -- `nsubd::Vector{Int}`: The number of subdivisions for each element. -- `elements_to_exclude::Vector{Int}`: The elements to exclude. + + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. + - `nsubd::Vector{Int}`: The number of subdivisions for each element. + - `elements_to_exclude::Vector{Int}`: The elements to exclude. # Returns -- `subdiv_eval_mask::AbstractEvaluationMask`: The subdivided evaluation mask. + + - `subdiv_eval_mask::AbstractEvaluationMask`: The subdivided evaluation mask. """ function subdivide_evaluation_mask( eval_mask::AbstractEvaluationMask{manifold_dim}, @@ -93,12 +97,14 @@ mesh into `nsubd[i][j]` elements in the `j`-th direction. The `elements_to_exclu argument is a vector of element indices to exclude from the mesh. # Arguments -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. -- `nsubd::Int`: The number of subdivisions for each element. -- `elements_to_exclude::AbstractVector{Int}`: The elements to exclude. + + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. + - `nsubd::Int`: The number of subdivisions for each element. + - `elements_to_exclude::AbstractVector{Int}`: The elements to exclude. # Returns -- `subdiv_eval_mask::AbstractEvaluationMask`: The subdivided evaluation mask. + + - `subdiv_eval_mask::AbstractEvaluationMask`: The subdivided evaluation mask. """ function subdivide_evaluation_mask( eval_mask::AbstractEvaluationMask{manifold_dim}, diff --git a/src/Geometry/Geometry.jl b/src/Geometry/Geometry.jl index 6f5c41670..8c244194c 100644 --- a/src/Geometry/Geometry.jl +++ b/src/Geometry/Geometry.jl @@ -18,12 +18,13 @@ import ..Hierarchy Supertype for all geometries. # Type parameters -- `manifold_dim`: Dimension of the manifold that parameterises the geometry (the intrinsic + + - `manifold_dim`: Dimension of the manifold that parameterises the geometry (the intrinsic dimension). -- `image_dim`: Embedding dimension of the geometry. For example, a planar geometry will + - `image_dim`: Embedding dimension of the geometry. For example, a planar geometry will have a `manifold_dim` and `image_dim` of 2. But a surface embedded in 3D will have a `manifold_dim` of 2 and an `image_dim` of 3. -- `num_patches`: The number of patch that constitute the geometry. There can be many + - `num_patches`: The number of patch that constitute the geometry. There can be many elements on one patch, but a single patch is always structured as a tensorial cube. The multi-patch setting allows for an unstructured connecting of the patches, allowing more general geometries to be described. @@ -36,10 +37,12 @@ abstract type AbstractGeometry{manifold_dim, image_dim, num_patches} end Returns the dimensions of the domain manifold of a given geometry. # Arguments -- `::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. + + - `::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. # Returns -- `::Int`: The domain manifold dimension. + + - `::Int`: The domain manifold dimension. """ get_manifold_dim( ::AbstractGeometry{manifold_dim, image_dim, num_patches} @@ -51,10 +54,12 @@ get_manifold_dim( Returns the dimensions of the image manifold of a given geometry. # Arguments -- `::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. + + - `::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. # Returns -- `::Int`: The image manifold dimension. + + - `::Int`: The image manifold dimension. """ get_image_dim( ::AbstractGeometry{manifold_dim, image_dim, num_patches} @@ -66,10 +71,12 @@ get_image_dim( Returns the number of patches in a given geometry. # Arguments -- `::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. + + - `::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. # Returns -- `::Int`: The number of patches in the geometry. + + - `::Int`: The number of patches in the geometry. """ get_num_patches( ::AbstractGeometry{manifold_dim, image_dim, num_patches} @@ -81,19 +88,22 @@ get_num_patches( Get the ID of the patch to which the specified global element belongs. # Arguments -- `geometry::AbstractGeometry`: The (multi-patch) geometry. -- `element_id::Int`: The global element ID. + + - `geometry::AbstractGeometry`: The (multi-patch) geometry. + - `element_id::Int`: The global element ID. # Returns -- `patch_id::Int`: ID of the patch to which the element belongs. + + - `patch_id::Int`: ID of the patch to which the element belongs. # Exceptions -- ArgumentError: This error is thrown if the given `element_id` is larger than the total + + - ArgumentError: This error is thrown if the given `element_id` is larger than the total number of elements found in the `geometry`. -- Error "no field 'num_elements'": This error is thrown if the number of elements is not + - Error "no field 'num_elements'": This error is thrown if the number of elements is not stored in a field called `num_elements` and if no specific `get_num_elements` method is defined for the given `geometry`. -- Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements + - Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements per patch is not stored in a field called `num_elements_per_patch` and if no specific `get_num_elements`-method (with `patch_id` argument) is defined for the given `geometry`. @@ -106,7 +116,7 @@ function get_patch_id(geometry::AbstractGeometry, element_id::Int) return patch_id end end - throw( + return throw( ArgumentError( LazyString( "Element ID ", @@ -145,20 +155,23 @@ end Get the constituent patch ID and local element ID for the specified global element ID. # Arguments -- `geometry::AbstractGeometry`: The multi-patch geometry. -- `element_id::Int`: The global element ID. + + - `geometry::AbstractGeometry`: The multi-patch geometry. + - `element_id::Int`: The global element ID. # Returns -- `patch_id::Int`: The patch ID -- `local_element_id::Int`: The local element ID. + + - `patch_id::Int`: The patch ID + - `local_element_id::Int`: The local element ID. # Exceptions -- ArgumentError: This error is thrown if the given `element_id` is larger than the total + + - ArgumentError: This error is thrown if the given `element_id` is larger than the total number of elements found in the `geometry`. -- Error "no field 'num_elements'": This error is thrown if the number of elements is not + - Error "no field 'num_elements'": This error is thrown if the number of elements is not stored in a field called `num_elements` and if no specific `get_num_elements` method is defined for the given `geometry`. -- Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements + - Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements per patch is not stored in a field called `num_elements_per_patch` and if no specific `get_num_elements`-method (with `patch_id` argument) is defined for the given `geometry`. @@ -179,7 +192,7 @@ function get_patch_and_local_element_id(geometry::AbstractGeometry, element_id:: local_element_id -= num_element_patch_i end - throw( + return throw( ArgumentError( LazyString( "Element ID ", @@ -218,12 +231,14 @@ end Get the global element ID for the specified constituent patch ID and local element ID. # Arguments -- `geometry::AbstractGeometry`: A (multi-)patch geometry. -- `patch_id::Int`: The constituent patch ID. -- `local_element_id::Int`: The local element ID. + + - `geometry::AbstractGeometry`: A (multi-)patch geometry. + - `patch_id::Int`: The constituent patch ID. + - `local_element_id::Int`: The local element ID. # Returns -- `::Int`: The global element ID. + + - `::Int`: The global element ID. """ function get_global_element_id( geometry::AbstractGeometry, patch_id::Int, local_element_id::Int @@ -282,20 +297,24 @@ Returns the number of elements in `geometry`. If a `patch_id` is given, return t elements in the patch. # Arguments -- `geometry::AbstractGeometry`: The geometry being used. + + - `geometry::AbstractGeometry`: The geometry being used. # Returns -- `::Int`: The number of elements in the geometry. + + - `::Int`: The number of elements in the geometry. # Notes + This method is used as a fallback and assumes that the number of elements (per patch) are explicitly stored. # Exceptions -- Error "no field 'num_elements'": This error is thrown if the number of elements is not + + - Error "no field 'num_elements'": This error is thrown if the number of elements is not stored in a field called `num_elements` and if no specific `get_num_elements` method is defined for the given `geometry`. -- Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements + - Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements per patch is not stored in a field called `num_elements_per_patch` and if no specific `get_num_elements_per_patch`- or `get_num_elements`-method (with `patch_id` argument) is defined for the given `geometry`. @@ -313,17 +332,21 @@ end Returns the number of elements in each patch of `geometry`. # Arguments -- `geometry::AbstractGeometry`: The geometry being used. + + - `geometry::AbstractGeometry`: The geometry being used. # Returns -- `::NTuple{num_patches, Int}`: The number of elements per patch in the geometry. + + - `::NTuple{num_patches, Int}`: The number of elements per patch in the geometry. # Notes + This method is used as a fallback if there isn't a more specific method to be used. The latter should only be implemented explicitly if necessary. # Exceptions -- Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements + + - Error "no field 'num_elements_per_patch'": This error is thrown if the number of elements per patch is not stored in a field called `num_elements_per_patch` and if no specific `get_num_elements_per_patch` method is defined for the given `geometry`. """ @@ -337,22 +360,25 @@ end Get (or create) the physical geometry on a specific patch. # Arguments -- `geometry::AbstractGeometry`: The multi-patch geometry. -- `patch_id::Int`: The patch ID. + + - `geometry::AbstractGeometry`: The multi-patch geometry. + - `patch_id::Int`: The patch ID. # Returns -- `<:AbstractGeometry{manifold_dim, image_dim, 1}`: The geometry on the specified patch. + + - `<:AbstractGeometry{manifold_dim, image_dim, 1}`: The geometry on the specified patch. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ function get_geometry(geometry::AbstractGeometry, patch_id::Int) - throw(MethodError(get_geometry, (geometry, patch_id))) + return throw(MethodError(get_geometry, (geometry, patch_id))) end function get_parametric_geometry(geometry::AbstractGeometry) - throw(MethodError(get_parametric_geometry, geometry)) + return throw(MethodError(get_parametric_geometry, geometry)) end """ @@ -362,18 +388,21 @@ Finds the parametric geometry of the patch given by `patch_id` in `geometry`. If `patch_id` is given, the parametric geometry of the whole geometry is returned. # Arguments -- 'geometry::AbstractGeometry': The (physical) geometry being used. -- 'patch_id::Int': (Optional) ID of the patch to get the parametric geometry for. + + - 'geometry::AbstractGeometry': The (physical) geometry being used. + - 'patch_id::Int': (Optional) ID of the patch to get the parametric geometry for. # Returns -- '<:CartesianGeometry': The (patch-wise) parametric geometry. + + - '<:CartesianGeometry': The (patch-wise) parametric geometry. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ function get_parametric_geometry(geometry::AbstractGeometry, patch_id::Int) - throw(MethodError(get_parametric_geometry, (geometry, patch_id))) + return throw(MethodError(get_parametric_geometry, (geometry, patch_id))) end """ @@ -382,18 +411,21 @@ end Computes the measure of the element given by `element_id` in `geometry`. # Arguments -- 'geometry::AbstractGeometry': The geometry being used. -- 'element_id::Int': Index of the element being considered. + + - 'geometry::AbstractGeometry': The geometry being used. + - 'element_id::Int': Index of the element being considered. # Returns -- '<:Number': The measure of the element. + + - '<:Number': The measure of the element. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ function get_element_measure(geometry::AbstractGeometry, element_id::Int) - throw(MethodError(get_element_measure, (geometry, element_id))) + return throw(MethodError(get_element_measure, (geometry, element_id))) end """ @@ -405,20 +437,23 @@ Computes the length, in each manifold dimension, of the element given by `elemen `geometry`. # Arguments -- 'geometry::AbstractGeometry': The geometry being used. -- 'element_id::Int': Index of the element being considered. + + - 'geometry::AbstractGeometry': The geometry being used. + - 'element_id::Int': Index of the element being considered. # Returns -- '<:NTuple{manifold_dim, Number}': The element's lengths. + + - '<:NTuple{manifold_dim, Number}': The element's lengths. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ function get_element_lengths( geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}, element_id::Int ) where {manifold_dim, image_dim, num_patches} - throw(MethodError(get_element_lengths, (geometry, element_id))) + return throw(MethodError(get_element_lengths, (geometry, element_id))) end """ @@ -430,22 +465,25 @@ Computes the vertices, in each manifold dimension, of the element given by `elem `geometry`. # Arguments -- 'geometry::AbstractGeometry': The geometry being used. -- 'element_id::Int': Index of the element being considered. + + - 'geometry::AbstractGeometry': The geometry being used. + - 'element_id::Int': Index of the element being considered. # Returns -- '<:NTuple{manifold_dim, NTuple{2, Number}}': The element's vertices per manifold dim. For + + - '<:NTuple{manifold_dim, NTuple{2, Number}}': The element's vertices per manifold dim. For example, for the unit cube [0.0, 1.0]^3 this will be: ((0.0, 1.0), (0.0, 1.0), (0.0, 1.0)). # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ function get_element_vertices( geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}, element_id::Int ) where {manifold_dim, image_dim, num_patches} - throw(MethodError(get_element_vertices, (geometry, element_id))) + return throw(MethodError(get_element_vertices, (geometry, element_id))) end """ @@ -459,16 +497,19 @@ Computes the coordinates of the physical points, given the canonical points `xi` the element identified by `element_id` of a given `geometry`. # Arguments -- `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being evaluated. -- `element_id::Int`: The global element id. -- `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain at which to + + - `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being evaluated. + - `element_id::Int`: The global element id. + - `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain at which to evaluate the geometry. # Returns -- `::Matrix{Float64}`: The physical coordinates of `xi` on element `element_id`. The + + - `::Matrix{Float64}`: The physical coordinates of `xi` on element `element_id`. The size of the matrix is `(num_eval_points, image_dim)`. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ @@ -477,7 +518,7 @@ function evaluate( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim, image_dim, num_patches} - throw(MethodError(evaluate, (geometry, element_id, xi))) + return throw(MethodError(evaluate, (geometry, element_id, xi))) end """ @@ -491,17 +532,20 @@ Computes the jacobian at the physical points, given the canonical points `xi,` o element identified by `element_id` of a given `geometry`. # Arguments -- `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. -- `element_id::Int`: The global element id. -- `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain to evaluate + + - `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. + - `element_id::Int`: The global element id. + - `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain to evaluate the jacobian at. # Returns -- `::Vector{SMatrix{image_dim, manifold_dim, Float64}}`: The jacobian at the physical points + + - `::Vector{SMatrix{image_dim, manifold_dim, Float64}}`: The jacobian at the physical points corresponding to the canonical points `xi` on element `element_id`. The length of the vector equals the number of evaluation points. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ @@ -510,7 +554,7 @@ function jacobian( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim, image_dim, num_patches} - throw(MethodError(jacobian, (geometry, element_id, xi))) + return throw(MethodError(jacobian, (geometry, element_id, xi))) end """ @@ -524,18 +568,21 @@ Computes the hessian at the physical points, given the canonical points `xi,` on element identified by `element_id` of a given `geometry`. # Arguments -- `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. -- `element_id::Int`: The global element id. -- `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain to evaluate + + - `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry being used. + - `element_id::Int`: The global element id. + - `xi::Points.AbstractPoints{manifold_dim}`: The points in the canonical domain to evaluate the hessian at. # Returns -- `::Vector{NTuple{image_dim, SMatrix{manifold_dim, manifold_dim}}`: The hessian at the + + - `::Vector{NTuple{image_dim, SMatrix{manifold_dim, manifold_dim}}`: The hessian at the physical points corresponding to the canonical points `xi` on element `element_id`. The length of the vector equals the number of evaluation points. Per points, there are `image_dim` many hessian metrices, hence the tuple. # Notes + There is no generic fallback for this method. It should be implemented for each concrete geometry type. """ @@ -544,7 +591,7 @@ function hessian( element_id::Int, xi::Points.AbstractPoints{manifold_dim}, ) where {manifold_dim, image_dim, num_patches} - throw(MethodError(hessian, (geometry, element_id, xi))) + return throw(MethodError(hessian, (geometry, element_id, xi))) end include("CartesianGeometry.jl") diff --git a/src/Geometry/GeometryConversions.jl b/src/Geometry/GeometryConversions.jl index 4a7fd05bf..29afd3059 100644 --- a/src/Geometry/GeometryConversions.jl +++ b/src/Geometry/GeometryConversions.jl @@ -21,7 +21,7 @@ function Base.convert( cart_num_patches = CartesianIndices(const_num_patches) breakpoints_per_patch = ntuple(num_patches) do patch_id return merge_tuples( - (map(get_breakpoints, const_geometries, Tuple(cart_num_patches[patch_id])))..., + (map(get_breakpoints, const_geometries, Tuple(cart_num_patches[patch_id])))... ) end diff --git a/src/Geometry/GeometryHelpers.jl b/src/Geometry/GeometryHelpers.jl index 2a3b3f5da..87b4dc806 100644 --- a/src/Geometry/GeometryHelpers.jl +++ b/src/Geometry/GeometryHelpers.jl @@ -13,11 +13,13 @@ Create a Cartesian box geometry with `manifold_dim` dimensions, starting at `starting_points` and with `box_sizes` and `num_elements` defining the size of the box. # Arguments + - `starting_points::NTuple{manifold_dim, Float64}`: The starting points of the box. - `box_sizes::NTuple{manifold_dim, Float64}`: The size of the box. - `num_elements::NTuple{manifold_dim, Int}`: The number of elements in each dimension. # Output + - `::CartesianGeometry{manifold_dim}`: The Cartesian box geometry. """ function create_cartesian_box( @@ -66,11 +68,15 @@ function create_curvilinear_mapping( (2.0 / (box_sizes[2])) * x[2] - 2.0 * starting_points[2] / (box_sizes[2]) - 1.0 return ( [ - -(2.0 / box_sizes[1])*pi^2*c*sinpi(x1_new)*sinpi(x2_new) (2/box_sizes[2])*pi^2*c*cospi(x1_new)*cospi(x2_new) + -(2.0 / box_sizes[1])*pi^2*c*sinpi(x1_new)*sinpi(x2_new) (2/box_sizes[2])*pi^2*c*cospi( + x1_new + )*cospi(x2_new) (2.0/box_sizes[2])*pi^2*c*cospi(x1_new)*cospi(x2_new) -(2 * box_sizes[1] / (box_sizes[2]^2))*pi^2*c*sinpi(x1_new)*sinpi(x2_new) ], [ - -(2 * box_sizes[2] / (box_sizes[1]^2))*pi^2*c*sinpi(x1_new)*sinpi(x2_new) (2.0/box_sizes[1])*pi^2*c*cospi(x1_new)*cospi(x2_new) + -(2 * box_sizes[2] / (box_sizes[1]^2))*pi^2*c*sinpi(x1_new)*sinpi(x2_new) (2.0/box_sizes[1])*pi^2*c*cospi( + x1_new + )*cospi(x2_new) (2.0/box_sizes[1])*pi^2*c*cospi(x1_new)*cospi(x2_new) -(2.0 / box_sizes[2])*pi^2*c*sinpi(x1_new)*sinpi(x2_new) ], ) @@ -138,10 +144,12 @@ direction and a `c` parameter to change the deformation of the mapping. Not that becomes singular with `c` = 0.3. # Arguments + - `num_elements::NTuple{2,Int}`: The number of elements in each direction. - `c::Float64 = 0.1`: The `c` parameter. # Output + - `geometry::MappedGeometry{2, 2, 1}`: The curvilinear square geometry. """ function create_curvilinear_square( diff --git a/src/Geometry/HierarchicalGeometry.jl b/src/Geometry/HierarchicalGeometry.jl index 95a7ed8cd..04a7facc1 100644 --- a/src/Geometry/HierarchicalGeometry.jl +++ b/src/Geometry/HierarchicalGeometry.jl @@ -3,8 +3,8 @@ ############################################################################################ """ - HierarchicalGeometry{manifold_dim, image_dim, num_patches, G} <: - AbstractGeometry{manifold_dim, image_dim, num_patches} + HierarchicalGeometry{manifold_dim, image_dim, num_patches, G} <: + AbstractGeometry{manifold_dim, image_dim, num_patches} Represents a hierarchy of nested `geometries`. Which elements are active or inactive is determined by `active_elements`. @@ -12,11 +12,12 @@ determined by `active_elements`. See also [`Hierarchy.ActiveInfo`](@ref). # Fiels -- `geometries::G`: A tuple `G` such that `G <: NTuple{num_levels, AbstractGeometry}`, where -`num_levels` is the number of levels in the hierarchy. As a consequence, `num_levels` is -also the number of distinct level-wise geometries. -- `active_elements::Hierarchy.ActiveInfo`: Information about which elements are active or -inactive at each level and geometry. + + - `geometries::G`: A tuple `G` such that `G <: NTuple{num_levels, AbstractGeometry}`, where + `num_levels` is the number of levels in the hierarchy. As a consequence, `num_levels` is + also the number of distinct level-wise geometries. + - `active_elements::Hierarchy.ActiveInfo`: Information about which elements are active or + inactive at each level and geometry. """ struct HierarchicalGeometry{manifold_dim, image_dim, num_patches, G} <: AbstractGeometry{manifold_dim, image_dim, num_patches} @@ -51,7 +52,7 @@ end ############################################################################################ """ - get_geometries(geometry::HierarchicalGeometry) + get_geometries(geometry::HierarchicalGeometry) Returns the tuple of level-wise geometries defining the hierarchical `geometry`. """ @@ -60,7 +61,7 @@ function get_geometries(geometry::HierarchicalGeometry) end """ - get_active_elements(geometry::HierarchicalGeometry) + get_active_elements(geometry::HierarchicalGeometry) Returns the `Hierarchy.ActiveInfo` object defining the active elements of the hierarchical `geometry`. @@ -72,7 +73,7 @@ function get_active_elements(geometry::HierarchicalGeometry) end """ - get_level_geometry(geometry::HierarchicalGeometry, level::Int) + get_level_geometry(geometry::HierarchicalGeometry, level::Int) Returns the full geometry associated with the given `level`. """ @@ -81,7 +82,7 @@ function get_level_geometry(geometry::HierarchicalGeometry, level::Int) end """ - get_num_levels(geometry::HierarchicalGeometry) + get_num_levels(geometry::HierarchicalGeometry) Returns the number of levels of the hierarchical `geometry`. """ @@ -116,20 +117,22 @@ end ############################################################################################ """ - convert_to_level_and_level_id(geometry::HierarchicalGeometry, element_id::Int) + convert_to_level_and_level_id(geometry::HierarchicalGeometry, element_id::Int) Returns the `level` and `level_id` of the `element_id` in hierarchical indexing. See also [`Hierarchy.convert_to_level_and_level_id`](@ref). # Arguments -- `geometry::HierarchicalGeometry`: The hierarchical geometry. -- `element_id::Int`: The hierarchical indexing of the given element. + + - `geometry::HierarchicalGeometry`: The hierarchical geometry. + - `element_id::Int`: The hierarchical indexing of the given element. # Returns -- `level::Int`: The level to which the element given by `element_id` corresponds to. -- `level_id::Int`: The index to which the element given by `element_id` corresponds to in - geometry at the determined `level`. + + - `level::Int`: The level to which the element given by `element_id` corresponds to. + - `level_id::Int`: The index to which the element given by `element_id` corresponds to in + geometry at the determined `level`. """ function convert_to_level_and_level_id(geometry::HierarchicalGeometry, element_id::Int) level, level_id = Hierarchy.convert_to_level_and_level_id( diff --git a/src/Geometry/MappedGeometry.jl b/src/Geometry/MappedGeometry.jl index 906536fb7..921f278ab 100644 --- a/src/Geometry/MappedGeometry.jl +++ b/src/Geometry/MappedGeometry.jl @@ -36,10 +36,12 @@ end Returns the dimension of the domain manifold of the mapping. # Arguments -- `::Mapping{manifold_dim, image_dim}`: The mapping structure. + + - `::Mapping{manifold_dim, image_dim}`: The mapping structure. # Returns -- `::Int`: The dimension of the domain manifold. + + - `::Int`: The dimension of the domain manifold. """ function get_manifold_dim( ::Mapping{manifold_dim, image_dim} @@ -53,10 +55,12 @@ end Returns the dimension of the image manifold of the mapping. # Arguments -- `::Mapping{manifold_dim, image_dim}`: The mapping structure. + + - `::Mapping{manifold_dim, image_dim}`: The mapping structure. # Returns -- `::Int`: The dimension of the image manifold. + + - `::Int`: The dimension of the image manifold. """ function get_image_dim(::Mapping{manifold_dim, image_dim}) where {manifold_dim, image_dim} return image_dim @@ -68,11 +72,13 @@ end Evaluates the mappping of the points `x` from the parametric space to physical space. # Arguments -- `mapping::Mapping`: The mapping defining the transformation of the points `x`. -- `x::Matrix{Float64}`: The points in parametric space to be mapped. + + - `mapping::Mapping`: The mapping defining the transformation of the points `x`. + - `x::Matrix{Float64}`: The points in parametric space to be mapped. # Returns -- `::Matrix{Float64}`: The mapped points in physical space. The size of the matrix is + + - `::Matrix{Float64}`: The mapped points in physical space. The size of the matrix is `(num_points, image_dim)`, where `num_points` is the number of rows in `x` and `image_dim` is the dimension of the mapped points. """ @@ -129,7 +135,7 @@ struct MappedGeometry{manifold_dim, image_dim, num_patches, G, Map} <: M <: NTuple{num_patches, AbstractMapping{manifold_dim, image_dim}}, } num_elements_per_patch = ntuple(num_patches) do geo_i - get_num_elements(geometry[geo_i]) + return get_num_elements(geometry[geo_i]) end return new{manifold_dim, image_dim, num_patches, G, M}( @@ -192,7 +198,7 @@ struct MappedGeometry{manifold_dim, image_dim, num_patches, G, Map} <: Map <: AbstractMapping{manifold_dim, image_dim}, } num_elements_per_patch = ntuple(num_patches) do geo_i - get_num_elements(geometry[geo_i]) + return get_num_elements(geometry[geo_i]) end return new{manifold_dim, image_dim, num_patches, G, Map}( @@ -344,13 +350,11 @@ function hessian( Jm = jacobian(get_mapping(geometry, base_patch_id), x) Hm = hessian(get_mapping(geometry, base_patch_id), x) - return [ - ntuple(image_dim) do i - Hp = transpose(Jb[p]) * Hm[p][i] * Jb[p] - for j in 1:manifold_dim - Hp += Jm[p][i, j] * Hb[p][j] - end - return Hp - end for p in eachindex(Jb, Jm, Hm, Hb) - ] + return [ntuple(image_dim) do i + Hp = transpose(Jb[p]) * Hm[p][i] * Jb[p] + for j in 1:manifold_dim + Hp += Jm[p][i, j] * Hb[p][j] + end + return Hp + end for p in eachindex(Jb, Jm, Hm, Hb)] end diff --git a/src/Geometry/MaskedGeometry.jl b/src/Geometry/MaskedGeometry.jl index 164fd80f6..b9e2f164b 100644 --- a/src/Geometry/MaskedGeometry.jl +++ b/src/Geometry/MaskedGeometry.jl @@ -10,8 +10,9 @@ geometry `base_geometry` is the original geometry and it is composed with the ev mask `eval_mask`. # Fields -- `base_geometry::G`: The base geometry. -- `eval_mask::AbstractEvaluationMask`: The evaluation mask. + + - `base_geometry::G`: The base geometry. + - `eval_mask::AbstractEvaluationMask`: The evaluation mask. """ struct MaskedGeometry{manifold_dim, image_dim, num_patches, G, M} <: AbstractGeometry{manifold_dim, image_dim, num_patches} diff --git a/src/Geometry/Metric.jl b/src/Geometry/Metric.jl index 394f4d8be..0ec0080b9 100644 --- a/src/Geometry/Metric.jl +++ b/src/Geometry/Metric.jl @@ -9,13 +9,15 @@ Returns the metric and its determinant. # Arguments -- `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry. -- `element_id::Int`: Global element id. -- `xi::Points.AbstractPoints{manifold_dim}`: Evaluation points in the canonical domain. + + - `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry. + - `element_id::Int`: Global element id. + - `xi::Points.AbstractPoints{manifold_dim}`: Evaluation points in the canonical domain. # Returns -- `g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Metric tensor per evaluation point. -- `sqrt_g::Vector{Float64}`: Square-root of the determinant of the metric per evaluation + + - `g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Metric tensor per evaluation point. + - `sqrt_g::Vector{Float64}`: Square-root of the determinant of the metric per evaluation point. """ function metric( @@ -48,15 +50,17 @@ end Returns the inverse metric, the metric and its determinant. # Arguments -- `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry. -- `element_id::Int`: Global element id. -- `xi::Points.AbstractPoints{manifold_dim}`: Evaluation points in the canonical domain. + + - `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry. + - `element_id::Int`: Global element id. + - `xi::Points.AbstractPoints{manifold_dim}`: Evaluation points in the canonical domain. # Returns -- `::Vector{SMatrix{manifold_dim, manifold_dim}}`: Inverse metric tensor per evaluation + + - `::Vector{SMatrix{manifold_dim, manifold_dim}}`: Inverse metric tensor per evaluation point. -- `g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Metric tensor per evaluation point. -- `sqrt_g::Vector{Float64}`: Square-root of the determinant of the metric per evaluation + - `g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Metric tensor per evaluation point. + - `sqrt_g::Vector{Float64}`: Square-root of the determinant of the metric per evaluation point. """ function inv_metric( @@ -79,22 +83,24 @@ end Returns the inverse metric, the metric and its determinant. # Arguments -- `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry. -- `element_id::Int`: Global element id. -- `xi::Points.AbstractPoints{manifold_dim}`: Evaluation points in the canonical domain. + + - `geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}`: The geometry. + - `element_id::Int`: Global element id. + - `xi::Points.AbstractPoints{manifold_dim}`: Evaluation points in the canonical domain. # Returns -- `J::Vector{SMatrix{image_dim, manifold_dim}}`: Jacobian per point. -- `inv_g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Inverse metric tensor per point. -- `g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Metric tensor per point. -- `sqrt_g::Vector{Float64}`: Square-root of the determinant of the metric per point. -- `dgdxs::NTuple{manifold_dim, Vector{SMatrix{manifold_dim, manifold_dim}}}`: Derivative of + + - `J::Vector{SMatrix{image_dim, manifold_dim}}`: Jacobian per point. + - `inv_g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Inverse metric tensor per point. + - `g::Vector{SMatrix{manifold_dim, manifold_dim}}`: Metric tensor per point. + - `sqrt_g::Vector{Float64}`: Square-root of the determinant of the metric per point. + - `dgdxs::NTuple{manifold_dim, Vector{SMatrix{manifold_dim, manifold_dim}}}`: Derivative of the metric tensor per direction in manifold_dim. -- `dinv_g_dxs::NTuple{manifold_dim, Vector{SMatrix{manifold_dim, manifold_dim}}}`: + - `dinv_g_dxs::NTuple{manifold_dim, Vector{SMatrix{manifold_dim, manifold_dim}}}`: Derivative of the inverse metric tensor per direction in manifold_dim. -- `dsqrt_g_dxs::NTuple{manifold_dim, Vector{Float64}`: Derivative of the square-root of the + - `dsqrt_g_dxs::NTuple{manifold_dim, Vector{Float64}`: Derivative of the square-root of the determinant of the metric tensor per direction in manifold_dim. -- `Hs::Vector{NTuple{image_dim, SMatrix{manifold_dim, manifold_dim}}}`: Hessians per point. + - `Hs::Vector{NTuple{image_dim, SMatrix{manifold_dim, manifold_dim}}}`: Hessians per point. """ function metric_derivatives( geometry::AbstractGeometry{manifold_dim, image_dim, num_patches}, diff --git a/src/Geometry/TensorProductGeometry.jl b/src/Geometry/TensorProductGeometry.jl index e0d8d8135..9a539af03 100644 --- a/src/Geometry/TensorProductGeometry.jl +++ b/src/Geometry/TensorProductGeometry.jl @@ -1,18 +1,19 @@ """ - TensorProductGeometry{ - manifold_dim, image_dim, num_patches, num_geometries, T, CI, LI - } <: AbstractGeometry{manifold_dim, image_dim, num_patches} + TensorProductGeometry{ + manifold_dim, image_dim, num_patches, num_geometries, T, CI, LI + } <: AbstractGeometry{manifold_dim, image_dim, num_patches} A geometry build by tensoring multiple constituent geometries. # Fields -- `geometries::T`: Tuple of constituent geometries. -- `cart_num_elements::CI`: A collection of Cartesian indices representing the - multi-dimensional positions of each tensor product element. Useful to convert from - linear to Cartesian indexing. -- `lin_num_elements::LI`: A collection of linear indices representing the global indexing of - each tensor product element. Useful to convert from Cartesian to linear indexing. -- `num_elements_per_patch::NTuple{num_patches, Int}`: The number of elements on each patch. + + - `geometries::T`: Tuple of constituent geometries. + - `cart_num_elements::CI`: A collection of Cartesian indices representing the + multi-dimensional positions of each tensor product element. Useful to convert from + linear to Cartesian indexing. + - `lin_num_elements::LI`: A collection of linear indices representing the global indexing of + each tensor product element. Useful to convert from Cartesian to linear indexing. + - `num_elements_per_patch::NTuple{num_patches, Int}`: The number of elements on each patch. """ struct TensorProductGeometry{ manifold_dim, image_dim, num_patches, num_geometries, T, CI, LI @@ -40,7 +41,9 @@ struct TensorProductGeometry{ const_num_patches = map(get_num_patches, geometries) cart_num_patches = CartesianIndices(const_num_patches) num_elements_per_patch = ntuple(num_patches) do patch_id - return prod(map(get_num_elements, geometries, Tuple(cart_num_patches[patch_id]))) + return prod( + map(get_num_elements, geometries, Tuple(cart_num_patches[patch_id])) + ) end return new{ @@ -59,14 +62,14 @@ end # Get properties. """ - get_cart_num_elements(geometry::TensorProductGeometry) + get_cart_num_elements(geometry::TensorProductGeometry) Returns a CartesianIndices iterator of all elements in the geometry. """ get_cart_num_elements(geometry::TensorProductGeometry) = geometry.cart_num_elements """ - get_lin_num_elements(geometry::TensorProductGeometry) + get_lin_num_elements(geometry::TensorProductGeometry) Returns a LinearIndices iterator of all elements in the geometry. """ @@ -315,7 +318,7 @@ function jacobian( setindex!( Jp[], jac_i, const_image_indices[geo_id[]], const_manifold_indices[geo_id[]] ) - geo_id[] += 1 + return geo_id[] += 1 end J[point] = SMatrix{image_dim, manifold_dim}(Jp[]) geo_id[] = 1 @@ -344,7 +347,7 @@ function jacobian( setindex!( Jp[], jac_i, const_image_indices[geo_id[]], const_manifold_indices[geo_id[]] ) - geo_id[] += 1 + return geo_id[] += 1 end J[point] = SMatrix{image_dim, manifold_dim}(Jp[]) geo_id[] = 1 @@ -433,7 +436,7 @@ function _hessian_per_point( const_manifold_indices[geo_id[]], ) end - geo_id[] += 1 + return geo_id[] += 1 end geo_id[] = 1 diff --git a/src/Geometry/UnstructuredGeometry.jl b/src/Geometry/UnstructuredGeometry.jl index b58a350bd..b42316be4 100644 --- a/src/Geometry/UnstructuredGeometry.jl +++ b/src/Geometry/UnstructuredGeometry.jl @@ -6,14 +6,16 @@ A geometry consisting of multiple patches, each with its own geometry. !!! warning "Avoid heterogeneous inputs." + While the constructors allow different types of geometries, it is strongly recommended to use only a few different types. Failing to do so can cause type instabilities and therefore a significant performance penalty. # Fields -- `geometry_per_patch::NTuple{GP, num_patches}`: The geometries for each patch. -- `num_elements::Int`: The total number of elements in the geometry. -- `num_elements_per_patch::NTuple{num_patches, Int}`: The number of elments per patch. + + - `geometry_per_patch::NTuple{GP, num_patches}`: The geometries for each patch. + - `num_elements::Int`: The total number of elements in the geometry. + - `num_elements_per_patch::NTuple{num_patches, Int}`: The number of elments per patch. """ struct UnstructuredGeometry{manifold_dim, image_dim, num_patches, GT} <: AbstractGeometry{manifold_dim, image_dim, num_patches} @@ -30,7 +32,7 @@ struct UnstructuredGeometry{manifold_dim, image_dim, num_patches, GT} <: GT <: NTuple{num_patches, AbstractGeometry{manifold_dim, image_dim, 1}}, } num_elements_per_patch = ntuple(num_patches) do i - get_num_elements(geometry_per_patch[i]) + return get_num_elements(geometry_per_patch[i]) end num_elements = sum(num_elements_per_patch) diff --git a/src/Hierarchy/ActiveInfo.jl b/src/Hierarchy/ActiveInfo.jl index bc29c59d3..d416b740d 100644 --- a/src/Hierarchy/ActiveInfo.jl +++ b/src/Hierarchy/ActiveInfo.jl @@ -10,10 +10,11 @@ the hierarchical space is such that the index of an object in level `l-1` is alw than that of an object in level `l`. # Fields -- `level_ids::Vector{Vector{Int}}`: Per level collection of active objects. + + - `level_ids::Vector{Vector{Int}}`: Per level collection of active objects. `level_ids[l][i]` gives the id in level `l` of the object indicated by `i`, not the hierarchical id of the overall set of objects. -- `level_cum_num_ids::Vector{Int}`: Total number of active objects up to a certain level, + - `level_cum_num_ids::Vector{Int}`: Total number of active objects up to a certain level, i.e. `level_cum_num_ids[l]=sum(length.(level_ids[1:l-1]))`. First entry is always 0 for ease of use. """ @@ -81,7 +82,7 @@ function convert_to_level_id(active_info::ActiveInfo, hier_id::Int) end """ - convert_to_level_and_level_id(active_info::ActiveInfo, hier_id::Int) + convert_to_level_and_level_id(active_info::ActiveInfo, hier_id::Int) Returns the `level` and `level_id` that correspond to hierarchical index `hier_id`. """ diff --git a/src/Hierarchy/Hierarchy.jl b/src/Hierarchy/Hierarchy.jl index 5d1b990b8..7438cd0ed 100644 --- a/src/Hierarchy/Hierarchy.jl +++ b/src/Hierarchy/Hierarchy.jl @@ -1,5 +1,5 @@ """ - module Hierarchy + module Hierarchy Contains all methods related with hierarchies that are object-agnostic. In essence, the methods in this module provide a base, general logic for hierarchical refinements — be those diff --git a/src/Mantis.jl b/src/Mantis.jl index 587bd2529..658ce5231 100644 --- a/src/Mantis.jl +++ b/src/Mantis.jl @@ -41,8 +41,8 @@ export get_label, get_num_basis, get_coefficients # Exports from TimeIntegrators using .TimeIntegrators -export define_explicit_ode, define_diagonally_implicit_ode, define_implicit_ode, - define_imex_ode +export define_explicit_ode, + define_diagonally_implicit_ode, define_implicit_ode, define_imex_ode export get_solution, initialise_scheme, time_integrate, time_integrate! end diff --git a/src/Mesh/Mesh.jl b/src/Mesh/Mesh.jl index 3ae79932e..ffcb689ad 100644 --- a/src/Mesh/Mesh.jl +++ b/src/Mesh/Mesh.jl @@ -15,23 +15,29 @@ module Mesh Is a 1-dimensional patch, so only the breakpoints. # Fields -- `breakpoints::Vector{Float64}`: Breakpoints. Should be strictly increasing. + + - `breakpoints::Vector{Float64}`: Breakpoints. Should be strictly increasing. # Extended help # Error behaviour + The strictly increasing property of the breakpoints is checked. An `ArgumentError` is raised if the breakpoints are not strictly increasing. """ struct Patch1D breakpoints::Vector{Float64} function Patch1D(breakpoints::Vector{Float64}) - for i in eachindex(breakpoints)[begin:end-1] - if breakpoints[i] >= breakpoints[i+1] - throw(ArgumentError("Breakpoints should be strictly increasing but they are not.")) + for i in eachindex(breakpoints)[begin:(end - 1)] + if breakpoints[i] >= breakpoints[i + 1] + throw( + ArgumentError( + "Breakpoints should be strictly increasing but they are not." + ), + ) end end - new(breakpoints) + return new(breakpoints) end function Patch1D(breakpoint::Float64) return Patch1D([breakpoint]) @@ -48,11 +54,13 @@ end Is an n-dimensional **structured** tensor-product patch. # Fields -- `patches_per_dim::NTuple{n, Patch1D}`: Patches per dimension. Should be strictly increasing. + + - `patches_per_dim::NTuple{n, Patch1D}`: Patches per dimension. Should be strictly increasing. # Extended help # Implementation + The patch is assumed to be a tensor-product patch of quadilaterals. This is also reflected in the implementation. """ @@ -61,7 +69,7 @@ struct Patch{n} # Inner constructor(s) function Patch(breakpoints::NTuple{n, Vector{Float64}}) where {n} - new{n}(NTuple{n, Patch1D}(Patch1D(breakpoints[d]) for d in 1:1:n)) + return new{n}(NTuple{n, Patch1D}(Patch1D(breakpoints[d]) for d in 1:1:n)) end end @@ -77,17 +85,21 @@ end Returns the breakpoints per dimension. # Arguments -- `patch::Patch{n}`: Patch of which to get the breakpoints. + + - `patch::Patch{n}`: Patch of which to get the breakpoints. # Returns -- `::NTuple{n, Vector{Float64}}`: Breakpoints per dimension. + + - `::NTuple{n, Vector{Float64}}`: Breakpoints per dimension. """ function get_breakpoints(patch::Patch{n}) where {n} - return NTuple{n, Vector{Float64}}(get_breakpoints(patch.patches_per_dim[d]) for d in 1:1:n) + return NTuple{n, Vector{Float64}}( + get_breakpoints(patch.patches_per_dim[d]) for d in 1:1:n + ) end function get_element_measure(patch::Patch1D, element_id::Int) - return patch.breakpoints[element_id+1]-patch.breakpoints[element_id] + return patch.breakpoints[element_id + 1]-patch.breakpoints[element_id] end """ @@ -96,14 +108,16 @@ end Returns the vertices of the element specified by `element_id`. # Arguments -- `patch::Patch1D`: The 1-dimensional patch. -- `element_id::Int`: The id of the element. + + - `patch::Patch1D`: The 1-dimensional patch. + - `element_id::Int`: The id of the element. # Returns -- `::NTuple{1, Vector{Float64}}`: The vertices of the element. + + - `::NTuple{1, Vector{Float64}}`: The vertices of the element. """ function get_element_vertices(patch::Patch1D, element_id::Int) - return ([patch.breakpoints[element_id],patch.breakpoints[element_id+1]],) + return ([patch.breakpoints[element_id], patch.breakpoints[element_id + 1]],) end """ @@ -112,11 +126,13 @@ end Returns the breakpoints in dimension `d`. # Arguments -- `patch::Patch{n}`: Patch of which to get the breakpoints. -- `d::Int`: Dimension in which to get the size. + + - `patch::Patch{n}`: Patch of which to get the breakpoints. + - `d::Int`: Dimension in which to get the size. # Returns -- `::Vector{Float64}`: Breakpoints in dimension `d`. + + - `::Vector{Float64}`: Breakpoints in dimension `d`. """ function get_breakpoints(patch::Patch{n}, d::Int) where {n} return get_breakpoints(patch.patches_per_dim[d]) @@ -131,13 +147,15 @@ Redefinition of `Base.size` for a `Patch`. This ensures that one can call `size(patch)` to get the size of the patch. # Arguments -- `patch::Patch{n}`: Patch of which to get the size. + + - `patch::Patch{n}`: Patch of which to get the size. # Returns -- `::NTuple{n, Int}`: Tuple of the number of elements per dimension. + + - `::NTuple{n, Int}`: Tuple of the number of elements per dimension. """ function Base.size(patch::Patch{n}) where {n} - return NTuple{n, Int}(length(get_breakpoints(patch)[i]) - 1 for i = 1:1:n) + return NTuple{n, Int}(length(get_breakpoints(patch)[i]) - 1 for i in 1:1:n) end """ @@ -149,11 +167,13 @@ Redefinition of `Base.size` for a `Patch`. This ensures that one can call `size(patch, d)` to get the size of the patch. # Arguments -- `patch::Patch{n}`: Patch of which to get the size. -- `d::Int`: Dimension in which to get the size. + + - `patch::Patch{n}`: Patch of which to get the size. + - `d::Int`: Dimension in which to get the size. # Returns -- `::Int`: Number of elements in dimension `d`. + + - `::Int`: Number of elements in dimension `d`. """ function Base.size(patch::Patch{n}, d::Int) where {n} return length(get_breakpoints(patch, d)) - 1 @@ -168,10 +188,12 @@ Redefinition of `Base.size` for a `Patch1D`. This ensures that one can call `size(patch_1d)` to get the size of the patch. # Arguments -- `patch_1d::Patch1D`: Patch of which to get the size. + + - `patch_1d::Patch1D`: Patch of which to get the size. # Returns -- `::Int`: Number of elements in the patch. + + - `::Int`: Number of elements in the patch. """ function Base.size(patch_1d::Patch1D) return length(get_breakpoints(patch_1d)) - 1 @@ -186,20 +208,23 @@ The element ids are computed on-the-fly by the iterator and are neither precomputed nor stored. # Arguments -- `patch::Patch{n}`: Patch to get the element ids for. + + - `patch::Patch{n}`: Patch to get the element ids for. # Returns -- `::Base.Iterators.ProductIterator{NTuple{n, StepRange{Int, Int}}}`: Iterator of NTuples of element ids. + + - `::Base.Iterators.ProductIterator{NTuple{n, StepRange{Int, Int}}}`: Iterator of NTuples of element ids. """ function get_element_ids(patch::Patch{n}) where {n} - return Iterators.product(NTuple{n,StepRange{Int,Int}}(1:1:nei for nei in size(patch))...) + return Iterators.product( + NTuple{n, StepRange{Int, Int}}(1:1:nei for nei in size(patch))... + ) end function get_element(patch::Patch1D, element_id::Int) - return @view get_breakpoints(patch)[element_id:element_id+1] + return @view get_breakpoints(patch)[element_id:(element_id + 1)] end - # TopologyFunctionality include("./MeshTopology.jl") diff --git a/src/Mesh/MeshTopology.jl b/src/Mesh/MeshTopology.jl index 51dc627b3..19a8e41ab 100644 --- a/src/Mesh/MeshTopology.jl +++ b/src/Mesh/MeshTopology.jl @@ -135,10 +135,11 @@ tuple. Supports dimensions 1 to 3. Logical positions are defined based on assumption of tensor product patches (lines, quads, hexahedra) and logical coordinate system is as follows: - - -1: located at the leftmost or bottommost position (start of interval of that - dimension) - - 1: located at rightmost or topmost position (end of interval of that dimension) - - 0: extended over that dimension + + - -1: located at the leftmost or bottommost position (start of interval of that + dimension) + - 1: located at rightmost or topmost position (end of interval of that dimension) + - 0: extended over that dimension This means that the start vertex of a line segment is at (-1,), the end vertex is at (1,), and the edge is at (0,). @@ -186,17 +187,19 @@ edges, faces, volumes), and the determination of topological neighbors. Supports 2D (quads), and 3D (hexahedra) topologies. # Fields -- `incidence_relations`: A nested tuple containing the incidence relations between + + - `incidence_relations`: A nested tuple containing the incidence relations between geometric objects of different dimensions. -- `n_geometric_objects`: Total number of global geometric objects per topological dimension. -- `n_local_geometric_objects`: Number of local geometric objects per patch per dimension. -- `local_edge2vertex`: Local edge-to-vertex mapping, `local_edge2vertex[i,j]` contains the + - `n_geometric_objects`: Total number of global geometric objects per topological dimension. + - `n_local_geometric_objects`: Number of local geometric objects per patch per dimension. + - `local_edge2vertex`: Local edge-to-vertex mapping, `local_edge2vertex[i,j]` contains the i-th vertex of the j-th edge, all at local level. -- `local_face2vertex`: Local face-to-vertex mapping, `local_face2vertex[i,j]` contains the + - `local_face2vertex`: Local face-to-vertex mapping, `local_face2vertex[i,j]` contains the i-th vertex of the j-th face, all at local level. # Constructors -- `MeshTopology(patches::Vector{Vector{Int}})`: Builds the patch topology from a list of + + - `MeshTopology(patches::Vector{Vector{Int}})`: Builds the patch topology from a list of patch connectivities (vertex indices). """ struct MeshTopology{manifold_dim, incidence_relations_dim, num_patches} @@ -545,12 +548,13 @@ end compute_face_neighbours(mesh_topology::MeshTopology{3,4}, patch_id::Int, face_local_id::Int) Returns a `4 × N` matrix for the face `face_local_id` of `patch_id` containing information - about its neighboring patches: -- Row 1: Neighboring patch ID -- Row 2: Local face ID in the neighbor patch -- Row 3: Rotation (number of vertices shifted), numbering of neighbour face dofs must be rotated +about its neighboring patches: + + - Row 1: Neighboring patch ID + - Row 2: Local face ID in the neighbor patch + - Row 3: Rotation (number of vertices shifted), numbering of neighbour face dofs must be rotated clockwise 90 degrees as many times are rotation. -- Row 4: Orientation (+1 if aligned, -1 if reversed), indicates if the axis of the face in the neighbor patch + - Row 4: Orientation (+1 if aligned, -1 if reversed), indicates if the axis of the face in the neighbor patch is aligned with the axis of the face in the current patch, if not, the dof numbering must be transposed. Only applicable to 3D hexahedral meshes. @@ -721,10 +725,11 @@ end Returns a `4 × N` matrix describing the neighboring patches across edge `edge_local_id` of `patch_id`. Each column encodes: -- Row 1: Neighboring patch ID. -- Row 2: Local edge ID in neighbor. -- Row 3: Always 0 (edges do not rotate), only orientation needs to be considered. -- Row 4: Orientation (+1 or -1), this indicates whether you need to apply `reverse` on edge numbering or not. + + - Row 1: Neighboring patch ID. + - Row 2: Local edge ID in neighbor. + - Row 3: Always 0 (edges do not rotate), only orientation needs to be considered. + - Row 4: Orientation (+1 or -1), this indicates whether you need to apply `reverse` on edge numbering or not. Only applicable to 2D quadrilateral meshes. """ @@ -821,10 +826,11 @@ end Same as the 2D version, but for 3D hexahedral meshes. Computes edge neighbors of a specific patch edge. Returns a `4 × N` matrix describing the neighboring patches across edge `edge_local_id` of `patch_id`. Each column encodes: -- Row 1: Neighboring patch ID. -- Row 2: Local edge ID in neighbor. -- Row 3: Always 0 (edges do not rotate), only orientation needs to be considered. -- Row 4: Orientation (+1 or -1). + + - Row 1: Neighboring patch ID. + - Row 2: Local edge ID in neighbor. + - Row 3: Always 0 (edges do not rotate), only orientation needs to be considered. + - Row 4: Orientation (+1 or -1). Only applicable to 3D hexahedral meshes. """ @@ -918,7 +924,7 @@ end compute_edge_neighbours(mesh_topology::MeshTopology) Returns a matrix containing neighbor information for all edges of all patches. - Each entry `[i,j]` corresponds to the result of `compute_edge_neighbours(mesh_topology, i, j)`. +Each entry `[i,j]` corresponds to the result of `compute_edge_neighbours(mesh_topology, i, j)`. """ function compute_edge_neighbours( mesh_topology::MT @@ -953,10 +959,11 @@ end compute_vertex_neighbours(mesh_topology::MeshTopology, patch_id::Int, vertex_local_id::Int) Returns a `4 × N` matrix of vertex neighbor data: -- Row 1: Neighboring patch ID. -- Row 2: Local vertex ID in neighbor. -- Row 3: Always 0 (no rotation for vertices). -- Row 4: Always 0 (no orientation. + + - Row 1: Neighboring patch ID. + - Row 2: Local vertex ID in neighbor. + - Row 3: Always 0 (no rotation for vertices). + - Row 4: Always 0 (no orientation. Applicable to all supported topologies. """ diff --git a/src/Plot/PlotHelpers.jl b/src/Plot/PlotHelpers.jl index aaea67dd4..99494fc19 100644 --- a/src/Plot/PlotHelpers.jl +++ b/src/Plot/PlotHelpers.jl @@ -1,21 +1,22 @@ """ export_geometry_to_vtk( - geo::Geometry.AbstractGeometry, - filename::String; - n_subcells::Int=1, - degree::Int=4, - output_directory_tree=[pwd()], - ) + geo::Geometry.AbstractGeometry, + filename::String; + n_subcells::Int=1, + degree::Int=4, + output_directory_tree=[pwd()], + ) Export the geometry to a VTK file. # Arguments -- `geo::Geometry.AbstractGeometry`: The geometry to be exported. -- `filename::String`: The name of the output file. -- `n_subcells::Int`: The number of subcells to be used in the visualization. -- `degree::Int`: The degree of the basis functions used in the visualization. -- `output_directory_tree`: A vector of strings representing the directory tree. Defaults to - the current working directory. + + - `geo::Geometry.AbstractGeometry`: The geometry to be exported. + - `filename::String`: The name of the output file. + - `n_subcells::Int`: The number of subcells to be used in the visualization. + - `degree::Int`: The degree of the basis functions used in the visualization. + - `output_directory_tree`: A vector of strings representing the directory tree. Defaults to + the current working directory. """ function export_geometry_to_vtk( geo::Geometry.AbstractGeometry, @@ -39,24 +40,25 @@ end """ export_form_fields_to_vtk( - form_sols, - var_names, - filename; - n_subcells::Int=1, - degree::Int=4, - output_directory_tree=[pwd()], - ) + form_sols, + var_names, + filename; + n_subcells::Int=1, + degree::Int=4, + output_directory_tree=[pwd()], + ) Export the form solutions to VTK files. # Arguments -- `form_sols::Vector{Forms.AbstractForm}`: The form solutions to be exported. -- `var_names::Vector{String}`: The names of the form solutions. -- `filename::String`: The name of the output file. -- `n_subcells::Int`: The number of subcells to be used in the visualization. -- `degree::Int`: The degree of the basis functions used in the visualization. -- `output_directory_tree`: A vector of strings representing the directory tree. Defaults to - the current working directory. + + - `form_sols::Vector{Forms.AbstractForm}`: The form solutions to be exported. + - `var_names::Vector{String}`: The names of the form solutions. + - `filename::String`: The name of the output file. + - `n_subcells::Int`: The number of subcells to be used in the visualization. + - `degree::Int`: The degree of the basis functions used in the visualization. + - `output_directory_tree`: A vector of strings representing the directory tree. Defaults to + the current working directory. """ function export_form_fields_to_vtk( form_sols, @@ -84,17 +86,18 @@ end """ export_form_fields_to_vtk( - form_sols, filename; n_subcells::Int=1, degree::Int=4, output_directory_tree=[pwd()] - ) + form_sols, filename; n_subcells::Int=1, degree::Int=4, output_directory_tree=[pwd()] + ) Export the form solutions to VTK files. # Arguments -- `form_sols::Vector{Forms.AbstractForm}`: The form solutions to be exported. -- `filename::String`: The name of the output file. -- `n_subcells::Int`: The number of subcells to be used in the visualization. -- `degree::Int`: The degree of the basis functions used in the visualization. - output_directory_tree=[pwd()], + + - `form_sols::Vector{Forms.AbstractForm}`: The form solutions to be exported. + - `filename::String`: The name of the output file. + - `n_subcells::Int`: The number of subcells to be used in the visualization. + - `degree::Int`: The degree of the basis functions used in the visualization. + output_directory_tree=[pwd()], """ function export_form_fields_to_vtk( form_sols, filename; n_subcells::Int=1, degree::Int=4, output_directory_tree=[pwd()] diff --git a/src/Plot/PlotKernel.jl b/src/Plot/PlotKernel.jl index 276abf8c7..4ad44c78d 100644 --- a/src/Plot/PlotKernel.jl +++ b/src/Plot/PlotKernel.jl @@ -49,7 +49,7 @@ function _plot( # vertices_el[:,i,j,k] .= vector_output_of_evaluate for element_idx in 1:n_total_elements - for subcell_idx in 1:(n_subcells^manifold_dim) + for subcell_idx in 1:(n_subcells ^ manifold_dim) ξ_shift_per_dir = dξ .* (Tuple(subcell_cartesian_idx[subcell_idx]) .- 1) ξ = Points.CartesianPoints( ntuple(dim -> ξ_shift_per_dir[dim] .+ dξ .* ξ_ref_per_dir, manifold_dim) @@ -93,7 +93,7 @@ function _plot( compress=compress, vtkversion=:latest, ) do vtk - vtk.version == "2.2" + return vtk.version == "2.2" end ################################### @@ -175,7 +175,7 @@ function _plot( compress=compress, vtkversion=:latest, ) do vtk - vtk.version == "2.2" + return vtk.version == "2.2" end end end @@ -293,8 +293,9 @@ function _plot( end if !isnothing(offset) - point_data[:, vertex_offset .+ (1:n_vertices_per_subcell)] .-= - offset(vertices[:, vertex_offset .+ (1:n_vertices_per_subcell)]')' + point_data[:, vertex_offset .+ (1:n_vertices_per_subcell)] .-= offset( + vertices[:, vertex_offset .+ (1:n_vertices_per_subcell)]' + )' end # Add cell @@ -330,7 +331,7 @@ function _plot( vtkversion=:latest, ) do vtk vtk.version == "2.2" - vtk[Forms.get_label(form), WriteVTK.VTKPointData()] = point_data + return vtk[Forms.get_label(form), WriteVTK.VTKPointData()] = point_data end ################################### @@ -412,7 +413,7 @@ function _plot( compress=compress, vtkversion=:latest, ) do vtk - vtk.version == "2.2" + return vtk.version == "2.2" end end end diff --git a/src/Points/CartesianPoints.jl b/src/Points/CartesianPoints.jl index b4cd8c4b8..6fb78d2a6 100644 --- a/src/Points/CartesianPoints.jl +++ b/src/Points/CartesianPoints.jl @@ -6,20 +6,22 @@ Conceptually, this structure combines the functionalities of `CartesianIndices` `Iterators.product`. # Fields -- `constituent_points::CP`: The set of points per manifold dimension. -- `cart_num_points::CI`: The `CartesianIndices` used to convert from linear to cartesian + + - `constituent_points::CP`: The set of points per manifold dimension. + - `cart_num_points::CI`: The `CartesianIndices` used to convert from linear to cartesian indexing. -- `lin_num_points::LI`: The `LinearIndices` used to convert from cartesian to linear + - `lin_num_points::LI`: The `LinearIndices` used to convert from cartesian to linear indexing. -- `iteration_order::NTuple{manifold_dim, Int}`: Used to determine the iteration order over + - `iteration_order::NTuple{manifold_dim, Int}`: Used to determine the iteration order over `cart_num_points`. If the `dim`-th entry has value `i`, then dimension `dim` will be the `i`-th fastest changing index. -- `permuted_cart_num_points::CI`: A permuted version of `cart_num_points` as given by + - `permuted_cart_num_points::CI`: A permuted version of `cart_num_points` as given by `iteration_order`. # Example + ```julia -julia> points = Points.CartesianPoints([1,2], [1,2,3]; iteration_order=(1,2)); +julia> points = Points.CartesianPoints([1, 2], [1, 2, 3]; iteration_order=(1, 2)); julia> for point in points display(point) @@ -31,7 +33,7 @@ julia> for point in points (1, 3) (2, 3) -julia> points = Points.CartesianPoints([1,2], [1,2,3]; iteration_order=(2,1)); +julia> points = Points.CartesianPoints([1, 2], [1, 2, 3]; iteration_order=(2, 1)); julia> for point in points display(point) @@ -115,14 +117,14 @@ Returns the `LinearIndices` used to convert from cartesian to linear indexing. get_lin_num_points(points::CartesianPoints) = points.lin_num_points """ - get_iteration_order(points::CartesianPoints) + get_iteration_order(points::CartesianPoints) Returns the `iteration_order` order used to index `points`. """ get_iteration_order(points::CartesianPoints) = points.iteration_order """ - get_permuted_cart_num_points(points::CartesianPoints) + get_permuted_cart_num_points(points::CartesianPoints) Returns the permuted `cart_num_points` used to index `points`, as given by `iteration_order`. diff --git a/src/Points/PointSet.jl b/src/Points/PointSet.jl index 12840fe0c..a02ee237c 100644 --- a/src/Points/PointSet.jl +++ b/src/Points/PointSet.jl @@ -4,7 +4,8 @@ Represents a set of points in `manifold_dim` dimensions. # Fields -- `constituent_points::NTuple{manifold_dim, T}`: The set of points per manifold dimension. + + - `constituent_points::NTuple{manifold_dim, T}`: The set of points per manifold dimension. """ struct PointSet{manifold_dim, T, CP} <: AbstractPoints{manifold_dim, T} constituent_points::CP diff --git a/src/Points/Points.jl b/src/Points/Points.jl index f9c978b2d..79312a2af 100644 --- a/src/Points/Points.jl +++ b/src/Points/Points.jl @@ -16,8 +16,9 @@ module Points Supertype for all evaluable points. # Type parameters -- `manifold_dim`: Dimension of the manifold where the points are evaluated. -- `T`: The `eltype` of the points; see `Base.eltype`. + + - `manifold_dim`: Dimension of the manifold where the points are evaluated. + - `T`: The `eltype` of the points; see `Base.eltype`. """ abstract type AbstractPoints{manifold_dim, T} end @@ -38,7 +39,7 @@ get_manifold_dim(::AbstractPoints{manifold_dim}) where {manifold_dim} = manifold Returns the number of of evaluable `points` in the given point structure. """ function get_num_points(::P) where {P <: AbstractPoints} - throw(MethodError(get_num_points, (P,))) + return throw(MethodError(get_num_points, (P,))) end """ @@ -51,25 +52,27 @@ function get_constituent_points(points::P) where {P <: AbstractPoints} end """ - scale_and_shift_points( - points::P, scalings::S, translations::T - ) where { - manifold_dim, - P <: AbstractPoints{manifold_dim}, - S <: NTuple{manifold_dim, Real}, - T <: NTuple{manifold_dim, Real}, - } + scale_and_shift_points( + points::P, scalings::S, translations::T + ) where { + manifold_dim, + P <: AbstractPoints{manifold_dim}, + S <: NTuple{manifold_dim, Real}, + T <: NTuple{manifold_dim, Real}, + } -Applies an affine map defined by `scalings` and `translations` to each point in `points`. +Applies an affine map defined by `scalings` and `translations` to each point in `points`. # Arguments -- `points::P`: The set of points. -- `scalings::S`: The scaling of the affine map. -- `translations::T`: The translation of the affine map. + + - `points::P`: The set of points. + - `scalings::S`: The scaling of the affine map. + - `translations::T`: The translation of the affine map. # Returns -- `transformed_points::P`: The set of transformed points of the same type as the original - `points`. + + - `transformed_points::P`: The set of transformed points of the same type as the original + `points`. """ function scale_and_shift_points( points::P, scalings::S, translations::T diff --git a/src/Quadrature/ElementQuadratureRules/ClenshawCurtis.jl b/src/Quadrature/ElementQuadratureRules/ClenshawCurtis.jl index 332b00714..b8b341cfc 100644 --- a/src/Quadrature/ElementQuadratureRules/ClenshawCurtis.jl +++ b/src/Quadrature/ElementQuadratureRules/ClenshawCurtis.jl @@ -7,13 +7,16 @@ degree `p`. However, in practise, this quadrature rule can obtain results compar Gauss quadrature (in some cases), see [Trefethen2008](@cite) and [Trefethen2022](@cite). # Arguments -- `p::Integer`: Degree of the quadrature rule. + + - `p::Integer`: Degree of the quadrature rule. # Returns -- `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. + + - `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. There will be `p+1` nodes and weights. # Notes + See [Waldvogel2006](@cite) for the algorithm based on fast fourier transforms. The algorithm used here is a direct translation from the given MATLAB code on page 201. """ diff --git a/src/Quadrature/ElementQuadratureRules/ElementQuadratureRules.jl b/src/Quadrature/ElementQuadratureRules/ElementQuadratureRules.jl index 189648771..6ced8b13b 100644 --- a/src/Quadrature/ElementQuadratureRules/ElementQuadratureRules.jl +++ b/src/Quadrature/ElementQuadratureRules/ElementQuadratureRules.jl @@ -4,10 +4,12 @@ Returns the quadrature nodes of a quadrature rule. # Arguments -- `qr::AbstractElementQuadratureRule{manifold_dim}`: Rule to get the nodes from. + + - `qr::AbstractElementQuadratureRule{manifold_dim}`: Rule to get the nodes from. # Returns -- `nodes::NTuple{manifold_dim, Vector{Float64}}`: Nodes of the quadrature rule. + + - `nodes::NTuple{manifold_dim, Vector{Float64}}`: Nodes of the quadrature rule. """ function get_nodes(qr::AbstractElementQuadratureRule{manifold_dim}) where {manifold_dim} return qr.nodes @@ -19,10 +21,12 @@ end Returns the quadrature weights of a quadrature rule. # Arguments -- `qr::AbstractElementQuadratureRule{manifold_dim}`: Rule to get the weights from. + + - `qr::AbstractElementQuadratureRule{manifold_dim}`: Rule to get the weights from. # Returns -- `weights::Vector{Float64}`: Weights of the quadrature rule. + + - `weights::Vector{Float64}`: Weights of the quadrature rule. """ function get_weights(qr::AbstractElementQuadratureRule{manifold_dim}) where {manifold_dim} return qr.weights @@ -34,10 +38,12 @@ end Returns the label of a quadrature rule. # Arguments -- `qr::AbstractElementQuadratureRule{manifold_dim}`: Rule to get the label from. + + - `qr::AbstractElementQuadratureRule{manifold_dim}`: Rule to get the label from. # Returns -- `rule_label::String`: Label of the quadrature rule. + + - `rule_label::String`: Label of the quadrature rule. """ function get_label(qr::AbstractElementQuadratureRule{manifold_dim}) where {manifold_dim} return qr.rule_label @@ -49,27 +55,29 @@ end Represents a quadrature rule on a canonical element of dimension `manifold_dim`. # Fields -- `nodes::NTuple{manifold_dim, Vector{Float64}}`: Quadrature nodes per dimension. -- `weights::Vector{Float64}`: Tensor product of quadrature rules. The shape is consistent + + - `nodes::NTuple{manifold_dim, Vector{Float64}}`: Quadrature nodes per dimension. + - `weights::Vector{Float64}`: Tensor product of quadrature rules. The shape is consistent with the output of the evaluate methods for `FunctionSpaces`. -- `rule_label::String`: Name or type of quadrature rule. Used for human verification. + - `rule_label::String`: Name or type of quadrature rule. Used for human verification. # Type parameters -- `manifold_dim`: Dimension of the domain + + - `manifold_dim`: Dimension of the domain # Inner Constructors -- `CanonicalQuadratureRule(nodes::NTuple{manifold_dim, Vector{Float64}}, weights::Vector{Float64})`: + + - `CanonicalQuadratureRule(nodes::NTuple{manifold_dim, Vector{Float64}}, weights::Vector{Float64})`: General constructor. # Outer Constructors -- [`gauss_lobatto`](@ref). -- [`gauss_legendre`](@ref). -- [`clenshaw_curtis`](@ref). -- [`newton_cotes`](@ref). -- [`tensor_product_rule(p::NTuple{manifold_dim, Integer}, quad_rule::F, rule_args_1d...) where - {manifold_dim, F <: Function}`](@ref). -- [`tensor_product_rule(qrules_1d::NTuple{manifold_dim, CanonicalQuadratureRule{1}}) where - {manifold_dim}`](@ref). + + - [`gauss_lobatto`](@ref). + - [`gauss_legendre`](@ref). + - [`clenshaw_curtis`](@ref). + - [`newton_cotes`](@ref). + - [`tensor_product_rule(p::NTuple{manifold_dim, Integer}, quad_rule::F, rule_args_1d...) where {manifold_dim, F <: Function}`](@ref). + - [`tensor_product_rule(qrules_1d::NTuple{manifold_dim, CanonicalQuadratureRule{1}}) where {manifold_dim}`](@ref). """ struct CanonicalQuadratureRule{manifold_dim, P} <: AbstractElementQuadratureRule{manifold_dim} diff --git a/src/Quadrature/ElementQuadratureRules/Gauss.jl b/src/Quadrature/ElementQuadratureRules/Gauss.jl index 656edbc28..b749c4960 100644 --- a/src/Quadrature/ElementQuadratureRules/Gauss.jl +++ b/src/Quadrature/ElementQuadratureRules/Gauss.jl @@ -8,17 +8,21 @@ Note that here the quadrature rule is valid for the interval ``\\xi \\in [0, 1]` of ``\\xi \\in [-1, 1]`` as usual. # Arguments -- `N::Integer`: Number of nodes used in the quadrature rule. + + - `N::Integer`: Number of nodes used in the quadrature rule. # Returns -- `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. + + - `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. There will be `N` nodes and weights. # Throws -- `DomainError`: If `N` is less than or equal to 1. This is handled by the + + - `DomainError`: If `N` is less than or equal to 1. This is handled by the FastGaussQuadrature.jl package. # Notes + Uses the FastGaussQuadrature.jl package. We only linearly map the nodes and weights to the interval [0, 1]. """ @@ -41,16 +45,20 @@ Note that here the quadrature rule is valid for the interval ``\\xi \\in [0, 1]` of ``\\xi \\in [-1, 1]`` as usual. # Arguments -- `N::Integer`: Number of nodes used in the quadrature rule. + + - `N::Integer`: Number of nodes used in the quadrature rule. # Returns -- `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. + + - `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. There will be `N` nodes and weights. # Throws -- `DomainError`: If `N` is less than or equal to zero. + + - `DomainError`: If `N` is less than or equal to zero. # Notes + Uses the FastGaussQuadrature.jl package. We only linearly map the nodes and weights to the interval [0, 1]. """ diff --git a/src/Quadrature/ElementQuadratureRules/NewtonCotes.jl b/src/Quadrature/ElementQuadratureRules/NewtonCotes.jl index 9c0a158de..a1892191f 100644 --- a/src/Quadrature/ElementQuadratureRules/NewtonCotes.jl +++ b/src/Quadrature/ElementQuadratureRules/NewtonCotes.jl @@ -10,11 +10,13 @@ interval, while open Newton-Cotes rules do not. The algorithm used to compute the weights is not the most efficient nor the most accurate. # Arguments -- `num_points::Integer`: Number of points in the quadrature rule. -- `type::String`: Type of the Newton-Cotes rule. Valid types are "closed" and "open". + + - `num_points::Integer`: Number of points in the quadrature rule. + - `type::String`: Type of the Newton-Cotes rule. Valid types are "closed" and "open". # Returns -- `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. + + - `::CanonicalQuadratureRule{1}`: 1 dimensional quadrature rule containing the nodes and weights. """ function newton_cotes(num_points::Integer, type::String) # Compute the equally spaced nodes on the interval [-1, 1]. diff --git a/src/Quadrature/ElementQuadratureRules/TensorProduct.jl b/src/Quadrature/ElementQuadratureRules/TensorProduct.jl index ee09d6717..5d8dc7063 100644 --- a/src/Quadrature/ElementQuadratureRules/TensorProduct.jl +++ b/src/Quadrature/ElementQuadratureRules/TensorProduct.jl @@ -6,13 +6,15 @@ Returns a tensor product quadrature rule of given degree and rule type. # Arguments -- `p::NTuple{manifold_dim, Integer}`: Degree of the quadrature rule per dimension. -- `quad_rule::F`: The function that returns a `CanonicalQuadratureRule{1}` given an integer degree. + + - `p::NTuple{manifold_dim, Integer}`: Degree of the quadrature rule per dimension. + - `quad_rule::F`: The function that returns a `CanonicalQuadratureRule{1}` given an integer degree. May take additional arguments. -- `rule_args_1d...`: Additional arguments for the 1D quadrature rule. Optional. + - `rule_args_1d...`: Additional arguments for the 1D quadrature rule. Optional. # Returns -- `::CanonicalQuadratureRule{manifold_dim}`: CanonicalQuadratureRule of the new dimension. + + - `::CanonicalQuadratureRule{manifold_dim}`: CanonicalQuadratureRule of the new dimension. """ function tensor_product_rule( p::NTuple{manifold_dim, Integer}, quad_rule::F, rule_args_1d... @@ -32,10 +34,12 @@ end Returns a tensor product quadrature rule from the given rules. # Arguments -- `qrules_1d::NTuple{manifold_dim, CanonicalQuadratureRule{1}}`: Quadrature rules per dimension. + + - `qrules_1d::NTuple{manifold_dim, CanonicalQuadratureRule{1}}`: Quadrature rules per dimension. # Returns -- `::CanonicalQuadratureRule{manifold_dim}`: CanonicalQuadratureRule of the new dimension. + + - `::CanonicalQuadratureRule{manifold_dim}`: CanonicalQuadratureRule of the new dimension. """ function tensor_product_rule( const_qrules::NTuple{manifold_dim, CanonicalQuadratureRule{1}} @@ -69,10 +73,12 @@ end Compute the tensor product of the given 1D quadrature weights. # Arguments -- `weights_1d::NTuple{manifold_dim, Vector{T}}`: Quadrature weights per dimension. + + - `weights_1d::NTuple{manifold_dim, Vector{T}}`: Quadrature weights per dimension. # Returns -- `::Vector{T}`: Tensor product of the quadrature weights. + + - `::Vector{T}`: Tensor product of the quadrature weights. """ function _compute_tensor_product( weights_1d::NTuple{manifold_dim, Vector{T}} diff --git a/src/Quadrature/GlobalQuadratureRules/GlobalQuadratureRules.jl b/src/Quadrature/GlobalQuadratureRules/GlobalQuadratureRules.jl index 35a53b3b9..50840f7e9 100644 --- a/src/Quadrature/GlobalQuadratureRules/GlobalQuadratureRules.jl +++ b/src/Quadrature/GlobalQuadratureRules/GlobalQuadratureRules.jl @@ -5,10 +5,12 @@ Get the number of quadrature elements in the global quadrature rule. These are t element units on which quadrature is performed. # Arguments -- `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. + + - `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. # Returns -- `Int`: The number of quadrature elements in the global quadrature rule. + + - `Int`: The number of quadrature elements in the global quadrature rule. """ function get_num_evaluation_elements(global_quad_rule::AbstractGlobalQuadratureRule) return global_quad_rule.num_quad_elements @@ -21,10 +23,12 @@ Get the number of base elements in the global quadrature rule. Each base element several sub-elements on which quadrature is performed. # Arguments -- `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. + + - `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. # Returns -- `Int`: The number of base elements in the global quadrature rule. + + - `Int`: The number of base elements in the global quadrature rule. """ function get_num_base_elements(global_quad_rule::AbstractGlobalQuadratureRule) return global_quad_rule.num_base_elements @@ -36,14 +40,18 @@ end Get the indices of the quadrature elements in a given base element. # Arguments -- `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. -- `element_idx::Int`: The index of the base element. + + - `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. + - `element_idx::Int`: The index of the base element. # Returns -- `Vector{Int}`: The indices of the quadrature elements in the specified base element. + + - `Vector{Int}`: The indices of the quadrature elements in the specified base element. """ function get_element_idxs(::AbstractGlobalQuadratureRule, ::Int) - throw(ArgumentError("Method not implemented for this type of global quadrature rule.")) + return throw( + ArgumentError("Method not implemented for this type of global quadrature rule.") + ) end """ @@ -52,11 +60,13 @@ end Get the quadrature rule for a specific quadrature element index . # Arguments -- `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. -- `element_idx::Int`: The index of the quadrature element. + + - `global_quad_rule::AbstractGlobalQuadratureRule`: The global quadrature rule. + - `element_idx::Int`: The index of the quadrature element. # Returns -- `AbstractElementQuadratureRule`: The quadrature rule for the specified quadrature element. + + - `AbstractElementQuadratureRule`: The quadrature rule for the specified quadrature element. """ function get_element_quadrature_rule(::AbstractGlobalQuadratureRule, ::Int) return throw( diff --git a/src/Quadrature/GlobalQuadratureRules/StandardQuadrature.jl b/src/Quadrature/GlobalQuadratureRules/StandardQuadrature.jl index f46a20425..9055b96fd 100644 --- a/src/Quadrature/GlobalQuadratureRules/StandardQuadrature.jl +++ b/src/Quadrature/GlobalQuadratureRules/StandardQuadrature.jl @@ -5,15 +5,18 @@ Represents a standard quadrature rule for a given manifold dimension, where each the same canonical rule `Q`. # Fields -- `canonical_qrule::Q`: The canonical quadrature rule used for the elements. -- `num_elements::Int`: The number of elements in the quadrature rule. + + - `canonical_qrule::Q`: The canonical quadrature rule used for the elements. + - `num_elements::Int`: The number of elements in the quadrature rule. # Type parameters -- `manifold_dim`: Dimension of the domain -- `Q`: Type of the canonical quadrature rule. + + - `manifold_dim`: Dimension of the domain + - `Q`: Type of the canonical quadrature rule. # Inner Constructors -- `StandardQuadrature(canonical_qrule::Q, num_elements::Int)`: Creates a new + + - `StandardQuadrature(canonical_qrule::Q, num_elements::Int)`: Creates a new `StandardQuadrature` instance with the specified canonical quadrature rule and number of elements. """ @@ -34,10 +37,12 @@ end Get the canonical quadrature rule of the standard quadrature. # Arguments -- `standard_quadrature::StandardQuadrature`: The standard quadrature rule. + + - `standard_quadrature::StandardQuadrature`: The standard quadrature rule. # Returns -- `CanonicalQuadratureRule`: The canonical quadrature rule of the standard quadrature. + + - `CanonicalQuadratureRule`: The canonical quadrature rule of the standard quadrature. """ function get_canonical_quadrature_rule(standard_quadrature::StandardQuadrature) return standard_quadrature.canonical_qrule @@ -53,10 +58,12 @@ end Returns the number of base elements in the standard quadrature. # Arguments -- `standard_quadrature::StandardQuadrature`: The standard quadrature rule. + + - `standard_quadrature::StandardQuadrature`: The standard quadrature rule. # Returns -- `::Int`: The number of base elements in the standard quadrature. + + - `::Int`: The number of base elements in the standard quadrature. """ function get_num_base_elements(standard_quadrature::StandardQuadrature) return standard_quadrature.num_elements diff --git a/src/Quadrature/Quadrature.jl b/src/Quadrature/Quadrature.jl index 415c87fda..3c14c27cb 100644 --- a/src/Quadrature/Quadrature.jl +++ b/src/Quadrature/Quadrature.jl @@ -12,7 +12,8 @@ import ..Geometry Abstract type for a quadrature rule on an entire domain of dimension `manifold_dim`. # Type parameters -- `manifold_dim`: Dimension of the domain + + - `manifold_dim`: Dimension of the domain """ abstract type AbstractQuadratureRule{manifold_dim} end diff --git a/src/Quadrature/QuadratureHelpers.jl b/src/Quadrature/QuadratureHelpers.jl index 3ae6c8ec8..b31b24245 100644 --- a/src/Quadrature/QuadratureHelpers.jl +++ b/src/Quadrature/QuadratureHelpers.jl @@ -9,14 +9,16 @@ Returns a tuple of tensor-product quadrature rules, of the type `q_rule`, for th number of quadrature points in each dimension. # Arguments -- `q_rule::Function`: The type of univariate quadrature rule to use. -- `nq_single::NTuple{manifold_dim, Int}`: Number of quadrature points per dimension for the + + - `q_rule::Function`: The type of univariate quadrature rule to use. + - `nq_single::NTuple{manifold_dim, Int}`: Number of quadrature points per dimension for the first quadrature rule. -- `nq_others::NTuple{manifold_dim, Int}...`: Number of quadrature points per dimension for the + - `nq_others::NTuple{manifold_dim, Int}...`: Number of quadrature points per dimension for the other quadrature rules. # Returns -- `::NTuple{num_rules, CanonicalQuadratureRule{manifold_dim}}`: A tuple of quadrature rules where + + - `::NTuple{num_rules, CanonicalQuadratureRule{manifold_dim}}`: A tuple of quadrature rules where `num_rules` is the number of other quadrature rules given plus the single rule. """ function get_canonical_quadrature_rules( @@ -38,15 +40,9 @@ function get_global_quadrature_rules( nq_single::NTuple{manifold_dim, Int}, nq_others::NTuple{manifold_dim, Int}..., ) where {manifold_dim} - q_single = StandardQuadrature( - tensor_product_rule(nq_single, q_rule), - num_elements - ) + q_single = StandardQuadrature(tensor_product_rule(nq_single, q_rule), num_elements) q_others = ntuple(length(nq_others)) do i - return StandardQuadrature( - tensor_product_rule(nq_others[i], q_rule), - num_elements - ) + return StandardQuadrature(tensor_product_rule(nq_others[i], q_rule), num_elements) end return (q_single, q_others...) diff --git a/src/TimeIntegrators/Definitions.jl b/src/TimeIntegrators/Definitions.jl index e4916800d..604bb02c2 100644 --- a/src/TimeIntegrators/Definitions.jl +++ b/src/TimeIntegrators/Definitions.jl @@ -4,9 +4,10 @@ Supertype for all time integrators. # Type parameters -- `num_stages`: The number of stages for a multi-step scheme (such as the Runge-Kutta + + - `num_stages`: The number of stages for a multi-step scheme (such as the Runge-Kutta family). Since every scheme is at least a single-stage scheme, `num_stages` >= 1. -- `num_steps`: The number of steps for a multi-step scheme (such as the Adams-Bashforth + - `num_steps`: The number of steps for a multi-step scheme (such as the Adams-Bashforth family). Since every scheme is at least a single-step scheme, `num_steps` >= 1. """ abstract type AbstractTimeIntegrator{num_stages, num_steps} end @@ -51,25 +52,23 @@ the types of the explicit and implicit functions, which are `Nothing` if not def that at least `EE` or `IS` must be a function. # Constructors -- `define_explicit_ode(explicit_evaluate::Function)`: For fully explicit ODEs. -- `define_diagonally_implicit_ode(implicit_solve::Function)`: For diagonally implicit ODEs. -- `define_implicit_ode(implicit_solve::Function, implicit_evaluate::Function)`: For fully + + - `define_explicit_ode(explicit_evaluate::Function)`: For fully explicit ODEs. + - `define_diagonally_implicit_ode(implicit_solve::Function)`: For diagonally implicit ODEs. + - `define_implicit_ode(implicit_solve::Function, implicit_evaluate::Function)`: For fully implicit ODEs. -- `define_imex_ode(explicit_evaluate::Function, implicit_solve::Function)`: For IMEX ODEs. -- `TimeIntegrationOperators( - explicit_evaluate::Union{Nothing, Function}, - implicit_solve::Union{Nothing, Function}, - implicit_evaluate::Union{Nothing, Function}, - )`: Generic constructor. + - `define_imex_ode(explicit_evaluate::Function, implicit_solve::Function)`: For IMEX ODEs. + - `TimeIntegrationOperators( explicit_evaluate::Union{Nothing, Function}, implicit_solve::Union{Nothing, Function}, implicit_evaluate::Union{Nothing, Function}, )`: Generic constructor. # Fields -- `explicitEvaluate!::EE`: A function that evaluates the explicit part of the ODE, that is, + + - `explicitEvaluate!::EE`: A function that evaluates the explicit part of the ODE, that is, the function that evaluates ``F = f(y, t)``. See the manual section on [TimeIntegrators](@ref) for the terminology. **This function must have the following inputs: (output, yn, t). It must also overwrite the output argument.** The output argument will be a vector-like object of length N (the number of variables), as will yn. The argument t will be a number indicating the current time. -- `implicitSolve!::IS`: A function that solves the implicit part of the ODE, that is, the + - `implicitSolve!::IS`: A function that solves the implicit part of the ODE, that is, the function that solves the equation ``\\mathbf{Y} - h \\mathbf{g}(\\mathbf{Y}) = \\mathbf{x}``, with ``h = a^{IM}_{ii} \\Delta t``. See the manual section on [TimeIntegrators](@ref) for @@ -82,7 +81,7 @@ that at least `EE` or `IS` must be a function. (num_stages, num_stages) (for Implicit integrators) and t will be a number (for DiagonallyImplicit integrators) or an SVector of length num_stages (for Implicit integrators) indicating the current time(s). -- `implicitEvaluate!::IE`: A function that evaluates the implicit part of the ODE, that is, + - `implicitEvaluate!::IE`: A function that evaluates the implicit part of the ODE, that is, the function that evaluates ``G = g(y, t)``. See the manual section on [TimeIntegrators](@ref) for the terminology. **This function must have the following inputs: (output, yn, t). It must also overwrite the output argument.** The output @@ -90,9 +89,10 @@ that at least `EE` or `IS` must be a function. yn, and t will be a number indicating the current time. # Type parameters -- `EE`: `typeof(explicitEvaluate!)` if initialised, `Nothing` otherwise. -- `IS`: `typeof(implicitSolve!)` if initialised, `Nothing` otherwise. -- `IE`: `typeof(implicitEvaluate!)` if initialised, `Nothing` otherwise. + + - `EE`: `typeof(explicitEvaluate!)` if initialised, `Nothing` otherwise. + - `IS`: `typeof(implicitSolve!)` if initialised, `Nothing` otherwise. + - `IE`: `typeof(implicitEvaluate!)` if initialised, `Nothing` otherwise. """ struct TimeIntegrationOperators{EE, IS, IE} explicitEvaluate!::EE @@ -143,8 +143,9 @@ end Creates a [`TimeIntegrationOperators`](@ref) object for a diagonally implicit ODE. The `implicit_evaluate` argument is needed when: -- using a multi-step scheme (for the initialisation process). -- using a diagonally implicit scheme that has at least one zero on the diagonal. + + - using a multi-step scheme (for the initialisation process). + - using a diagonally implicit scheme that has at least one zero on the diagonal. """ function define_diagonally_implicit_ode( implicit_solve::Function, implicit_evaluate::Union{Nothing, Function}=nothing @@ -168,8 +169,9 @@ end Creates a [`TimeIntegrationOperators`](@ref) object for an IMEX ODE. The `implicit_evaluate` argument is needed when: -- using a multi-step scheme (for the initialisation process). -- using a diagonally implicit scheme that has at least one zero on the diagonal. + + - using a multi-step scheme (for the initialisation process). + - using a diagonally implicit scheme that has at least one zero on the diagonal. """ function define_imex_ode( explicit_evaluate::Function, @@ -191,14 +193,13 @@ integrator. The initialisation procedure uses this information to determine what be initialised. Note that the vectors may be empty. # Constructors -- `TimeLevels( - step_values::Vector{Int} - step_derivatives_implicit::Vector{Int} - step_derivatives_explicit::Vector{Int} - )`: Generic constructor. + + - `TimeLevels( step_values::Vector{Int} step_derivatives_implicit::Vector{Int} step_derivatives_explicit::Vector{Int} )`: Generic constructor. # Examples + Consider the [`CNAB2`](@ref) scheme, which defines the input vector as + ```math \\begin{bmatrix} \\mathbf{y}^n \\\\ @@ -207,9 +208,11 @@ Consider the [`CNAB2`](@ref) scheme, which defines the input vector as \\Delta t \\mathbf{F}^{n-1} \\end{bmatrix} \\;, ``` + so, the `TimeLevels` input for this scheme is `TimeLevels([0], [0], [0, 1])`. Consider the [`BDF3`](@ref) scheme, which defines the input vector as + ```math \\begin{bmatrix} \\mathbf{y}^{n} \\\\ @@ -217,16 +220,18 @@ Consider the [`BDF3`](@ref) scheme, which defines the input vector as \\mathbf{y}^{n-2} \\\\ \\end{bmatrix} \\;, ``` + so, the `TimeLevels` input for this scheme is `TimeLevels([0, 1, 2], Int[], Int[])`. # Fields -- `step_values::Vector{Int}`: The length of this vector determines how many previous + + - `step_values::Vector{Int}`: The length of this vector determines how many previous solutions are needed. For multi-stage methods, this is often just `[0]`. For multi-step methods, this may include a longer history. -- `step_derivatives_implicit::Vector{Int}`: Required implicit step derivatives from + - `step_derivatives_implicit::Vector{Int}`: Required implicit step derivatives from previous steps. Note that this is ``\\Delta t G``. This is, for example, used in the Adams-Moulton schemes. -- `step_derivatives_explicit::Vector{Int}`: Required explicit step derivatives from + - `step_derivatives_explicit::Vector{Int}`: Required explicit step derivatives from previous steps. Note that this is ``\\Delta t F``. This is, for example, used in the Adams-Bashford schemes. """ @@ -280,8 +285,9 @@ Base.maximum(tl::TimeLevels) = max( Check if the GLM matrix `A` belongs to an implicit or diagonally implicit scheme. # Returns -- `is_implicit::Bool`: whether the scheme is fully implicit or not -- `is_diagonally_implicit::Bool`: whether the scheme is diagonally implicit. Note that both + + - `is_implicit::Bool`: whether the scheme is fully implicit or not + - `is_diagonally_implicit::Bool`: whether the scheme is diagonally implicit. Note that both outputs are true for a fully implicit scheme, and that only this argument is true for a diagonally implicit scheme. """ @@ -308,23 +314,26 @@ end Explicit time integration scheme. !!! note "Explicit time integrators are explicit in the ODE sense" + Following [Vos2011](@cite), the explicit time integrators in this framework are considered explicit integrators when applied to ODEs. When applied to PDEs using a Galerkin method, one still has to solve a linear system. This can be referred to as an indirect explicit method in this case. # Fields -- `A`, `B`, `U`, `V`, `C`: See the [GLM characterisation](@ref TIGLMCharacter) for more + + - `A`, `B`, `U`, `V`, `C`: See the [GLM characterisation](@ref TIGLMCharacter) for more details, including the matrix sizes. All matrices (and vector) are of type `SMatrix` (or `SVector`, respectively) with the appropriate size and `NT` as eltype. -- `time_levels::TimeLevels`: Required information from previous steps. See + - `time_levels::TimeLevels`: Required information from previous steps. See [`TimeLevels`](@ref) for the details. -- `order::Int`: Order of the scheme. + - `order::Int`: Order of the scheme. # Type parameters -- `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. -- `NT`: Element type of the `A`, `B`, `U`, `V` matrices, and `C` vector. -- `AA`, `AE`, `EE`: Number of entries in `A`, (`B` and `U`), and `V`, respectively. + + - `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. + - `NT`: Element type of the `A`, `B`, `U`, `V` matrices, and `C` vector. + - `AA`, `AE`, `EE`: Number of entries in `A`, (`B` and `U`), and `V`, respectively. """ struct Explicit{num_stages, num_steps, NT, AA, AE, EE} <: AbstractTimeIntegrator{num_stages, num_steps} @@ -368,17 +377,19 @@ end Diagonally implicit time integration scheme. # Fields -- `A`, `B`, `U`, `V`, `C`: See the [GLM characterisation](@ref TIGLMCharacter) for more + + - `A`, `B`, `U`, `V`, `C`: See the [GLM characterisation](@ref TIGLMCharacter) for more details, including the matrix sizes. All matrices (and vector) are of type `SMatrix` (or `SVector`, respectively) with the appropriate size and `NT` as eltype. -- `time_levels::TimeLevels`: Required information from previous steps. See + - `time_levels::TimeLevels`: Required information from previous steps. See [`TimeLevels`](@ref) for the details. -- `order::Int`: Order of the scheme. + - `order::Int`: Order of the scheme. # Type parameters -- `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. -- `NT`: Element type of the `A`, `B`, `U`, `V` matrices, and `C` vector. -- `AA`, `AE`, `EE`: Number of entries in `A`, (`B` and `U`), and `V`, respectively. + + - `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. + - `NT`: Element type of the `A`, `B`, `U`, `V` matrices, and `C` vector. + - `AA`, `AE`, `EE`: Number of entries in `A`, (`B` and `U`), and `V`, respectively. """ struct DiagonallyImplicit{num_stages, num_steps, NT, AA, AE, EE} <: AbstractTimeIntegrator{num_stages, num_steps} @@ -422,17 +433,19 @@ end Implicit time integration scheme # Fields -- `A`, `B`, `U`, `V`, `C`: See the [GLM characterisation](@ref TIGLMCharacter) for more + + - `A`, `B`, `U`, `V`, `C`: See the [GLM characterisation](@ref TIGLMCharacter) for more details, including the matrix sizes. All matrices (and vector) are of type `SMatrix` (or `SVector`, respectively) with the appropriate size and `NT` as eltype. -- `time_levels::TimeLevels`: Required information from previous steps. See + - `time_levels::TimeLevels`: Required information from previous steps. See [`TimeLevels`](@ref) for the details. -- `order::Int`: Order of the scheme. + - `order::Int`: Order of the scheme. # Type parameters -- `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. -- `NT`: Element type of the `A`, `B`, `U`, `V` matrices, and `C` vector. -- `AA`, `AE`, `EE`: Number of entries in `A`, (`B` and `U`), and `V`, respectively. + + - `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. + - `NT`: Element type of the `A`, `B`, `U`, `V` matrices, and `C` vector. + - `AA`, `AE`, `EE`: Number of entries in `A`, (`B` and `U`), and `V`, respectively. """ struct Implicit{num_stages, num_steps, NT, AA, AE, EE} <: AbstractTimeIntegrator{num_stages, num_steps} @@ -465,19 +478,21 @@ Implicit-Explicit (IMEX) time integration scheme. Currently only supportes impli which are diagonally implicit. # Fields -- `A_IM`, `A_EX`, `B_IM` `B_EX`, `U`, `V`, `C_IM`, `C_EX`: See the + + - `A_IM`, `A_EX`, `B_IM` `B_EX`, `U`, `V`, `C_IM`, `C_EX`: See the [GLM characterisation](@ref TIGLMCharacter) for more details, including the matrix sizes. All matrices (and vector) are of type `SMatrix` (or `SVector`, respectively) with the appropriate size and `NT` as eltype. -- `time_levels::TimeLevels`: Required information from previous steps. See + - `time_levels::TimeLevels`: Required information from previous steps. See [`TimeLevels`](@ref) for the details. -- `order::Int`: Order of the scheme. + - `order::Int`: Order of the scheme. # Type parameters -- `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. -- `NT`: Element type of the `A_IM`, `A_EX`, `B_IM` `B_EX`, `U`, and `V` matrices, and + + - `num_stages`, `num_steps`: See [AbstractTimeIntegrator](@ref) for the details. + - `NT`: Element type of the `A_IM`, `A_EX`, `B_IM` `B_EX`, `U`, and `V` matrices, and `C_IM`, and `C_EX` vectors. -- `AA`, `AE`, `EE`: Number of entries in (`A_IM` and `A_EX`), (`B_IM`, `B_EX` and `U`), + - `AA`, `AE`, `EE`: Number of entries in (`A_IM` and `A_EX`), (`B_IM`, `B_EX` and `U`), and `V`, respectively. """ struct IMEX{num_stages, num_steps, NT, AA, AE, EE} <: @@ -546,43 +561,40 @@ end Solution and current state of the time integration problem. !!! note "`TimeIntegrationSolution` does not store problem-specific information." + While a `TimeIntegrationSolution` stores most information, it does not store problem- specific information. See [`TimeIntegrationOperators`](@ref) for the problem-specific information. - # Constructors -- `TimeIntegrationSolution( - solution::Matrix{NT}, - scheme::AbstractTimeIntegrator{num_stages, num_steps}, - startup_scheme::Union{Nothing, AbstractTimeIntegrator}, - remaining_startup_steps::Int, - startup_solution::ST=nothing, - ) where {NT, num_stages, num_steps, ST}`: General constructor. Note that the eltype of - the solution matrix will dictate the number type used in the - `TimeIntegrationSolution`. + + - `TimeIntegrationSolution( solution::Matrix{NT}, scheme::AbstractTimeIntegrator{num_stages, num_steps}, startup_scheme::Union{Nothing, AbstractTimeIntegrator}, remaining_startup_steps::Int, startup_solution::ST=nothing, ) where {NT, num_stages, num_steps, ST}`: General constructor. Note that the eltype of + the solution matrix will dictate the number type used in the + `TimeIntegrationSolution`. # Fields -- `N::Int`: Number varables in the system. -- `solution::Matrix{NT}`: Of size (`N`, num_steps). -- `scheme::T`: The time integration scheme. -- `startup_scheme::S`: The startup scheme, if provided. Will be `nothing` if not provided. + + - `N::Int`: Number varables in the system. + - `solution::Matrix{NT}`: Of size (`N`, num_steps). + - `scheme::T`: The time integration scheme. + - `startup_scheme::S`: The startup scheme, if provided. Will be `nothing` if not provided. Defaults to `nothing`. -- `remaining_startup_steps::Int`: Remaining startup steps. -- `solution_allocated::Matrix{NT}`: Pre-allocated memory for calculations. -- `F_alLocated::Matrix{NT}`: Pre-allocated memory for calculations. -- `G_alLocated::Matrix{NT}`: Pre-allocated memory for calculations. -- `startup_solution::ST`: The `TimeIntegrationSolution` object used for the startup + - `remaining_startup_steps::Int`: Remaining startup steps. + - `solution_allocated::Matrix{NT}`: Pre-allocated memory for calculations. + - `F_alLocated::Matrix{NT}`: Pre-allocated memory for calculations. + - `G_alLocated::Matrix{NT}`: Pre-allocated memory for calculations. + - `startup_solution::ST`: The `TimeIntegrationSolution` object used for the startup procedure. This will contain information specific to the startup scheme and its current state. Will be `nothing` if not provided. Defaults to `nothing`. -- `stage_values::Vector{NT}`: Pre-allocated memory for calculations. -- `temp_var::Vector{NT}`: Pre-allocated memory for calculations. + - `stage_values::Vector{NT}`: Pre-allocated memory for calculations. + - `temp_var::Vector{NT}`: Pre-allocated memory for calculations. # Type parameters -- `T`: Type of the scheme. -- `S`: Type of the startup scheme. `Nothing` if no startup scheme is provided. -- `NT`: eltype of the solution and pre-allocated arrays. -- `ST`: Type of the startup solution object, `Nothing` if no startup solution is provided. + + - `T`: Type of the scheme. + - `S`: Type of the startup scheme. `Nothing` if no startup scheme is provided. + - `NT`: eltype of the solution and pre-allocated arrays. + - `ST`: Type of the startup solution object, `Nothing` if no startup solution is provided. """ mutable struct TimeIntegrationSolution{T, S, NT, ST} N::Int diff --git a/src/TimeIntegrators/Initialisations.jl b/src/TimeIntegrators/Initialisations.jl index e328e8d3d..bd6404427 100644 --- a/src/TimeIntegrators/Initialisations.jl +++ b/src/TimeIntegrators/Initialisations.jl @@ -6,11 +6,13 @@ Creates the TimeIntegrationSolution object with an initialised y0 vector. # Arguments -- `y0::Matrix{T}`: The initial value of the solution matrix. -- `scheme::AbstractTimeIntegrator{num_stages, num_steps}`: The time integration scheme. + + - `y0::Matrix{T}`: The initial value of the solution matrix. + - `scheme::AbstractTimeIntegrator{num_stages, num_steps}`: The time integration scheme. # Returns -- `TimeIntegrationSolution{num_steps}`: The initialised solution vector. + + - `TimeIntegrationSolution{num_steps}`: The initialised solution vector. """ function initialise_scheme( y0::Matrix{T}, scheme::AbstractTimeIntegrator{num_stages, num_steps} @@ -27,8 +29,9 @@ Initialise single-step schemes. The given input vector will be turned into a mat appropriate size. # Arguments -- `y0::Vector{T}`: The initial value of the solution vector. -- `scheme::AbstractTimeIntegrator{num_stages, num_steps}`: The time integration scheme. + + - `y0::Vector{T}`: The initial value of the solution vector. + - `scheme::AbstractTimeIntegrator{num_stages, num_steps}`: The time integration scheme. """ function initialise_scheme( y0::Vector{T}, scheme::AbstractTimeIntegrator{num_stages, num_steps} @@ -54,13 +57,15 @@ end Initialise multi-step schemes. # Arguments -- `y0::Vector{T}`: The initial value of the solution vector. -- `scheme::AbstractTimeIntegrator{num_stages_scheme, num_steps}`: The time integration + + - `y0::Vector{T}`: The initial value of the solution vector. + - `scheme::AbstractTimeIntegrator{num_stages_scheme, num_steps}`: The time integration scheme. -- `startup_scheme::AbstractTimeIntegrator{num_stages_startup, 1}`: The startup scheme. + - `startup_scheme::AbstractTimeIntegrator{num_stages_startup, 1}`: The startup scheme. # Returns -- `TimeIntegrationSolution{num_steps}`: The initialised solution vector. + + - `TimeIntegrationSolution{num_steps}`: The initialised solution vector. """ function initialise_scheme( y0::Vector{T}, diff --git a/src/TimeIntegrators/Integrations.jl b/src/TimeIntegrators/Integrations.jl index dfb92b950..8020ba75d 100644 --- a/src/TimeIntegrators/Integrations.jl +++ b/src/TimeIntegrators/Integrations.jl @@ -11,17 +11,20 @@ Perform a single time integration step using the given time integration scheme a system operators. Creates a copy of the solution when called. # See also + [`time_integrate!`](@ref) # Arguments -- `y_n::TimeIntegrationSolution`: The current solution vector. -- `ode::TimeIntegrationOperators`: The ODE system operators. -- `t::Float64`: The current time. -- `dt::Float64`: The time step. -- `kwargs...`: Additional arguments passed to the ODE system. + + - `y_n::TimeIntegrationSolution`: The current solution vector. + - `ode::TimeIntegrationOperators`: The ODE system operators. + - `t::Float64`: The current time. + - `dt::Float64`: The time step. + - `kwargs...`: Additional arguments passed to the ODE system. # Returns -- `TimeIntegrationSolution`: The updated solution vector after one time step. + + - `TimeIntegrationSolution`: The updated solution vector after one time step. """ function time_integrate( y_n::TimeIntegrationSolution, @@ -48,17 +51,20 @@ Perform a single, in-place time integration step using the given time integratio and ODE system operators. # See also + [`time_integrate`](@ref) # Arguments -- `y_n::TimeIntegrationSolution`: The current solution vector. -- `ode::TimeIntegrationOperators`: The ODE system operators. -- `t::Float64`: The current time. -- `dt::Float64`: The time step. -- `kwargs...`: Additional arguments passed to the ODE system. + + - `y_n::TimeIntegrationSolution`: The current solution vector. + - `ode::TimeIntegrationOperators`: The ODE system operators. + - `t::Float64`: The current time. + - `dt::Float64`: The time step. + - `kwargs...`: Additional arguments passed to the ODE system. # Returns (in-place) -- `TimeIntegrationSolution`: The updated solution vector after one time step. + + - `TimeIntegrationSolution`: The updated solution vector after one time step. """ function time_integrate!( y_n::TimeIntegrationSolution{T, S}, @@ -302,15 +308,17 @@ ODE system operators. Implements algorithm 1 of [Vos2011](@cite) specifically fo integrator is explicit. # Arguments -- `y_n::TimeIntegrationSolution`: The current solution vector. -- `scheme::Explicit{num_stages, num_steps}`: The Explicit time integration scheme. -- `ode::TimeIntegrationOperators`: The ODE system operators. -- `t::Float64`: The current time. -- `dt::Float64`: The time step. -- `kwargs...`: Additional arguments passed to the ODE system. + + - `y_n::TimeIntegrationSolution`: The current solution vector. + - `scheme::Explicit{num_stages, num_steps}`: The Explicit time integration scheme. + - `ode::TimeIntegrationOperators`: The ODE system operators. + - `t::Float64`: The current time. + - `dt::Float64`: The time step. + - `kwargs...`: Additional arguments passed to the ODE system. # Returns (in-place) -- `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. + + - `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. """ function _time_integrate!( y_n::TimeIntegrationSolution, @@ -395,15 +403,17 @@ scheme and ODE system operators. Implements algorithm 1 of [Vos2011](@cite) spec for when the integrator in diagonally implicit. # Arguments -- `y_n::TimeIntegrationSolution{num_steps}`: The current solution vector. -- `scheme::DiagonallyImplicit{num_stages,num_steps}`: The DiagonallyImplicit time integration scheme. -- `ode::TimeIntegrationOperators`: The ODE system operators. -- `t::Float64`: The current time. -- `dt::Float64`: The time step. -- `kwargs...`: Additional arguments passed to the ODE system. + + - `y_n::TimeIntegrationSolution{num_steps}`: The current solution vector. + - `scheme::DiagonallyImplicit{num_stages,num_steps}`: The DiagonallyImplicit time integration scheme. + - `ode::TimeIntegrationOperators`: The ODE system operators. + - `t::Float64`: The current time. + - `dt::Float64`: The time step. + - `kwargs...`: Additional arguments passed to the ODE system. # Returns (in-place) -- `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. + + - `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. """ function _time_integrate!( y_n::TimeIntegrationSolution, @@ -499,15 +509,17 @@ Perform a single time integration step using the given Implicit time integration ODE system operators. # Arguments -- `y_n::TimeIntegrationSolution{num_steps}`: The current solution vector. -- `scheme::Implicit{num_stages,num_steps}`: The Implicit time integration scheme. -- `ode::TimeIntegrationOperators`: The ODE system operators. -- `t::Float64`: The current time. -- `dt::Float64`: The time step. -- `kwargs...`: Additional arguments passed to the ODE system. + + - `y_n::TimeIntegrationSolution{num_steps}`: The current solution vector. + - `scheme::Implicit{num_stages,num_steps}`: The Implicit time integration scheme. + - `ode::TimeIntegrationOperators`: The ODE system operators. + - `t::Float64`: The current time. + - `dt::Float64`: The time step. + - `kwargs...`: Additional arguments passed to the ODE system. # Returns (in-place) -- `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. + + - `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. """ function _time_integrate!( y_n::TimeIntegrationSolution, @@ -566,15 +578,17 @@ Perform a single time integration step using the given IMEX time integration sch system operators. Implements algorithm 1 of [Vos2011](@cite) in full. # Arguments -- `y_n::TimeIntegrationSolution{num_steps}`: The current solution vector. -- `scheme::IMEX{num_stages,num_steps}`: The IMEX time integration scheme. -- `ode::TimeIntegrationOperators`: The ODE system operators. -- `t::Float64`: The current time. -- `dt::Float64`: The time step. -- `kwargs...`: Additional arguments passed to the ODE system. + + - `y_n::TimeIntegrationSolution{num_steps}`: The current solution vector. + - `scheme::IMEX{num_stages,num_steps}`: The IMEX time integration scheme. + - `ode::TimeIntegrationOperators`: The ODE system operators. + - `t::Float64`: The current time. + - `dt::Float64`: The time step. + - `kwargs...`: Additional arguments passed to the ODE system. # Returns (in-place) -- `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. + + - `TimeIntegrationSolution{num_steps}`: The updated solution vector after one time step. """ function _time_integrate!( y_n::TimeIntegrationSolution, diff --git a/src/TimeIntegrators/Schemes.jl b/src/TimeIntegrators/Schemes.jl index c27eb65ac..3ab1f4256 100644 --- a/src/TimeIntegrators/Schemes.jl +++ b/src/TimeIntegrators/Schemes.jl @@ -396,6 +396,7 @@ const GAUSS_LEGENDRE_6 = butcher_tableau_to_glm( AB1 Adams-Bashforth 1: + ```math y_{n+1} = y_{n} + \\Delta t f(y_{n})\\;. ``` @@ -418,6 +419,7 @@ const AB1 = Explicit( AB2 Adams-Bashforth 2: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{3}{2} f(y_{n}) - \\frac{1}{2} f(y_{n-1}))\\;. ``` @@ -440,6 +442,7 @@ const AB2 = Explicit( AB3 Adams-Bashforth 3: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{23}{12} f(y_{n}) - \\frac{4}{3} f(y_{n-1}) + \\frac{5}{12} f(y_{n-2}))\\;. ``` @@ -462,6 +465,7 @@ const AB3 = Explicit( AB4 Adams-Bashforth 4: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{55}{24} f(y_{n}) - \\frac{59}{24} f(y_{n-1}) + \\frac{37}{24} f(y_{n-2}) - \\frac{9}{24} f(y_{n-3}))\\;. ``` @@ -504,6 +508,7 @@ const AB4 = Explicit( AM0 Adams-Moulton 0: + ```math y_{n+1} = y_{n} + \\Delta t g(y_{n})\\;. ``` @@ -526,6 +531,7 @@ const AM0 = DiagonallyImplicit( AM1 Adams-Moulton 1: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{1}{2} g(y_{n+1}) + \\frac{1}{2} g(y_{n}))\\;. ``` @@ -548,6 +554,7 @@ const AM1 = DiagonallyImplicit( AM2 Adams-Moulton 2: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{5}{12} g(y_{n+1}) + \\frac{8}{12} g(y_{n}) - \\frac{1}{12} g(y_{n-1})\\;. ``` @@ -570,6 +577,7 @@ const AM2 = DiagonallyImplicit( AM3 Adams-Moulton 3: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{9}{24} g(y_{n+1}) + \\frac{19}{24} g(y_{n}) - \\frac{5}{24} g(y_{n-1}) + \\frac{1}{24} g(y_{n-2}))\\;. ``` @@ -594,6 +602,7 @@ const AM3 = DiagonallyImplicit( AM4 Adams-Moulton 4: + ```math y_{n+1} = y_{n} + \\Delta t (\\frac{251}{720} g(y_{n+1}) + \\frac{646}{720} g(y_{n}) - \\frac{264}{720} g(y_{n-1}) + \\frac{106}{720} g(y_{n-2}) - \\frac{19}{720} g(y_{n-3}))\\;. ``` @@ -643,6 +652,7 @@ const AM4 = DiagonallyImplicit( BDF1 Backward differentiation formula 1: + ```math y_{n+1} = y_{n} + \\Delta t f(y_n)\\;. ``` @@ -665,6 +675,7 @@ const BDF1 = DiagonallyImplicit( BDF2 Backward differentiation formula 2: + ```math y_{n+1} = \\frac{4}{3} y_{n} - \\frac{1}{3} y_{n-1} + \\frac{2}{3} \\Delta t f(y_{n})\\;. ``` @@ -687,6 +698,7 @@ const BDF2 = DiagonallyImplicit( BDF3 Backward differentiation formula 3: + ```math y_{n+1} = \\frac{18}{11} y_{n} - \\frac{9}{11} y_{n-1} + \\frac{2}{11} y_{n-2} + \\frac{6}{11} \\Delta t f(y_{n})\\;. ``` @@ -709,6 +721,7 @@ const BDF3 = DiagonallyImplicit( BDF4 Backward differentiation formula 4: + ```math y_{n+1} = \\frac{48}{25} y_{n} - \\frac{36}{25} y_{n-1} + \\frac{16}{25} y_{n-2} - \\frac{3}{25} y_{n-3} + \\frac{12}{25} \\Delta t f(y_{n})\\;. ``` diff --git a/test/Assemblers/0-form-TensorProduct-LaplacianTests.jl b/test/Assemblers/0-form-TensorProduct-LaplacianTests.jl index c16dd4465..b3906b889 100644 --- a/test/Assemblers/0-form-TensorProduct-LaplacianTests.jl +++ b/test/Assemblers/0-form-TensorProduct-LaplacianTests.jl @@ -75,9 +75,7 @@ function test_0form_hodge_laplacian( if section_space == FunctionSpaces.Lagrange nodes = ntuple(manifold_dim) do i return Points.get_constituent_points( - Quadrature.get_nodes( - Quadrature.gauss_lobatto(degree[i]+1) - ) + Quadrature.get_nodes(Quadrature.gauss_lobatto(degree[i]+1)) )[1] end section_spaces = map(section_space, nodes) @@ -126,7 +124,9 @@ function test_0form_hodge_laplacian( uₕ = Assemblers.solve_zero_form_hodge_laplacian(X[1], fₑ, dΩ) if test if section_space == FunctionSpaces.Lagrange - ref_coeffs = read_data(sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh.txt") + ref_coeffs = read_data( + sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh.txt" + ) else ref_coeffs = read_data(sub_dir, "$p-$section_space-$mesh.txt") end diff --git a/test/Assemblers/1-form-Hierarchical-MaxwellEigenvalueTests.jl b/test/Assemblers/1-form-Hierarchical-MaxwellEigenvalueTests.jl index 93105a919..ed9139197 100644 --- a/test/Assemblers/1-form-Hierarchical-MaxwellEigenvalueTests.jl +++ b/test/Assemblers/1-form-Hierarchical-MaxwellEigenvalueTests.jl @@ -76,8 +76,9 @@ function run_problems( num_eig, geo, scale_factors ) eigval_errors = compt_eigvals - exact_eigvals - eigfunc_errors = - Analysis.compute_error_total.(compt_eigfuncs, exact_eigfuncs, Ref(dΩₑ), Ref("L2")) + eigfunc_errors = Analysis.compute_error_total.( + compt_eigfuncs, exact_eigfuncs, Ref(dΩₑ), Ref("L2") + ) return eigval_errors, eigfunc_errors end diff --git a/test/Assemblers/k-form-TensorProduct-L2ProjectionTests.jl b/test/Assemblers/k-form-TensorProduct-L2ProjectionTests.jl index 63d01ee22..f78e6b198 100644 --- a/test/Assemblers/k-form-TensorProduct-L2ProjectionTests.jl +++ b/test/Assemblers/k-form-TensorProduct-L2ProjectionTests.jl @@ -74,9 +74,7 @@ for (mesh_idx, mesh) in enumerate(mesh_type) if section_space == FunctionSpaces.Lagrange nodes = ntuple(manifold_dim) do i return Points.get_constituent_points( - Quadrature.get_nodes( - Quadrature.gauss_lobatto(degree[i]+1) - ) + Quadrature.get_nodes(Quadrature.gauss_lobatto(degree[i]+1)) )[1] end section_spaces = map(section_space, nodes) @@ -127,9 +125,14 @@ for (mesh_idx, mesh) in enumerate(mesh_type) # read reference data and compare if section_space == FunctionSpaces.Lagrange - ref_coeffs = read_data(sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh-$form_rank.txt") + ref_coeffs = read_data( + sub_dir, + "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh-$form_rank.txt", + ) else - ref_coeffs = read_data(sub_dir, "$p-$section_space-$mesh-$form_rank.txt") + ref_coeffs = read_data( + sub_dir, "$p-$section_space-$mesh-$form_rank.txt" + ) end @test all( isapprox.(fₕ.coefficients, ref_coeffs, atol=atol * 20, rtol=rtol * 20) diff --git a/test/Assemblers/n-form-TensorProduct-MixedLaplacianTests.jl b/test/Assemblers/n-form-TensorProduct-MixedLaplacianTests.jl index c210ad82b..56fb6040d 100644 --- a/test/Assemblers/n-form-TensorProduct-MixedLaplacianTests.jl +++ b/test/Assemblers/n-form-TensorProduct-MixedLaplacianTests.jl @@ -112,9 +112,7 @@ for (mesh_idx, mesh) in enumerate(mesh_type) if section_space == FunctionSpaces.Lagrange nodes = ntuple(manifold_dim) do i return Points.get_constituent_points( - Quadrature.get_nodes( - Quadrature.gauss_lobatto(degree[i]+1) - ) + Quadrature.get_nodes(Quadrature.gauss_lobatto(degree[i]+1)) )[1] end section_spaces = map(section_space, nodes) @@ -167,7 +165,9 @@ for (mesh_idx, mesh) in enumerate(mesh_type) # display([n_dofs cond_num]) if section_space == FunctionSpaces.Lagrange - ref_coeffs = read_data(sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh-uh.txt") + ref_coeffs = read_data( + sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh-uh.txt" + ) else ref_coeffs = read_data(sub_dir, "$p-$section_space-$mesh-uh.txt") end @@ -176,7 +176,9 @@ for (mesh_idx, mesh) in enumerate(mesh_type) isapprox.(uₕ.coefficients, ref_coeffs, atol=atol * 50, rtol=rtol * 50) ) if section_space == FunctionSpaces.Lagrange - ref_coeffs = read_data(sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh-phih.txt") + ref_coeffs = read_data( + sub_dir, "$p-Mantis.FunctionSpaces.LobattoLegendre-$mesh-phih.txt" + ) else ref_coeffs = read_data(sub_dir, "$p-$section_space-$mesh-phih.txt") end diff --git a/test/Forms/ConstantFormSpaceTests.jl b/test/Forms/ConstantFormSpaceTests.jl index 9e96edc7c..dc35897ff 100644 --- a/test/Forms/ConstantFormSpaceTests.jl +++ b/test/Forms/ConstantFormSpaceTests.jl @@ -15,7 +15,7 @@ const num_points_per_dim = 3 function test_cartesian(manifold_dim::Int, num_quad_points_per_dim::Int) # Create a simple Cartesian geometry with random breakpoints breakpoints = ntuple(manifold_dim) do i - el_sizes = 1.1.^(1:num_elements_per_dim) + el_sizes = 1.1 .^ (1:num_elements_per_dim) el_sizes ./= sum(el_sizes) el_sizes .*= L return cumsum([0.0; el_sizes]) @@ -24,7 +24,9 @@ function test_cartesian(manifold_dim::Int, num_quad_points_per_dim::Int) canonical_qrule = Quadrature.tensor_product_rule( ntuple(i->num_quad_points_per_dim, manifold_dim), Quadrature.gauss_legendre ) - dΩ = Mantis.Quadrature.StandardQuadrature(canonical_qrule, Geometry.get_num_elements(geom)) + dΩ = Mantis.Quadrature.StandardQuadrature( + canonical_qrule, Geometry.get_num_elements(geom) + ) # Setup the form spaces, evaluate and check the results ξ = Points.CartesianPoints(ntuple(i->range(0.0, 1.0, num_points_per_dim), manifold_dim)) @@ -33,10 +35,10 @@ function test_cartesian(manifold_dim::Int, num_quad_points_per_dim::Int) @test_throws ArgumentError Forms.ConstantFormSpace(form_rank, geom, "c") else form_space = Forms.ConstantFormSpace(form_rank, geom, "a") - + # Check number of basis functions @test Forms.get_num_basis(form_space) == 1 - + # Check number of basis functions for each element for element_id in 1:Forms.get_num_elements(form_space) @test Forms.get_num_basis(form_space, element_id) == 1 @@ -68,7 +70,8 @@ function test_cartesian(manifold_dim::Int, num_quad_points_per_dim::Int) for element_id in 1:Forms.get_num_elements(form_space) d_eval, d_inds = Forms.evaluate(d_form_space, element_id, ξ) for i in 1:manifold_dim - @test d_eval[i] == reshape([0.0 for _ in 1:Points.get_num_points(ξ)], :, 1) + @test d_eval[i] == + reshape([0.0 for _ in 1:Points.get_num_points(ξ)], :, 1) end @test d_inds == [[1]] end @@ -86,7 +89,6 @@ function test_cartesian(manifold_dim::Int, num_quad_points_per_dim::Int) end @test isapprox(integral_Eval, L^manifold_dim, atol=1e-12) end - end end @@ -107,12 +109,14 @@ const r = 1.0 const Δr = 0.1 function mapping(x::AbstractVector) - return [x[1].*cos(x[2]*π/2), x[1].*sin(x[2]*π/2), 1.0] + return [x[1] .* cos(x[2]*π/2), x[1] .* sin(x[2]*π/2), 1.0] end function dmapping(x::AbstractVector) - return [cos(x[2]*π/2) -x[1]*sin(x[2]*π/2)*π/2; - sin(x[2]*π/2) x[1]*cos(x[2]*π/2)*π/2; - 0.0 0.0] + return [ + cos(x[2]*π/2) -x[1]*sin(x[2]*π/2)*π/2; + sin(x[2]*π/2) x[1]*cos(x[2]*π/2)*π/2; + 0.0 0.0 + ] end dimension = (2, 3) curved_mapping = Geometry.Mapping(dimension, mapping, dmapping) @@ -124,20 +128,23 @@ mapped_geometry = Geometry.MappedGeometry(geom, curved_mapping) surface_area = π * (r^2 - Δr^2)/4 function test_mapped(num_quad_points_per_dim::Int) - canonical_qrule = Quadrature.tensor_product_rule( ntuple(i->num_quad_points_per_dim, manifold_dim), Quadrature.gauss_legendre ) - dΩ = Mantis.Quadrature.StandardQuadrature(canonical_qrule, Geometry.get_num_elements(mapped_geometry)) + dΩ = Mantis.Quadrature.StandardQuadrature( + canonical_qrule, Geometry.get_num_elements(mapped_geometry) + ) # Setup the form spaces, evaluate and check the results ξ = Points.CartesianPoints(ntuple(i->range(0.0, 1.0, num_points_per_dim), manifold_dim)) for form_rank in 0:manifold_dim if form_rank ∉ Set([0, manifold_dim]) - @test_throws ArgumentError Forms.ConstantFormSpace(form_rank, mapped_geometry, "c") + @test_throws ArgumentError Forms.ConstantFormSpace( + form_rank, mapped_geometry, "c" + ) else form_space = Forms.ConstantFormSpace(form_rank, mapped_geometry, "a") - + # Check number of basis functions @test Forms.get_num_basis(form_space) == 1 @@ -172,7 +179,8 @@ function test_mapped(num_quad_points_per_dim::Int) for element_id in 1:Forms.get_num_elements(form_space) d_eval, d_inds = Forms.evaluate(d_form_space, element_id, ξ) for i in 1:manifold_dim - @test d_eval[i] == reshape([0.0 for _ in 1:Points.get_num_points(ξ)], :, 1) + @test d_eval[i] == + reshape([0.0 for _ in 1:Points.get_num_points(ξ)], :, 1) end @test d_inds == [[1]] end @@ -190,7 +198,6 @@ function test_mapped(num_quad_points_per_dim::Int) end @test isapprox(integral_Eval, surface_area, atol=1e-12) end - end end @@ -200,5 +207,4 @@ end end end - -end \ No newline at end of file +end diff --git a/test/Forms/FormExpressionTests.jl b/test/Forms/FormExpressionTests.jl index 4c6c61c91..72d0cb6c2 100644 --- a/test/Forms/FormExpressionTests.jl +++ b/test/Forms/FormExpressionTests.jl @@ -37,7 +37,9 @@ function dmapping_ed_test(x::Vector{Float64}) x2_new = (2.0 / (Ltop - Lbottom)) * x[2] - 2.0 * Lbottom / (Ltop - Lbottom) - 1.0 return [ - 1.0+pi * c * cospi(x1_new) * sinpi(x2_new) ((Lright - Lleft)/(Ltop - Lbottom))*pi*c*sinpi(x1_new)*cospi(x2_new) + 1.0+pi * c * cospi(x1_new) * sinpi(x2_new) ((Lright - Lleft)/(Ltop - Lbottom))*pi*c*sinpi( + x1_new + )*cospi(x2_new) ((Ltop - Lbottom)/(Lright - Lleft))*pi*c*cospi(x1_new)*sinpi(x2_new) 1.0+pi * c * sinpi(x1_new) * cospi(x2_new) ] end diff --git a/test/Forms/FormOperators/ExteriorDerivativeTests.jl b/test/Forms/FormOperators/ExteriorDerivativeTests.jl index 934637676..34104981b 100644 --- a/test/Forms/FormOperators/ExteriorDerivativeTests.jl +++ b/test/Forms/FormOperators/ExteriorDerivativeTests.jl @@ -33,7 +33,9 @@ function dmapping_ed_test(x::Vector{Float64}) x2_new = (2.0 / (Ltop - Lbottom)) * x[2] - 2.0 * Lbottom / (Ltop - Lbottom) - 1.0 return [ - 1.0+pi * c * cospi(x1_new) * sinpi(x2_new) ((Lright - Lleft)/(Ltop - Lbottom))*pi*c*sinpi(x1_new)*cospi(x2_new) + 1.0+pi * c * cospi(x1_new) * sinpi(x2_new) ((Lright - Lleft)/(Ltop - Lbottom))*pi*c*sinpi( + x1_new + )*cospi(x2_new) ((Ltop - Lbottom)/(Lright - Lleft))*pi*c*cospi(x1_new)*sinpi(x2_new) 1.0+pi * c * sinpi(x1_new) * cospi(x2_new) ] end @@ -144,7 +146,7 @@ const q_rule = Quadrature.tensor_product_rule((deg + 1, deg + 1), Quadrature.gau abs.( Forms.evaluate( dα⁰, elem_id, Quadrature.get_nodes(q_rule) - )[1][1] + )[1][1], ), ), 0.0; @@ -157,7 +159,7 @@ const q_rule = Quadrature.tensor_product_rule((deg + 1, deg + 1), Quadrature.gau abs.( Forms.evaluate( dα⁰, elem_id, Quadrature.get_nodes(q_rule) - )[1][2] + )[1][2], ), ), 0.0; @@ -178,7 +180,7 @@ const q_rule = Quadrature.tensor_product_rule((deg + 1, deg + 1), Quadrature.gau abs.( Forms.evaluate( dζ¹, elem_id, Quadrature.get_nodes(q_rule) - )[1][1] + )[1][1], ), ), 0.0; @@ -609,7 +611,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dα⁰, elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][1] + )[1][1], ), ), 0.0; @@ -622,7 +624,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dα⁰, elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][2] + )[1][2], ), ), 0.0; @@ -635,7 +637,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dα⁰, elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][3] + )[1][3], ), ), 0.0; @@ -651,7 +653,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dζ¹, elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][1] + )[1][1], ), ), 0.0; @@ -664,7 +666,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dζ¹, elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][2] + )[1][2], ), ), 0.0; @@ -677,7 +679,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dζ¹, elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][3] + )[1][3], ), ), 0.0; @@ -693,7 +695,7 @@ const q_rule_3D = Quadrature.tensor_product_rule( abs.( Forms.evaluate( dβ², elem_id, Quadrature.get_nodes(q_rule_3D) - )[1][1] + )[1][1], ), ), 0.0; diff --git a/test/Forms/FormOperators/HodgeTests.jl b/test/Forms/FormOperators/HodgeTests.jl index fbf16bf60..36fb6a71a 100644 --- a/test/Forms/FormOperators/HodgeTests.jl +++ b/test/Forms/FormOperators/HodgeTests.jl @@ -26,9 +26,7 @@ cart2 = Geometry.CartesianGeometry(breakpoints2) # Crazy mesh c = 0.2 -crazy_mapping = Geometry.create_curvilinear_mapping( - (Lleft, Lbottom), (Lright, Ltop), c -) +crazy_mapping = Geometry.create_curvilinear_mapping((Lleft, Lbottom), (Lright, Ltop), c) # first B-spline patch deg1 = 2 diff --git a/test/Forms/FormOperators/WedgeTests.jl b/test/Forms/FormOperators/WedgeTests.jl index 4c7b730c3..cb1d90764 100644 --- a/test/Forms/FormOperators/WedgeTests.jl +++ b/test/Forms/FormOperators/WedgeTests.jl @@ -265,12 +265,7 @@ cart_complex_2d = Forms.create_tensor_product_bspline_de_rham_complex( starting_point_2d, box_size_2d, num_elements_2d, degrees_2d, regularities_2d ) curv_complex_2d = Forms.create_curvilinear_tensor_product_bspline_de_rham_complex( - starting_point_2d, - box_size_2d, - num_elements_2d, - degrees_2d, - regularities_2d; - c=c, + starting_point_2d, box_size_2d, num_elements_2d, degrees_2d, regularities_2d; c=c ) # The canonical quadrature information. diff --git a/test/FunctionSpaces/CanonicalSpaces/ECTSpaces/runtests.jl b/test/FunctionSpaces/CanonicalSpaces/ECTSpaces/runtests.jl index bcec574e5..33cdc2289 100644 --- a/test/FunctionSpaces/CanonicalSpaces/ECTSpaces/runtests.jl +++ b/test/FunctionSpaces/CanonicalSpaces/ECTSpaces/runtests.jl @@ -2,6 +2,8 @@ module ECTSpacesTests using Test -@testset "GeneralizedSpaces" begin include("GeneralizedSpacesTests.jl") end +@testset "GeneralizedSpaces" begin + include("GeneralizedSpacesTests.jl") +end end diff --git a/test/FunctionSpaces/CanonicalSpaces/LagrangePolynomialsTests.jl b/test/FunctionSpaces/CanonicalSpaces/LagrangePolynomialsTests.jl index eb16f8393..e0d44704e 100644 --- a/test/FunctionSpaces/CanonicalSpaces/LagrangePolynomialsTests.jl +++ b/test/FunctionSpaces/CanonicalSpaces/LagrangePolynomialsTests.jl @@ -137,7 +137,9 @@ ll_evaluation = [ # Perform the tests for p_idx in eachindex(p_reference) p = p_reference[p_idx] # define the polynomial degree - nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_lobatto(p+1)))[1] + nodes = Points.get_constituent_points( + Quadrature.get_nodes(Quadrature.gauss_lobatto(p+1)) + )[1] ll_polynomial = FunctionSpaces.Lagrange(nodes) xi_evaluate = Points.CartesianPoints((range(0.0, 1.0; length=11),)) # the points where to evaluate for testing @@ -159,7 +161,9 @@ end # Perform derivative tests degrees_to_test = 1:25 for p in degrees_to_test - nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_lobatto(p+1)))[1] + nodes = Points.get_constituent_points( + Quadrature.get_nodes(Quadrature.gauss_lobatto(p+1)) + )[1] ll_polynomial = FunctionSpaces.Lagrange(nodes) xi_evaluate = Points.CartesianPoints((range(0.0, 1.0; length=11),)) # the points where to evaluate for testing @@ -330,7 +334,9 @@ gl_evaluation = [ # Test nodes for p_idx in eachindex(p_reference) p = p_reference[p_idx] - nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_legendre(p+1)))[1] + nodes = Points.get_constituent_points( + Quadrature.get_nodes(Quadrature.gauss_legendre(p+1)) + )[1] gl_polynomial = FunctionSpaces.Lagrange(nodes) # Test that nodes are generate as expected @@ -350,7 +356,9 @@ end # Perform derivative tests degrees_to_test = 1:25 for p in degrees_to_test - nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_legendre(p+1)))[1] + nodes = Points.get_constituent_points( + Quadrature.get_nodes(Quadrature.gauss_legendre(p+1)) + )[1] gl_polynomial = FunctionSpaces.Lagrange(nodes) xi_evaluate = Points.CartesianPoints((range(0.0, 1.0; length=11),)) # the points where to evaluate for testing @@ -403,7 +411,9 @@ for p in degrees_to_test # - Integral Kronecker delta property # Construct the polynomials - nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_lobatto(p+2)))[1] + nodes = Points.get_constituent_points( + Quadrature.get_nodes(Quadrature.gauss_lobatto(p+2)) + )[1] ell_poly = FunctionSpaces.Edge(nodes) # Compute the evaluation points (quadrature points) diff --git a/test/FunctionSpaces/CanonicalSpaces/runtests.jl b/test/FunctionSpaces/CanonicalSpaces/runtests.jl index 18e6e4ae9..4a2d14cf9 100644 --- a/test/FunctionSpaces/CanonicalSpaces/runtests.jl +++ b/test/FunctionSpaces/CanonicalSpaces/runtests.jl @@ -2,9 +2,15 @@ module CanonicalSpacesTests using Test -@testset "LagrangePolynomials" begin include("LagrangePolynomialsTests.jl") end -@testset "BernsteinPolynomials" begin include("BernsteinPolynomialsTests.jl") end +@testset "LagrangePolynomials" begin + include("LagrangePolynomialsTests.jl") +end +@testset "BernsteinPolynomials" begin + include("BernsteinPolynomialsTests.jl") +end -@testset verbose=true "ECTSpaces" begin include("ECTSpaces/runtests.jl") end +@testset verbose=true "ECTSpaces" begin + include("ECTSpaces/runtests.jl") +end end diff --git a/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalBSplineTests.jl b/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalBSplineTests.jl index 033309d75..3f953c29b 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalBSplineTests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/HierarchicalBSplineTests.jl @@ -57,7 +57,7 @@ for element_id in 1:1:FunctionSpaces.get_num_elements(hier_space) level, element_level_id = FunctionSpaces.convert_to_element_level_and_level_id( hier_space, element_id ) - borders = FunctionSpaces.get_element_vertices(hier_space, element_id)[1] + borders = FunctionSpaces.get_element_vertices(hier_space, element_id)[1] x = borders[1] .+ Points.get_constituent_points(xi)[1] .* (borders[2] - borders[1]) idx = ((element_id - 1) * nxi + 1):(element_id * nxi) xs[idx] = x @@ -68,7 +68,7 @@ end coeffs = A \ xs A * coeffs .- xs -all(isapprox.(A * coeffs .- xs, 0.0, atol=1e-14)) +all(isapprox.(A * coeffs .- xs, 0.0; atol=1e-14)) @test FunctionSpaces.get_num_levels(hier_space) == nlevels diff --git a/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductHBSplineTests.jl b/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductHBSplineTests.jl index 45d023ac1..f33cd3add 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductHBSplineTests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductHBSplineTests.jl @@ -8,9 +8,9 @@ using Test ne1 = 5 ne2 = 5 -breakpoints1 = collect(range(0,1,ne1+1)) +breakpoints1 = collect(range(0, 1, ne1+1)) patch1 = Mantis.Geometry.CartesianGeometry(breakpoints1) -breakpoints2 = collect(range(0,1,ne2+1)) +breakpoints2 = collect(range(0, 1, ne2+1)) patch2 = Mantis.Geometry.CartesianGeometry(breakpoints2) deg1 = 2 @@ -22,7 +22,7 @@ CB2 = Mantis.FunctionSpaces.BSplineSpace(patch2, deg2, [-1; fill(deg2-1, ne2-1); nsub1 = 2 nsub2 = 2 -TS1,FB1 = Mantis.FunctionSpaces.build_two_scale_operator(CB1, nsub1) +TS1, FB1 = Mantis.FunctionSpaces.build_two_scale_operator(CB1, nsub1) TS2, FB2 = Mantis.FunctionSpaces.build_two_scale_operator(CB2, nsub2) CTP = Mantis.FunctionSpaces.TensorProductSpace((CB1, CB2)) @@ -31,17 +31,23 @@ spaces = [CTP, FTP] CTP_num_els = Mantis.FunctionSpaces.get_num_elements(CTP) -CTS = Mantis.FunctionSpaces.TensorProductTwoScaleOperator(CTP, FTP, (TS1,TS2)) +CTS = Mantis.FunctionSpaces.TensorProductTwoScaleOperator(CTP, FTP, (TS1, TS2)) -coarse_elements_to_refine = [3,4,5,8,9,10] -refined_elements = vcat(Mantis.FunctionSpaces.get_element_children.(Ref(CTS), coarse_elements_to_refine)...) +coarse_elements_to_refine = [3, 4, 5, 8, 9, 10] +refined_elements = vcat( + Mantis.FunctionSpaces.get_element_children.(Ref(CTS), coarse_elements_to_refine)... +) -refined_domains = Mantis.Hierarchy.ActiveInfo([collect(1:CTP_num_els),refined_elements]) +refined_domains = Mantis.Hierarchy.ActiveInfo([collect(1:CTP_num_els), refined_elements]) ### -hier_space = Mantis.FunctionSpaces.HierarchicalFiniteElementSpace(spaces, [CTS], refined_domains, (nsub1, nsub2)) +hier_space = Mantis.FunctionSpaces.HierarchicalFiniteElementSpace( + spaces, [CTS], refined_domains, (nsub1, nsub2) +) -qrule = Mantis.Quadrature.tensor_product_rule((deg1+1, deg2+1), Mantis.Quadrature.gauss_legendre) +qrule = Mantis.Quadrature.tensor_product_rule( + (deg1+1, deg2+1), Mantis.Quadrature.gauss_legendre +) xi = Mantis.Quadrature.get_nodes(qrule) # Tests for coefficients and evaluation @@ -57,12 +63,11 @@ for element_id in 1:1:Mantis.FunctionSpaces.get_num_elements(hier_space) @test minimum(h_eval[1][1][1]) >= 0.0 end - ne1 = 10 ne2 = 10 -breakpoints1 = collect(range(0,1,ne1+1)) +breakpoints1 = collect(range(0, 1, ne1+1)) patch1 = Mantis.Geometry.CartesianGeometry(breakpoints1) -breakpoints2 = collect(range(0,1,ne2+1)) +breakpoints2 = collect(range(0, 1, ne2+1)) patch2 = Mantis.Geometry.CartesianGeometry(breakpoints2) deg1 = 3 @@ -74,7 +79,7 @@ CB2 = Mantis.FunctionSpaces.BSplineSpace(patch2, deg2, [-1; fill(deg2-1, ne2-1); nsub1 = 2 nsub2 = 2 -TS1,FB1 = Mantis.FunctionSpaces.build_two_scale_operator(CB1, nsub1) +TS1, FB1 = Mantis.FunctionSpaces.build_two_scale_operator(CB1, nsub1) TS2, FB2 = Mantis.FunctionSpaces.build_two_scale_operator(CB2, nsub2) CTP = Mantis.FunctionSpaces.TensorProductSpace((CB1, CB2)) @@ -83,14 +88,16 @@ spaces = [CTP, FTP] CTP_num_els = Mantis.FunctionSpaces.get_num_elements(CTP) -CTS = Mantis.FunctionSpaces.TensorProductTwoScaleOperator(CTP, FTP, (TS1,TS2)) +CTS = Mantis.FunctionSpaces.TensorProductTwoScaleOperator(CTP, FTP, (TS1, TS2)) basis1_support = Mantis.FunctionSpaces.get_support(CTP, 57) basis2_support = Mantis.FunctionSpaces.get_support(CTP, 98) coarse_elements_to_refine = vcat(basis1_support, basis2_support) -refined_elements = vcat(Mantis.FunctionSpaces.get_element_children.(Ref(CTS), coarse_elements_to_refine)...) +refined_elements = vcat( + Mantis.FunctionSpaces.get_element_children.(Ref(CTS), coarse_elements_to_refine)... +) -refined_domains = Mantis.Hierarchy.ActiveInfo([collect(1:CTP_num_els),refined_elements]) +refined_domains = Mantis.Hierarchy.ActiveInfo([collect(1:CTP_num_els), refined_elements]) non_simplified_hier_space = Mantis.FunctionSpaces.HierarchicalFiniteElementSpace( spaces, [CTS], refined_domains, (nsub1, nsub2), false @@ -101,7 +108,9 @@ simplified_hier_space = Mantis.FunctionSpaces.HierarchicalFiniteElementSpace( for level in 1:2 @test length(Mantis.FunctionSpaces.get_level_basis_ids(simplified_hier_space, level)) <= - length(Mantis.FunctionSpaces.get_level_basis_ids(non_simplified_hier_space, level)) + length( + Mantis.FunctionSpaces.get_level_basis_ids(non_simplified_hier_space, level) + ) if level == 2 @test Mantis.FunctionSpaces.get_level_basis_ids(simplified_hier_space, level) != Mantis.FunctionSpaces.get_level_basis_ids(non_simplified_hier_space, level) diff --git a/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductTHBSplineTests.jl b/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductTHBSplineTests.jl index 84c71d60c..9a6071673 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductTHBSplineTests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/Hierarchical/TensorProductTHBSplineTests.jl @@ -7,9 +7,9 @@ using Test # Tests for a tensor product HierarchicalSplineSpace ne1 = 5 ne2 = 5 -breakpoints1 = collect(range(0,1,ne1+1)) +breakpoints1 = collect(range(0, 1, ne1+1)) patch1 = Mantis.Geometry.CartesianGeometry(breakpoints1) -breakpoints2 = collect(range(0,1,ne2+1)) +breakpoints2 = collect(range(0, 1, ne2+1)) patch2 = Mantis.Geometry.CartesianGeometry(breakpoints2) deg1 = 2 deg2 = 2 @@ -25,19 +25,31 @@ TTS, FTP = Mantis.FunctionSpaces.build_two_scale_operator(CTP, nsubs) spaces = [CTP, FTP] operators = [TTS] -for level ∈ 3:nlevels - new_operator, new_space = Mantis.FunctionSpaces.build_two_scale_operator(spaces[level-1], nsubs) +for level in 3:nlevels + new_operator, new_space = Mantis.FunctionSpaces.build_two_scale_operator( + spaces[level - 1], nsubs + ) push!(spaces, new_space) push!(operators, new_operator) end -level_2_marked_elements = [child for parent in [7,8,9,12,13,14,17,18,19] for child in Mantis.FunctionSpaces.get_element_children(operators[1], parent)] -level_3_marked_elements = [child for parent in [23, 24, 25, 33, 34, 35, 43, 44, 45] for child in Mantis.FunctionSpaces.get_element_children(operators[2], parent)] +level_2_marked_elements = [ + child for parent in [7, 8, 9, 12, 13, 14, 17, 18, 19] for + child in Mantis.FunctionSpaces.get_element_children(operators[1], parent) +] +level_3_marked_elements = [ + child for parent in [23, 24, 25, 33, 34, 35, 43, 44, 45] for + child in Mantis.FunctionSpaces.get_element_children(operators[2], parent) +] marked_elements_per_level = [Int[], level_2_marked_elements, level_3_marked_elements] -hier_space = Mantis.FunctionSpaces.HierarchicalFiniteElementSpace(spaces, operators, marked_elements_per_level, nsubs, true) +hier_space = Mantis.FunctionSpaces.HierarchicalFiniteElementSpace( + spaces, operators, marked_elements_per_level, nsubs, true +) -qrule = Mantis.Quadrature.tensor_product_rule((deg1+1, deg2+1), Mantis.Quadrature.gauss_legendre) +qrule = Mantis.Quadrature.tensor_product_rule( + (deg1+1, deg2+1), Mantis.Quadrature.gauss_legendre +) xi = Mantis.Quadrature.get_nodes(qrule) # Tests for coefficients and evaluation diff --git a/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductTests.jl b/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductTests.jl index 0330a11db..6d245bcb6 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductTests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/TensorProductTests.jl @@ -223,9 +223,7 @@ basic_tests(TP_B1_b, answers_1b) # Reduction test, single-patch, single element, 1D, Cartesian, degree 3 Lagrange. nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_lobatto(4)))[1] ll_polynomial = FunctionSpaces.Lagrange(nodes) -L3 = FunctionSpaces.BSplineSpace( - geometry1, geometry1, ll_polynomial, [-1, -1] -) +L3 = FunctionSpaces.BSplineSpace(geometry1, geometry1, ll_polynomial, [-1, -1]) TP_L3 = FunctionSpaces.TensorProductSpace((L3,)) answers_L3 = ( 1, diff --git a/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/runtests.jl b/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/runtests.jl index 1e4f54951..d5a23c71e 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/runtests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/TensorProductSpaces/runtests.jl @@ -2,6 +2,8 @@ module TensorProductSpacesTests using Test -@testset "TensorProductSpaces" begin include("TensorProductTests.jl") end +@testset "TensorProductSpaces" begin + include("TensorProductTests.jl") +end end diff --git a/test/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelationsTests.jl b/test/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelationsTests.jl index e4677539b..5d7fa28fd 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelationsTests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/TwoScaleRelations/UnstructuredTwoScaleRelationsTests.jl @@ -127,8 +127,9 @@ size_tp_coarse = size(geom_coeffs_coarse) TS_tp, space_tp_fine = FunctionSpaces.build_two_scale_operator( FunctionSpaces.get_degenerate_space(P_scalar_coarse), (num_subdiv_p, num_subdiv_r) ) -size_tp_fine = - FunctionSpaces.get_num_basis.(FunctionSpaces.get_constituent_spaces(space_tp_fine)) +size_tp_fine = FunctionSpaces.get_num_basis.( + FunctionSpaces.get_constituent_spaces(space_tp_fine) +) subdiv_mat_tp = FunctionSpaces.get_global_subdiv_matrix(TS_tp) geom_coeffs_fine = reshape( subdiv_mat_tp * reshape(geom_coeffs_coarse, :, size_tp_coarse[3]), diff --git a/test/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/runtests.jl b/test/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/runtests.jl index 2fb8fc80e..e9052b00f 100644 --- a/test/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/runtests.jl +++ b/test/FunctionSpaces/FiniteElementSpaces/UnstructuredSpaces/runtests.jl @@ -2,7 +2,11 @@ module UnstructuredSpacesTests using Test -@testset "GTBSplines" begin include("GTBSplinesTests.jl") end -@testset "Polarsplines" begin include("PolarSplineTests.jl") end +@testset "GTBSplines" begin + include("GTBSplinesTests.jl") +end +@testset "Polarsplines" begin + include("PolarSplineTests.jl") +end end diff --git a/test/Geometry/CartesianGeometryTests.jl b/test/Geometry/CartesianGeometryTests.jl index bbe6820ff..a9ffe7c6d 100644 --- a/test/Geometry/CartesianGeometryTests.jl +++ b/test/Geometry/CartesianGeometryTests.jl @@ -218,10 +218,10 @@ for i in 1:Geometry.get_num_elements(geometryMP100) hess = Geometry.hessian(geometryMP100, i, Points.CartesianPoints(([0.0, 1.0],))) for p in eachindex(jac, hess) - if !all(isapprox.(jac[p][:, :], [1.0 / i], rtol=1e-14)) + if !all(isapprox.(jac[p][:, :], [1.0 / i]; rtol=1e-14)) all_jac_MP100 = false end - if !all(isapprox.(hess[p][1][:, :], [0.0], rtol=1e-14)) + if !all(isapprox.(hess[p][1][:, :], [0.0]; rtol=1e-14)) all_hess_MP100 = false end end diff --git a/test/Geometry/DiscreteGeometryTests.jl b/test/Geometry/DiscreteGeometryTests.jl index ebd765b74..42885aec2 100644 --- a/test/Geometry/DiscreteGeometryTests.jl +++ b/test/Geometry/DiscreteGeometryTests.jl @@ -68,7 +68,9 @@ run_tests(geom, file_name) # Lagrange-Bernstein (Square with hole) # ############################################################################################ deg = 1 -nodes = Points.get_constituent_points(Quadrature.get_nodes(Quadrature.gauss_lobatto(deg+1)))[1] +nodes = Points.get_constituent_points( + Quadrature.get_nodes(Quadrature.gauss_lobatto(deg+1)) +)[1] b = FunctionSpaces.Lagrange(nodes) B1 = FunctionSpaces.BSplineSpace( Geometry.CartesianGeometry(([0.0, 1.0, 2.0, 3.0, 4.0],)), b, [-1, 0, 0, 0, -1] @@ -138,8 +140,8 @@ geom_coeffs_0 = [ r0 = 1 r1 = 2 geom_coeffs = [ - geom_coeffs_0.*r0 [-1.0, 1.0, -1.0, 1.0] - geom_coeffs_0.*r1 [1.0, -1.0, 1.0, -1.0] + geom_coeffs_0 .* r0 [-1.0, 1.0, -1.0, 1.0] + geom_coeffs_0 .* r1 [1.0, -1.0, 1.0, -1.0] ] geom = FunctionSpaces.DiscreteGeometry(TP, geom_coeffs) file_name = "fem_geometry_wavy_surface_test" @@ -163,8 +165,8 @@ geom_coeffs_0 = [ r0 = 1 r1 = 2 geom_coeffs = [ - geom_coeffs_0.*r0 zeros(3) - geom_coeffs_0.*r1 zeros(3) + geom_coeffs_0 .* r0 zeros(3) + geom_coeffs_0 .* r1 zeros(3) ] geom = FunctionSpaces.DiscreteGeometry(TP, geom_coeffs) file_name = "fem_geometry_nurbs_quarter_annulus_test" @@ -191,8 +193,8 @@ geom_coeffs_0 = [ r0 = 1 r1 = 2 geom_coeffs = [ - geom_coeffs_0.*r0 zeros(4) - geom_coeffs_0.*r1 zeros(4) + geom_coeffs_0 .* r0 zeros(4) + geom_coeffs_0 .* r1 zeros(4) ] geom = FunctionSpaces.DiscreteGeometry(TP, geom_coeffs) file_name = "fem_geometry_nurbs_annulus_test" @@ -219,8 +221,8 @@ geom_coeffs_0 = [ r0 = 1 r1 = 2 geom_coeffs = [ - geom_coeffs_0.*r0 [-1.0, 1.0, -1.0, 1.0] - geom_coeffs_0.*r1 [1.0, -1.0, 1.0, -1.0] + geom_coeffs_0 .* r0 [-1.0, 1.0, -1.0, 1.0] + geom_coeffs_0 .* r1 [1.0, -1.0, 1.0, -1.0] ] geom = FunctionSpaces.DiscreteGeometry(TP, geom_coeffs) file_name = "fem_geometry_nurbs_wavy_surface_test" @@ -261,8 +263,8 @@ geom_coeffs_0 = [ r0 = 1 r1 = 2 geom_coeffs = [ - geom_coeffs_0.*r0 zeros(4) - geom_coeffs_0.*r1 zeros(4) + geom_coeffs_0 .* r0 zeros(4) + geom_coeffs_0 .* r1 zeros(4) ] # NURBS annulus with B-spline and NURBS bases diff --git a/test/Geometry/MappedGeometryTests.jl b/test/Geometry/MappedGeometryTests.jl index 674e12c36..20f58850c 100644 --- a/test/Geometry/MappedGeometryTests.jl +++ b/test/Geometry/MappedGeometryTests.jl @@ -357,7 +357,7 @@ for (k, IJ) in enumerate(CartesianIndices((4, 4))) ] jactest = true for p in eachindex(jac) - if !all(isapprox.(jac[p][:, :], [0.25 0.0; 0.0 0.25; yans[p] xans[p]], rtol=1e-14)) + if !all(isapprox.(jac[p][:, :], [0.25 0.0; 0.0 0.25; yans[p] xans[p]]; rtol=1e-14)) jactest = false end end @@ -365,15 +365,15 @@ for (k, IJ) in enumerate(CartesianIndices((4, 4))) hesstest = true for p in eachindex(hess) - if !all(isapprox.(hess[p][1][:, :], [0.0 0.0; 0.0 0.0], atol=1e-14)) + if !all(isapprox.(hess[p][1][:, :], [0.0 0.0; 0.0 0.0]; atol=1e-14)) println(1) hesstest = false end - if !all(isapprox.(hess[p][2][:, :], [0.0 0.0; 0.0 0.0], atol=1e-14)) + if !all(isapprox.(hess[p][2][:, :], [0.0 0.0; 0.0 0.0]; atol=1e-14)) println(2) hesstest = false end - if !all(isapprox.(hess[p][3][:, :], [0.0 1.0/16.0; 1.0/16.0 0.0], rtol=1e-14)) + if !all(isapprox.(hess[p][3][:, :], [0.0 1.0/16.0; 1.0/16.0 0.0]; rtol=1e-14)) println(3) hesstest = false end diff --git a/test/Geometry/MetricTests.jl b/test/Geometry/MetricTests.jl index 450bcda5d..098ffbea1 100644 --- a/test/Geometry/MetricTests.jl +++ b/test/Geometry/MetricTests.jl @@ -180,7 +180,7 @@ for (k, IJ) in enumerate(CartesianIndices((4, 4))) inv_g[p], 1.0 / ((0.0625 + yans[p]^2) * (0.0625 + xans[p]^2) - (xans[p] * yans[p])^2) * - [0.0625+xans[p]^2 -xans[p]*yans[p]; -xans[p]*yans[p] 0.0625+yans[p]^2], + [0.0625+xans[p]^2 -xans[p]*yans[p]; -xans[p]*yans[p] 0.0625+yans[p]^2]; rtol=1e-14, ), ) @@ -189,7 +189,7 @@ for (k, IJ) in enumerate(CartesianIndices((4, 4))) if !all( isapprox.( g[p][:, :], - [0.0625+yans[p]^2 xans[p]*yans[p]; xans[p]*yans[p] 0.0625+xans[p]^2], + [0.0625+yans[p]^2 xans[p]*yans[p]; xans[p]*yans[p] 0.0625+xans[p]^2]; rtol=1e-14, ), ) @@ -197,15 +197,15 @@ for (k, IJ) in enumerate(CartesianIndices((4, 4))) end end if !all( - isapprox.( - det_g, - sqrt.([ - (0.0625 + yans[p]^2) * (0.0625 + xans[p]^2) - (xans[p] * yans[p])^2 for - p in eachindex(det_g) - ]), - rtol=1e-14, - ), - ) + isapprox.( + det_g, + sqrt.([ + (0.0625 + yans[p]^2) * (0.0625 + xans[p]^2) - (xans[p] * yans[p])^2 for + p in eachindex(det_g) + ]); + rtol=1e-14, + ), + ) sqrt_test_geo23 = false end end @@ -264,37 +264,37 @@ dgdx2_test_geo23ext = true dinvg1_test_geo23ext = true dinvg2_test_geo23ext = true for p in eachindex(xi) - if !all(isapprox.(J[p], Jans(xi[p]...), rtol=1e-14)) + if !all(isapprox.(J[p], Jans(xi[p]...); rtol=1e-14)) J_test_geo23ext = false end - if !all(isapprox.(g[p], gans(xi[p]...), rtol=1e-14)) + if !all(isapprox.(g[p], gans(xi[p]...); rtol=1e-14)) g_test_geo23ext = false end - if !all(isapprox.(sqrt_g[p], sqrtgans(xi[p]...), rtol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans(xi[p]...); rtol=1e-14)) sqrt_test_geo23ext = false end - if !all(isapprox.(inv_g[p], ginvans(xi[p]...), rtol=1e-14)) + if !all(isapprox.(inv_g[p], ginvans(xi[p]...); rtol=1e-14)) inv_g_test_geo23ext = false end - if !all(isapprox.(Hs[p][1], Hans(xi[p]...)[1], rtol=1e-14)) + if !all(isapprox.(Hs[p][1], Hans(xi[p]...)[1]; rtol=1e-14)) H1_test_geo23ext = false end - if !all(isapprox.(Hs[p][2], Hans(xi[p]...)[2], rtol=1e-14)) + if !all(isapprox.(Hs[p][2], Hans(xi[p]...)[2]; rtol=1e-14)) H2_test_geo23ext = false end - if !all(isapprox.(Hs[p][3], Hans(xi[p]...)[3], rtol=1e-14)) + if !all(isapprox.(Hs[p][3], Hans(xi[p]...)[3]; rtol=1e-14)) H3_test_geo23ext = false end - if !all(isapprox.(dgdxs[1][p], dgduans(xi[p]...), rtol=1e-14)) + if !all(isapprox.(dgdxs[1][p], dgduans(xi[p]...); rtol=1e-14)) dgdx1_test_geo23ext = false end - if !all(isapprox.(dgdxs[2][p], dgdvans(xi[p]...), rtol=1e-14)) + if !all(isapprox.(dgdxs[2][p], dgdvans(xi[p]...); rtol=1e-14)) dgdx2_test_geo23ext = false end - if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans(xi[p]...), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans(xi[p]...); rtol=1e-12)) dinvg1_test_geo23ext = false end - if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans(xi[p]...), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans(xi[p]...); rtol=1e-12)) dinvg2_test_geo23ext = false end end @@ -354,37 +354,37 @@ for (k, IJ) in enumerate(CartesianIndices((3, 4))) uv = Geometry.evaluate(geom_cart_ext2, k, xi) for p in eachindex(xi) u, v = uv[p, :] - if !all(isapprox.(J[p], Jans_geo232(u, v), rtol=1e-12)) + if !all(isapprox.(J[p], Jans_geo232(u, v); rtol=1e-12)) J_test_geo23ext2 = false end - if !all(isapprox.(g[p], gans_geo232(u, v), rtol=1e-14)) + if !all(isapprox.(g[p], gans_geo232(u, v); rtol=1e-14)) g_test_geo23ext2 = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_geo232(u, v), rtol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans_geo232(u, v); rtol=1e-14)) sqrt_test_geo23ext2 = false end - if !all(isapprox.(inv_g[p], ginvans_geo232(u, v), rtol=1e-14)) + if !all(isapprox.(inv_g[p], ginvans_geo232(u, v); rtol=1e-14)) inv_g_test_geo23ext2 = false end - if !all(isapprox.(Hs[p][1], Hans_geo232(u, v)[1], rtol=1e-14)) + if !all(isapprox.(Hs[p][1], Hans_geo232(u, v)[1]; rtol=1e-14)) H1_test_geo23ext2 = false end - if !all(isapprox.(Hs[p][2], Hans_geo232(u, v)[2], rtol=1e-14)) + if !all(isapprox.(Hs[p][2], Hans_geo232(u, v)[2]; rtol=1e-14)) H2_test_geo23ext2 = false end - if !all(isapprox.(Hs[p][3], Hans_geo232(u, v)[3], rtol=1e-14)) + if !all(isapprox.(Hs[p][3], Hans_geo232(u, v)[3]; rtol=1e-14)) H3_test_geo23ext2 = false end - if !all(isapprox.(dgdxs[1][p], dgduans_geo232(u, v), rtol=1e-14)) + if !all(isapprox.(dgdxs[1][p], dgduans_geo232(u, v); rtol=1e-14)) dgdx1_test_geo23ext2 = false end - if !all(isapprox.(dgdxs[2][p], dgdvans_geo232(u, v), rtol=1e-14)) + if !all(isapprox.(dgdxs[2][p], dgdvans_geo232(u, v); rtol=1e-14)) dgdx2_test_geo23ext2 = false end - if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_geo232(u, v), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_geo232(u, v); rtol=1e-12)) dinvg1_test_geo23ext2 = false end - if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_geo232(u, v), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_geo232(u, v); rtol=1e-12)) dinvg2_test_geo23ext2 = false end end @@ -455,37 +455,37 @@ for (k, IJ) in enumerate(CartesianIndices((1, 1))) uv = Geometry.evaluate(geometry_exp12, k, xi_exp12) for p in eachindex(xi_exp12) u, v = uv[p, :] - if !all(isapprox.(J[p], Jans_exp12(u, v), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(J[p], Jans_exp12(u, v); rtol=1e-14, atol=1e-14)) J_test_exp12 = false end - if !all(isapprox.(g[p], gans_exp12(u, v), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(g[p], gans_exp12(u, v); rtol=1e-14, atol=1e-14)) g_test_exp12 = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_exp12(u, v), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans_exp12(u, v); rtol=1e-14, atol=1e-14)) sqrt_test_exp12 = false end - if !all(isapprox.(inv_g[p], ginvans_exp12(u, v), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(inv_g[p], ginvans_exp12(u, v); rtol=1e-14, atol=1e-14)) inv_g_test_exp12 = false end - if !all(isapprox.(Hs[p][1], Hans_exp12(u, v)[1], rtol=1e-14, atol=1e-14)) + if !all(isapprox.(Hs[p][1], Hans_exp12(u, v)[1]; rtol=1e-14, atol=1e-14)) H1_test_exp12 = false end - if !all(isapprox.(Hs[p][2], Hans_exp12(u, v)[2], rtol=1e-14, atol=1e-14)) + if !all(isapprox.(Hs[p][2], Hans_exp12(u, v)[2]; rtol=1e-14, atol=1e-14)) H2_test_exp12 = false end - if !all(isapprox.(dgdxs[1][p], dgduans_exp12(u, v), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(dgdxs[1][p], dgduans_exp12(u, v); rtol=1e-14, atol=1e-14)) dgdx1_test_exp12 = false end - if !all(isapprox.(dgdxs[2][p], dgdvans_exp12(u, v), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(dgdxs[2][p], dgdvans_exp12(u, v); rtol=1e-14, atol=1e-14)) dgdx2_test_exp12 = false end if !all( - isapprox.(dinv_g_dxs[1][p], dginvgduans_exp12(u, v), rtol=1e-14, atol=1e-14) + isapprox.(dinv_g_dxs[1][p], dginvgduans_exp12(u, v); rtol=1e-14, atol=1e-14) ) dinvg1_test_exp12 = false end if !all( - isapprox.(dinv_g_dxs[2][p], dginvgdvans_exp12(u, v), rtol=1e-14, atol=1e-14) + isapprox.(dinv_g_dxs[2][p], dginvgdvans_exp12(u, v); rtol=1e-14, atol=1e-14) ) dinvg2_test_exp12 = false end @@ -532,34 +532,34 @@ dgdx2_test_cb = true dinvg1_test_cb = true dinvg2_test_cb = true for p in eachindex(xi) - if !all(isapprox.(J[p], Jans_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(J[p], Jans_cart_box(xi[p]...); rtol=1e-14)) J_test_cb = false end - if !all(isapprox.(g[p], gans_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(g[p], gans_cart_box(xi[p]...); rtol=1e-14)) g_test_cb = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans_cart_box(xi[p]...); rtol=1e-14)) sqrt_test_cb = false end - if !all(isapprox.(inv_g[p], ginvans_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(inv_g[p], ginvans_cart_box(xi[p]...); rtol=1e-14)) inv_g_test_cb = false end - if !all(isapprox.(Hs[p][1], Hans_cart_box(xi[p]...)[1], rtol=1e-14)) + if !all(isapprox.(Hs[p][1], Hans_cart_box(xi[p]...)[1]; rtol=1e-14)) H1_test_cb = false end - if !all(isapprox.(Hs[p][2], Hans_cart_box(xi[p]...)[2], rtol=1e-14)) + if !all(isapprox.(Hs[p][2], Hans_cart_box(xi[p]...)[2]; rtol=1e-14)) H2_test_cb = false end - if !all(isapprox.(dgdxs[1][p], dgduans_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(dgdxs[1][p], dgduans_cart_box(xi[p]...); rtol=1e-14)) dgdx1_test_cb = false end - if !all(isapprox.(dgdxs[2][p], dgdvans_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(dgdxs[2][p], dgdvans_cart_box(xi[p]...); rtol=1e-14)) dgdx2_test_cb = false end - if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_cart_box(xi[p]...), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_cart_box(xi[p]...); rtol=1e-12)) dinvg1_test_cb = false end - if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_cart_box(xi[p]...), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_cart_box(xi[p]...); rtol=1e-12)) dinvg2_test_cb = false end end @@ -592,7 +592,11 @@ Jans_curv(u, v) = [ (t - b)/(r - l)*hx*pi*c*cospi(u)*sinpi(v) (1.0 + pi * c * sinpi(u) * cospi(v))*hy ] gans_curv(u, v) = [ - (Jans_curv(u, v)[1, 1])^2+(Jans_curv(u, v)[2, 1])^2 Jans_curv(u, v)[1, 1] * Jans_curv(u, v)[1, 2]+Jans_curv(u, v)[2, 1] * Jans_curv(u, v)[2, 2] + (Jans_curv(u, v)[1, 1])^2+(Jans_curv(u, v)[2, 1])^2 Jans_curv(u, v)[1, 1] * Jans_curv(u, v)[1, 2]+Jans_curv(u, v)[2, 1] * Jans_curv( + u, v + )[ + 2, 2 + ] Jans_curv(u, v)[1, 1] * Jans_curv(u, v)[1, 2]+Jans_curv(u, v)[2, 1] * Jans_curv(u, v)[2, 2] (Jans_curv(u, v)[1, 2])^2+(Jans_curv(u, v)[2, 2])^2 ] sqrtgans_curv(u, v) = sqrt(det(gans_curv(u, v))) @@ -611,16 +615,16 @@ for (k, IJ) in enumerate(CartesianIndices((3, 4))) u, v = uv[p, :] u = (2.0 / (r - l)) * uv[p, 1] - 2.0 * l / (r - l) - 1.0 v = (2.0 / (t - b)) * uv[p, 2] - 2.0 * b / (t - b) - 1.0 - if !all(isapprox.(J[p], Jans_curv(u, v), rtol=1e-12)) + if !all(isapprox.(J[p], Jans_curv(u, v); rtol=1e-12)) J_test_cg = false end - if !all(isapprox.(g[p], gans_curv(u, v), rtol=1e-12)) + if !all(isapprox.(g[p], gans_curv(u, v); rtol=1e-12)) g_test_cg = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_curv(u, v), rtol=1e-12)) + if !all(isapprox.(sqrt_g[p], sqrtgans_curv(u, v); rtol=1e-12)) sqrt_test_cg = false end - if !all(isapprox.(inv_g[p], invgans_curv(u, v), rtol=1e-12)) + if !all(isapprox.(inv_g[p], invgans_curv(u, v); rtol=1e-12)) inv_g_test_cg = false end end @@ -634,12 +638,10 @@ end # TensorProductGeometry -------------------------------------------------------------------- # TensorProductGeometry: TensorProduct equivalent of Cartesian Box ------------------------- -tp_cart_box = Geometry.TensorProductGeometry( - ( - Geometry.CartesianGeometry((LinRange(0.0, 1.0, 7))), - Geometry.CartesianGeometry((LinRange(0.0, 1.0, 8))), - ) -) +tp_cart_box = Geometry.TensorProductGeometry(( + Geometry.CartesianGeometry((LinRange(0.0, 1.0, 7))), + Geometry.CartesianGeometry((LinRange(0.0, 1.0, 8))), +)) Jans_tp_cart_box(u, v) = [1.0/7.0 0.0; 0.0 1.0/8.0] gans_tp_cart_box(u, v) = [1.0/49.0 0.0; 0.0 1.0/64.0] sqrtgans_tp_cart_box(u, v) = sqrt((1.0 / 49.0) * (1.0 / 64.0)) @@ -665,34 +667,34 @@ dgdx2_test_tpcb = true dinvg1_test_tpcb = true dinvg2_test_tpcb = true for p in eachindex(xi) - if !all(isapprox.(J[p], Jans_tp_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(J[p], Jans_tp_cart_box(xi[p]...); rtol=1e-14)) J_test_tpcb = false end - if !all(isapprox.(g[p], gans_tp_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(g[p], gans_tp_cart_box(xi[p]...); rtol=1e-14)) g_test_tpcb = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_tp_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans_tp_cart_box(xi[p]...); rtol=1e-14)) sqrt_test_tpcb = false end - if !all(isapprox.(inv_g[p], ginvans_tp_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(inv_g[p], ginvans_tp_cart_box(xi[p]...); rtol=1e-14)) inv_g_test_tpcb = false end - if !all(isapprox.(Hs[p][1], Hans_tp_cart_box(xi[p]...)[1], rtol=1e-14)) + if !all(isapprox.(Hs[p][1], Hans_tp_cart_box(xi[p]...)[1]; rtol=1e-14)) H1_test_tpcb = false end - if !all(isapprox.(Hs[p][2], Hans_tp_cart_box(xi[p]...)[2], rtol=1e-14)) + if !all(isapprox.(Hs[p][2], Hans_tp_cart_box(xi[p]...)[2]; rtol=1e-14)) H2_test_tpcb = false end - if !all(isapprox.(dgdxs[1][p], dgduans_tp_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(dgdxs[1][p], dgduans_tp_cart_box(xi[p]...); rtol=1e-14)) dgdx1_test_tpcb = false end - if !all(isapprox.(dgdxs[2][p], dgdvans_tp_cart_box(xi[p]...), rtol=1e-14)) + if !all(isapprox.(dgdxs[2][p], dgdvans_tp_cart_box(xi[p]...); rtol=1e-14)) dgdx2_test_tpcb = false end - if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_tp_cart_box(xi[p]...), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_tp_cart_box(xi[p]...); rtol=1e-12)) dinvg1_test_tpcb = false end - if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_tp_cart_box(xi[p]...), rtol=1e-12)) + if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_tp_cart_box(xi[p]...); rtol=1e-12)) dinvg2_test_tpcb = false end end @@ -719,36 +721,32 @@ const dz = H / n_z circle_cart = Geometry.CartesianGeometry((LinRange(0.0, 2*π, n_theta + 1),)) circle_map_geo(theta) = [R * cos(theta[1]), R * sin(theta[1])] circle_map_dgeo(theta) = [-R * sin(theta[1]) R * cos(theta[1])] -circle_map_d2geo(theta) = ( - [-R * cos(theta[1]);;], - [-R * sin(theta[1]);;], -) +circle_map_d2geo(theta) = ([-R * cos(theta[1]);;], [-R * sin(theta[1]);;]) circle_mapping = Geometry.Mapping((1, 2), circle_map_geo, circle_map_dgeo, circle_map_d2geo) geo_circle = Geometry.MappedGeometry(circle_cart, circle_mapping) geo_z = Geometry.CartesianGeometry((LinRange(0.0, H, n_z + 1),)) -cyl_uv_domain = Geometry.CartesianGeometry( - (LinRange(0.0, 2*π, n_theta + 1), LinRange(0.0, H, n_z + 1)) -) +cyl_uv_domain = Geometry.CartesianGeometry(( + LinRange(0.0, 2*π, n_theta + 1), LinRange(0.0, H, n_z + 1) +)) cylinder = Geometry.TensorProductGeometry((geo_circle, geo_z)) -Jans_tp_cyl(u, v) = SMatrix{3,2}( - -R * sin(u) * dtheta, R * cos(u) * dtheta, 0.0, 0.0, 0.0, dz -) -gans_tp_cyl(u, v) = SMatrix{2,2}(R^2 * dtheta^2, 0.0, 0.0, dz^2) +Jans_tp_cyl(u, v) = + SMatrix{3, 2}(-R * sin(u) * dtheta, R * cos(u) * dtheta, 0.0, 0.0, 0.0, dz) +gans_tp_cyl(u, v) = SMatrix{2, 2}(R^2 * dtheta^2, 0.0, 0.0, dz^2) sqrtgans_tp_cyl(u, v) = R * dtheta * dz -ginvans_tp_cyl(u, v) = SMatrix{2,2}(1.0/(R^2 * dtheta^2), 0.0, 0.0, 1.0/dz^2) +ginvans_tp_cyl(u, v) = SMatrix{2, 2}(1.0/(R^2 * dtheta^2), 0.0, 0.0, 1.0/dz^2) Hans_tp_cyl(u, v) = ( - SMatrix{2,2}(-R * cos(u) * dtheta^2, 0.0, 0.0, 0.0), - SMatrix{2,2}(-R * sin(u) * dtheta^2, 0.0, 0.0, 0.0), - SMatrix{2,2}(0.0, 0.0, 0.0, 0.0), + SMatrix{2, 2}(-R * cos(u) * dtheta^2, 0.0, 0.0, 0.0), + SMatrix{2, 2}(-R * sin(u) * dtheta^2, 0.0, 0.0, 0.0), + SMatrix{2, 2}(0.0, 0.0, 0.0, 0.0), ) -dgduans_tp_cyl(u, v) = SMatrix{2,2}(0.0, 0.0, 0.0, 0.0) -dgdvans_tp_cyl(u, v) = SMatrix{2,2}(0.0, 0.0, 0.0, 0.0) -dginvgduans_tp_cyl(u, v) = SMatrix{2,2}(0.0, 0.0, 0.0, 0.0) -dginvgdvans_tp_cyl(u, v) = SMatrix{2,2}(0.0, 0.0, 0.0, 0.0) +dgduans_tp_cyl(u, v) = SMatrix{2, 2}(0.0, 0.0, 0.0, 0.0) +dgdvans_tp_cyl(u, v) = SMatrix{2, 2}(0.0, 0.0, 0.0, 0.0) +dginvgduans_tp_cyl(u, v) = SMatrix{2, 2}(0.0, 0.0, 0.0, 0.0) +dginvgdvans_tp_cyl(u, v) = SMatrix{2, 2}(0.0, 0.0, 0.0, 0.0) xi_cyl = Points.CartesianPoints((LinRange(0.0, 1.0, 6), LinRange(0.0, 1.0, 7))) J_test_tpcyl = true @@ -769,37 +767,37 @@ for (k, IJ) in enumerate(CartesianIndices((n_theta, n_z))) uv = Geometry.evaluate(cyl_uv_domain, k, xi_cyl) for p in eachindex(xi_cyl) u, v = uv[p, :] - if !all(isapprox.(J[p], Jans_tp_cyl(u, v), rtol=1e-14)) + if !all(isapprox.(J[p], Jans_tp_cyl(u, v); rtol=1e-14)) J_test_tpcyl = false end - if !all(isapprox.(g[p], gans_tp_cyl(u, v), rtol=1e-14)) + if !all(isapprox.(g[p], gans_tp_cyl(u, v); rtol=1e-14)) g_test_tpcyl = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_tp_cyl(u, v), rtol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans_tp_cyl(u, v); rtol=1e-14)) sqrt_test_tpcyl = false end - if !all(isapprox.(inv_g[p], ginvans_tp_cyl(u, v), rtol=1e-14)) + if !all(isapprox.(inv_g[p], ginvans_tp_cyl(u, v); rtol=1e-14)) inv_g_test_tpcyl = false end - if !all(isapprox.(Hs[p][1], Hans_tp_cyl(u, v)[1], rtol=1e-14)) + if !all(isapprox.(Hs[p][1], Hans_tp_cyl(u, v)[1]; rtol=1e-14)) H1_test_tpcyl = false end - if !all(isapprox.(Hs[p][2], Hans_tp_cyl(u, v)[2], rtol=1e-14)) + if !all(isapprox.(Hs[p][2], Hans_tp_cyl(u, v)[2]; rtol=1e-14)) H2_test_tpcyl = false end - if !all(isapprox.(Hs[p][3], Hans_tp_cyl(u, v)[3], rtol=1e-14)) + if !all(isapprox.(Hs[p][3], Hans_tp_cyl(u, v)[3]; rtol=1e-14)) H3_test_tpcyl = false end - if !all(isapprox.(dgdxs[1][p], dgduans_tp_cyl(u, v), atol=1e-14)) + if !all(isapprox.(dgdxs[1][p], dgduans_tp_cyl(u, v); atol=1e-14)) dgdx1_test_tpcyl = false end - if !all(isapprox.(dgdxs[2][p], dgdvans_tp_cyl(u, v), atol=1e-14)) + if !all(isapprox.(dgdxs[2][p], dgdvans_tp_cyl(u, v); atol=1e-14)) dgdx2_test_tpcyl = false end - if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_tp_cyl(u, v), atol=1e-14)) + if !all(isapprox.(dinv_g_dxs[1][p], dginvgduans_tp_cyl(u, v); atol=1e-14)) dinvg1_test_tpcyl = false end - if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_tp_cyl(u, v), atol=1e-14)) + if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_tp_cyl(u, v); atol=1e-14)) dinvg2_test_tpcyl = false end end @@ -827,17 +825,23 @@ const dr = R_cyl / n_r const dtheta_cyl = 2π / n_theta_cyl const dz_cyl = H_cyl / n_z_cyl -disk_cart = Geometry.CartesianGeometry( - (LinRange(0.0, R_cyl, n_r + 1), LinRange(0.0, 2π, n_theta_cyl + 1)) -) +disk_cart = Geometry.CartesianGeometry(( + LinRange(0.0, R_cyl, n_r + 1), LinRange(0.0, 2π, n_theta_cyl + 1) +)) disk_map_geo(x) = [x[1] * cos(x[2]), x[1] * sin(x[2])] -disk_map_dgeo(x) = [cos(x[2]) -x[1]*sin(x[2]); - sin(x[2]) x[1]*cos(x[2])] +disk_map_dgeo(x) = [ + cos(x[2]) -x[1]*sin(x[2]); + sin(x[2]) x[1]*cos(x[2]) +] disk_map_d2geo(x) = ( - [0.0 -sin(x[2]); - -sin(x[2]) -x[1]*cos(x[2])], - [0.0 cos(x[2]); - cos(x[2]) -x[1]*sin(x[2])], + [ + 0.0 -sin(x[2]); + -sin(x[2]) -x[1]*cos(x[2]) + ], + [ + 0.0 cos(x[2]); + cos(x[2]) -x[1]*sin(x[2]) + ], ) disk_mapping = Geometry.Mapping((2, 2), disk_map_geo, disk_map_dgeo, disk_map_d2geo) geo_disk = Geometry.MappedGeometry(disk_cart, disk_mapping) @@ -852,8 +856,7 @@ solid_cyl_uvw_domain = Geometry.CartesianGeometry(( solid_cylinder = Geometry.TensorProductGeometry((geo_disk, geo_z_cyl)) - -Jans_solid_cyl(r, theta, z) = SMatrix{3,3}( +Jans_solid_cyl(r, theta, z) = SMatrix{3, 3}( cos(theta)*dr, sin(theta)*dr, 0.0, @@ -864,56 +867,51 @@ Jans_solid_cyl(r, theta, z) = SMatrix{3,3}( 0.0, dz_cyl, ) -gans_solid_cyl(r, theta, z) = SMatrix{3,3}( - dr^2, 0.0, 0.0, 0.0, r^2 * dtheta_cyl^2, 0.0, 0.0, 0.0, dz_cyl^2 -) +gans_solid_cyl(r, theta, z) = + SMatrix{3, 3}(dr^2, 0.0, 0.0, 0.0, r^2 * dtheta_cyl^2, 0.0, 0.0, 0.0, dz_cyl^2) sqrtgans_solid_cyl(r, theta, z) = r * dr * dtheta_cyl * dz_cyl -ginvans_solid_cyl(r, theta, z) = SMatrix{3,3}( +ginvans_solid_cyl(r, theta, z) = SMatrix{3, 3}( 1.0/dr^2, 0.0, 0.0, 0.0, 1.0/(r^2 * dtheta_cyl^2), 0.0, 0.0, 0.0, 1.0/dz_cyl^2 ) Hans_solid_cyl(r, theta, z) = ( - SMatrix{3,3}( - 0.0, - -sin(theta)*dr*dtheta_cyl, - 0.0, - -sin(theta)*dr*dtheta_cyl, - -r*cos(theta)*dtheta_cyl^2, - 0.0, - 0.0, - 0.0, - 0.0, + SMatrix{3, 3}( + 0.0, + -sin(theta)*dr*dtheta_cyl, + 0.0, + -sin(theta)*dr*dtheta_cyl, + -r*cos(theta)*dtheta_cyl^2, + 0.0, + 0.0, + 0.0, + 0.0, ), - SMatrix{3,3}( - 0.0, - cos(theta)*dr*dtheta_cyl, - 0.0, - cos(theta)*dr*dtheta_cyl, - -r*sin(theta)*dtheta_cyl^2, - 0.0, - 0.0, - 0.0, - 0.0, + SMatrix{3, 3}( + 0.0, + cos(theta)*dr*dtheta_cyl, + 0.0, + cos(theta)*dr*dtheta_cyl, + -r*sin(theta)*dtheta_cyl^2, + 0.0, + 0.0, + 0.0, + 0.0, ), - SMatrix{3,3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), -) -dgduans_solid_cyl(r, theta, z) = SMatrix{3,3}( - 0.0, 0.0, 0.0, 0.0, 2.0 * r * dtheta_cyl^2 * dr, 0.0, 0.0, 0.0, 0.0 -) -dgdvans_solid_cyl(r, theta, z) = SMatrix{3,3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) -dgdwans_solid_cyl(r, theta, z) = SMatrix{3,3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) -dginvgduans_solid_cyl(r, theta, z) = SMatrix{3,3}( - 0.0, 0.0, 0.0, 0.0, -2.0 * dr / (r^3 * dtheta_cyl^2), 0.0, 0.0, 0.0, 0.0 -) -dginvgdvans_solid_cyl(r, theta, z) = SMatrix{3,3}( - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 -) -dginvgdwans_solid_cyl(r, theta, z) = SMatrix{3,3}( - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 -) - -xi_solid_cyl = Points.CartesianPoints( - (LinRange(0.1, 1.0, 4), LinRange(0.0, 1.0, 5), LinRange(0.0, 1.0, 3)) + SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), ) +dgduans_solid_cyl(r, theta, z) = + SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, 2.0 * r * dtheta_cyl^2 * dr, 0.0, 0.0, 0.0, 0.0) +dgdvans_solid_cyl(r, theta, z) = SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) +dgdwans_solid_cyl(r, theta, z) = SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) +dginvgduans_solid_cyl(r, theta, z) = + SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, -2.0 * dr / (r^3 * dtheta_cyl^2), 0.0, 0.0, 0.0, 0.0) +dginvgdvans_solid_cyl(r, theta, z) = + SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) +dginvgdwans_solid_cyl(r, theta, z) = + SMatrix{3, 3}(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + +xi_solid_cyl = Points.CartesianPoints(( + LinRange(0.1, 1.0, 4), LinRange(0.0, 1.0, 5), LinRange(0.0, 1.0, 3) +)) J_test_scyl = true g_test_scyl = true @@ -935,65 +933,53 @@ for (k, IJK) in enumerate(CartesianIndices((n_r, n_theta_cyl, n_z_cyl))) uvw = Geometry.evaluate(solid_cyl_uvw_domain, k, xi_solid_cyl) for p in eachindex(xi_solid_cyl) r, theta, z = uvw[p, 1], uvw[p, 2], uvw[p, 3] - if !all(isapprox.(J[p], Jans_solid_cyl(r, theta, z), rtol=1e-14)) + if !all(isapprox.(J[p], Jans_solid_cyl(r, theta, z); rtol=1e-14)) J_test_scyl = false end - if !all(isapprox.(g[p], gans_solid_cyl(r, theta, z), rtol=1e-14, atol=1e-14)) + if !all(isapprox.(g[p], gans_solid_cyl(r, theta, z); rtol=1e-14, atol=1e-14)) g_test_scyl = false end - if !all(isapprox.(sqrt_g[p], sqrtgans_solid_cyl(r, theta, z), rtol=1e-14)) + if !all(isapprox.(sqrt_g[p], sqrtgans_solid_cyl(r, theta, z); rtol=1e-14)) sqrt_test_scyl = false end - if !all( - isapprox.(inv_g[p], ginvans_solid_cyl(r, theta, z), rtol=1e-14, atol=1e-14) - ) + if !all(isapprox.(inv_g[p], ginvans_solid_cyl(r, theta, z); rtol=1e-14, atol=1e-14)) inv_g_test_scyl = false end - if !all( - isapprox.(Hs[p][1], Hans_solid_cyl(r, theta, z)[1], rtol=1e-14, atol=1e-14) - ) + if !all(isapprox.(Hs[p][1], Hans_solid_cyl(r, theta, z)[1]; rtol=1e-14, atol=1e-14)) H1_test_scyl = false end - if !all( - isapprox.(Hs[p][2], Hans_solid_cyl(r, theta, z)[2], rtol=1e-14, atol=1e-14) - ) + if !all(isapprox.(Hs[p][2], Hans_solid_cyl(r, theta, z)[2]; rtol=1e-14, atol=1e-14)) H2_test_scyl = false end - if !all( - isapprox.(Hs[p][3], Hans_solid_cyl(r, theta, z)[3], rtol=1e-14, atol=1e-14) - ) + if !all(isapprox.(Hs[p][3], Hans_solid_cyl(r, theta, z)[3]; rtol=1e-14, atol=1e-14)) H3_test_scyl = false end if !all( - isapprox.(dgdxs[1][p], dgduans_solid_cyl(r, theta, z), rtol=1e-14, atol=1e-14) + isapprox.(dgdxs[1][p], dgduans_solid_cyl(r, theta, z); rtol=1e-14, atol=1e-14) ) dgdx1_test_scyl = false end if !all( - isapprox.(dgdxs[2][p], dgdvans_solid_cyl(r, theta, z), rtol=1e-14, atol=1e-14) + isapprox.(dgdxs[2][p], dgdvans_solid_cyl(r, theta, z); rtol=1e-14, atol=1e-14) ) dgdx2_test_scyl = false end if !all( - isapprox.(dgdxs[3][p], dgdwans_solid_cyl(r, theta, z), rtol=1e-14, atol=1e-14) + isapprox.(dgdxs[3][p], dgdwans_solid_cyl(r, theta, z); rtol=1e-14, atol=1e-14) ) dgdx3_test_scyl = false end if !all( isapprox.( - dinv_g_dxs[1][p], dginvgduans_solid_cyl(r, theta, z), rtol=1e-12, atol=1e-12 - ) + dinv_g_dxs[1][p], dginvgduans_solid_cyl(r, theta, z); rtol=1e-12, atol=1e-12 + ), ) dinvg1_test_scyl = false end - if !all( - isapprox.(dinv_g_dxs[2][p], dginvgdvans_solid_cyl(r, theta, z), atol=1e-12) - ) + if !all(isapprox.(dinv_g_dxs[2][p], dginvgdvans_solid_cyl(r, theta, z); atol=1e-12)) dinvg2_test_scyl = false end - if !all( - isapprox.(dinv_g_dxs[3][p], dginvgdwans_solid_cyl(r, theta, z), atol=1e-12) - ) + if !all(isapprox.(dinv_g_dxs[3][p], dginvgdwans_solid_cyl(r, theta, z); atol=1e-12)) dinvg3_test_scyl = false end end diff --git a/test/Geometry/UnstructuredGeometryTests.jl b/test/Geometry/UnstructuredGeometryTests.jl index b1c250b43..d8c7c52a7 100644 --- a/test/Geometry/UnstructuredGeometryTests.jl +++ b/test/Geometry/UnstructuredGeometryTests.jl @@ -29,15 +29,7 @@ basic_tests(geometry1, answers_1) cg1 = Geometry.CartesianGeometry((LinRange(0.5, 2.5, 5), LinRange(-0.75, 0.75, 3))) cg2 = Geometry.CartesianGeometry((LinRange(2.5, 5.0, 6), LinRange(-0.75, 0.75, 5))) geometry_2 = Geometry.UnstructuredGeometry((cg1, cg2)) -answers_2 = ( - 2, - 28, - 2, - 2, - (8, 20), - 8, - (0.5, 0.75), -) +answers_2 = (2, 28, 2, 2, (8, 20), 8, (0.5, 0.75)) basic_tests(geometry_2, answers_2) xi = Points.CartesianPoints(([0.0, 1.0], [0.0, 1.0])) @@ -48,7 +40,7 @@ for i in 1:Geometry.get_num_elements(geometry_2) evals_ans = Geometry.evaluate(cg1, i, xi) jac_ans = Geometry.jacobian(cg1, i, xi) for p in axes(evals, 1) - @test all(isapprox.(evals[p,:,:], evals_ans[p,:,:], rtol=1e-14)) + @test all(isapprox.(evals[p, :, :], evals_ans[p, :, :], rtol=1e-14)) end for p in axes(jac, 1) @test all(isapprox.(jac[p], jac_ans[p], rtol=1e-14)) @@ -57,7 +49,7 @@ for i in 1:Geometry.get_num_elements(geometry_2) evals_ans = Geometry.evaluate(cg2, i-8, xi) jac_ans = Geometry.jacobian(cg2, i-8, xi) for p in axes(evals, 1) - @test all(isapprox.(evals[p,:,:], evals_ans[p,:,:], rtol=1e-14)) + @test all(isapprox.(evals[p, :, :], evals_ans[p, :, :], rtol=1e-14)) end for p in axes(jac, 1) @test all(isapprox.(jac[p], jac_ans[p], rtol=1e-14)) diff --git a/test/Geometry/runtests.jl b/test/Geometry/runtests.jl index 87f1cbff1..83c151b10 100644 --- a/test/Geometry/runtests.jl +++ b/test/Geometry/runtests.jl @@ -19,7 +19,7 @@ end end @testset "HierarchicalGeometry" verbose = true begin - include("HierarchicalGeometryTests.jl") + include("HierarchicalGeometryTests.jl") end @testset "Metric" begin diff --git a/test/Hierarchy/runtests.jl b/test/Hierarchy/runtests.jl index eb73e8712..3e7714a37 100644 --- a/test/Hierarchy/runtests.jl +++ b/test/Hierarchy/runtests.jl @@ -3,7 +3,7 @@ module HierarchyTests using Test @testset "ActiveInfo" verbose = true begin - include("ActiveInfoTests.jl") + include("ActiveInfoTests.jl") end end diff --git a/test/Inference/FESpacesInferenceTests.jl b/test/Inference/FESpacesInferenceTests.jl index c88c260c2..31063f3f1 100644 --- a/test/Inference/FESpacesInferenceTests.jl +++ b/test/Inference/FESpacesInferenceTests.jl @@ -31,9 +31,7 @@ B1ELL = FunctionSpaces.BSplineSpace(geometry1, geometry1multi, el_poly, fill(-1, # Rational R1 = FunctionSpaces.RationalFESpace(B1, [0.2, 0.8]) -R1m = FunctionSpaces.RationalFESpace( - B1multi, rand(FunctionSpaces.get_num_basis(B1multi)) -) +R1m = FunctionSpaces.RationalFESpace(B1multi, rand(FunctionSpaces.get_num_basis(B1multi))) # Multi-variate and multi-component --- # TensorProduct @@ -62,9 +60,7 @@ DS_TP1mTP1m = FunctionSpaces.DirectSumSpace((TP_B1mB1m, TP_B1mB1m)) # 3-component, two distinct spaces, 2D, multi-element, single-patch DS_TP1mTPR1mTP1m = FunctionSpaces.DirectSumSpace((TP_B1mB1m, TP_R1mR1m, TP_B1mB1m)) # 3-component, three distinct spaces, 2D, multi-element, single-patch -DS_TPB1mTPR1mTPBR1m = FunctionSpaces.DirectSumSpace(( - TP_B1mB1m, TP_R1mR1m, TP_B1mR1m -)) +DS_TPB1mTPR1mTPBR1m = FunctionSpaces.DirectSumSpace((TP_B1mB1m, TP_R1mR1m, TP_B1mR1m)) const spaces = ( B1, @@ -109,9 +105,7 @@ foreach(spaces) do space @test_opt FunctionSpaces.get_component_spaces(space) @test_opt FunctionSpaces.get_extraction_operator(space) @test_opt FunctionSpaces.get_extraction(space, element_id, component_id) - @test_opt FunctionSpaces.get_extraction_coefficients( - space, element_id, component_id - ) + @test_opt FunctionSpaces.get_extraction_coefficients(space, element_id, component_id) @test_opt FunctionSpaces.get_basis_indices(space, element_id) @test_opt FunctionSpaces.get_basis_permutation(space, element_id, component_id) @test_opt FunctionSpaces.get_num_basis(space) diff --git a/test/Inference/GeometryInferenceTests.jl b/test/Inference/GeometryInferenceTests.jl index 204841839..169fdc0fa 100644 --- a/test/Inference/GeometryInferenceTests.jl +++ b/test/Inference/GeometryInferenceTests.jl @@ -37,9 +37,7 @@ cg2d = Geometry.CartesianGeometry(( )) tpgeo = Geometry.TensorProductGeometry((cg2d, cg1d)) # 11D, TensorProduct, mixed -tpgeo11D = Geometry.TensorProductGeometry(( - tpgeo, geometry1p4D, cg2d, cg1d, geometry1 -)) +tpgeo11D = Geometry.TensorProductGeometry((tpgeo, geometry1p4D, cg2d, cg1d, geometry1)) # 2D, Mapped # Mappings to create the deformed geometries. The mappings are defined with # reference to the unit square [0,1]x[0,1] as parametric domain. @@ -119,19 +117,13 @@ geom_slanted_2patch_oneref = Geometry.MappedGeometry( # Mapped, multiple patches with one map. geom_slanted_2patch_onemap = Geometry.MappedGeometry( ( - Geometry.CartesianGeometry((( - LinRange(0.0, 0.5, 5), LinRange(0.0, 1.0, 7) - ),)), - Geometry.CartesianGeometry((( - LinRange(0.5, 1.0, 4), LinRange(0.0, 1.0, 7) - ),)), + Geometry.CartesianGeometry(((LinRange(0.0, 0.5, 5), LinRange(0.0, 1.0, 7)),)), + Geometry.CartesianGeometry(((LinRange(0.5, 1.0, 4), LinRange(0.0, 1.0, 7)),)), ), mapping_patch_1_slanted, ) # Mapped, single mapping, single patch. -geom_slanted_2patch_11 = Geometry.MappedGeometry( - geom_cart_patch_1, mapping_patch_1_slanted -) +geom_slanted_2patch_11 = Geometry.MappedGeometry(geom_cart_patch_1, mapping_patch_1_slanted) # Unstructured geom_unstr = Geometry.UnstructuredGeometry(( @@ -187,9 +179,7 @@ const xi_1D = Points.CartesianPoints(([0.0, 1.0],)) const xi_2D = Points.CartesianPoints(([0.0, 1.0], [0.0, 1.0])) const xi_3D = Points.CartesianPoints(([0.0, 1.0], [0.0, 1.0], [0.0, 1.0])) const xi_3D_set = Points.PointSet(([0.0, 1.0], [0.0, 1.0], [0.0, 1.0])) -const xi_4D = Points.CartesianPoints(( - [0.0, 1.0], [0.0, 1.0], [0.0, 1.0], [0.0, 1.0] -)) +const xi_4D = Points.CartesianPoints(([0.0, 1.0], [0.0, 1.0], [0.0, 1.0], [0.0, 1.0])) const xi_11D = Points.CartesianPoints(( [0.0, 1.0], [0.0, 1.0], diff --git a/test/Inference/runtests.jl b/test/Inference/runtests.jl index c1e0620f7..721364db1 100644 --- a/test/Inference/runtests.jl +++ b/test/Inference/runtests.jl @@ -2,8 +2,14 @@ module JETTests using Test -@testset "Geometry" begin include("GeometryInferenceTests.jl") end -@testset "FESpaces" begin include("FESpacesInferenceTests.jl") end -@testset "TimeIntegrators" begin include("TimeIntegratorsInferenceTests.jl") end +@testset "Geometry" begin + include("GeometryInferenceTests.jl") +end +@testset "FESpaces" begin + include("FESpacesInferenceTests.jl") +end +@testset "TimeIntegrators" begin + include("TimeIntegratorsInferenceTests.jl") +end end diff --git a/test/Mesh/MeshTopologyTests.jl b/test/Mesh/MeshTopologyTests.jl index bba9a7f45..26815cc9b 100644 --- a/test/Mesh/MeshTopologyTests.jl +++ b/test/Mesh/MeshTopologyTests.jl @@ -10,12 +10,12 @@ using Test # Test 1D # ----------------------------------------------------------------------------- # Test vertices -for vertex_id = 1:2 +for vertex_id in 1:2 @test vertex_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(1, 0, vertex_id)) end # Test edges -for edge_id = 1:1 +for edge_id in 1:1 @test edge_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(1, 1, edge_id)) end @@ -23,17 +23,17 @@ end # Test 2D # ----------------------------------------------------------------------------- # Test vertices -for vertex_id = 1:4 +for vertex_id in 1:4 @test vertex_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(2, 0, vertex_id)) end # Test edges -for edge_id = 1:4 +for edge_id in 1:4 @test edge_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(2, 1, edge_id)) end # Test surfaces -for surface_id = 1:1 +for surface_id in 1:1 @test surface_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(2, 2, surface_id)) end @@ -41,22 +41,22 @@ end # Test 3D # ----------------------------------------------------------------------------- # Test vertices -for vertex_id = 1:8 +for vertex_id in 1:8 @test vertex_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(3, 0, vertex_id)) end # Test edges -for edge_id = 1:12 +for edge_id in 1:12 @test edge_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(3, 1, edge_id)) end # Test surfaces -for surface_id = 1:6 +for surface_id in 1:6 @test surface_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(3, 2, surface_id)) end # Test volumes -for volume_id = 1:1 +for volume_id in 1:1 @test volume_id == Mantis.Mesh.position2id(Mantis.Mesh.id2position(3, 3, volume_id)) end # ----------------------------------------------------------------------------- @@ -72,28 +72,145 @@ end # 2nd patch: 7, 6, 2, 3, 12, 11, 9, 10 (vertex indices) # Define the mesh -mesh_connectivity_3d_ex_1 = [ - [1, 2, 3, 4, 5, 6, 7, 8], - [7, 6, 2, 3, 12, 11, 9, 10] - ] +mesh_connectivity_3d_ex_1 = [[1, 2, 3, 4, 5, 6, 7, 8], [7, 6, 2, 3, 12, 11, 9, 10]] # Define reference results # Incidence relations incidence_relation_ex_1_ref = Matrix{Vector{Vector{Int}}}(undef, 4, 4) incidence_relation_ex_1_ref[1, 1] = Vector{Int64}[] -incidence_relation_ex_1_ref[1, 2] = [[1, 2, 3], [1, 4, 5, 6], [4, 7, 8, 9], [2, 7, 10], [3, 11, 12], [5, 11, 13, 14], [8, 13, 15, 16], [10, 12, 15], [6, 17, 18], [9, 17, 19], [14, 18, 20], [16, 19, 20]] -incidence_relation_ex_1_ref[1, 3] = [[1, 2, 3], [1, 2, 4, 5, 6], [1, 4, 5, 7, 8], [1, 3, 7], [2, 3, 9], [2, 4, 6, 9, 10], [4, 7, 8, 9, 10], [3, 7, 9], [5, 6, 11], [5, 8, 11], [6, 10, 11], [8, 10, 11]] -incidence_relation_ex_1_ref[1, 4] = [[1], [1, 2], [1, 2], [1], [1], [1, 2], [1, 2], [1], [2], [2], [2], [2]] -incidence_relation_ex_1_ref[2, 1] = [[2, 1], [4, 1], [5, 1], [2, 3], [6, 2], [9, 2], [3, 4], [7, 3], [10, 3], [8, 4], [6, 5], [8, 5], [7, 6], [11, 6], [7, 8], [12, 7], [9, 10], [9, 11], [10, 12], [11, 12]] +incidence_relation_ex_1_ref[1, 2] = [ + [1, 2, 3], + [1, 4, 5, 6], + [4, 7, 8, 9], + [2, 7, 10], + [3, 11, 12], + [5, 11, 13, 14], + [8, 13, 15, 16], + [10, 12, 15], + [6, 17, 18], + [9, 17, 19], + [14, 18, 20], + [16, 19, 20], +] +incidence_relation_ex_1_ref[1, 3] = [ + [1, 2, 3], + [1, 2, 4, 5, 6], + [1, 4, 5, 7, 8], + [1, 3, 7], + [2, 3, 9], + [2, 4, 6, 9, 10], + [4, 7, 8, 9, 10], + [3, 7, 9], + [5, 6, 11], + [5, 8, 11], + [6, 10, 11], + [8, 10, 11], +] +incidence_relation_ex_1_ref[1, 4] = [ + [1], [1, 2], [1, 2], [1], [1], [1, 2], [1, 2], [1], [2], [2], [2], [2] +] +incidence_relation_ex_1_ref[2, 1] = [ + [2, 1], + [4, 1], + [5, 1], + [2, 3], + [6, 2], + [9, 2], + [3, 4], + [7, 3], + [10, 3], + [8, 4], + [6, 5], + [8, 5], + [7, 6], + [11, 6], + [7, 8], + [12, 7], + [9, 10], + [9, 11], + [10, 12], + [11, 12], +] incidence_relation_ex_1_ref[2, 2] = Vector{Int64}[] -incidence_relation_ex_1_ref[2, 3] = [[1, 2], [1, 3], [2, 3], [1, 4, 5], [2, 4, 6], [5, 6], [1, 7], [4, 7, 8], [5, 8], [3, 7], [2, 9], [3, 9], [4, 9, 10], [6, 10], [7, 9], [8, 10], [5, 11], [6, 11], [8, 11], [10, 11]] -incidence_relation_ex_1_ref[2, 4] = [[1], [1], [1], [1, 2], [1, 2], [2], [1], [1, 2], [2], [1], [1], [1], [1, 2], [2], [1], [2], [2], [2], [2], [2]] -incidence_relation_ex_1_ref[3, 1] = [[3, 2, 1, 4], [6, 5, 1, 2], [8, 4, 1, 5], [7, 6, 2, 3], [9, 2, 3, 10], [9, 11, 6, 2], [7, 3, 4, 8], [10, 3, 7, 12], [7, 8, 5, 6], [11, 12, 7, 6], [9, 10, 12, 11]] -incidence_relation_ex_1_ref[3, 2] = [[7, 1, -4, 2], [5, 3, 11, 1], [12, 2, 10, 3], [8, 5, 13, -4], [17, 4, 6, 9], [6, 14, 18, -5], [15, 7, 8, 10], [19, -8, 9, 16], [13, 12, 15, 11], [14, 16, 20, -13], [18, 19, 17, 20]] +incidence_relation_ex_1_ref[2, 3] = [ + [1, 2], + [1, 3], + [2, 3], + [1, 4, 5], + [2, 4, 6], + [5, 6], + [1, 7], + [4, 7, 8], + [5, 8], + [3, 7], + [2, 9], + [3, 9], + [4, 9, 10], + [6, 10], + [7, 9], + [8, 10], + [5, 11], + [6, 11], + [8, 11], + [10, 11], +] +incidence_relation_ex_1_ref[2, 4] = [ + [1], + [1], + [1], + [1, 2], + [1, 2], + [2], + [1], + [1, 2], + [2], + [1], + [1], + [1], + [1, 2], + [2], + [1], + [2], + [2], + [2], + [2], + [2], +] +incidence_relation_ex_1_ref[3, 1] = [ + [3, 2, 1, 4], + [6, 5, 1, 2], + [8, 4, 1, 5], + [7, 6, 2, 3], + [9, 2, 3, 10], + [9, 11, 6, 2], + [7, 3, 4, 8], + [10, 3, 7, 12], + [7, 8, 5, 6], + [11, 12, 7, 6], + [9, 10, 12, 11], +] +incidence_relation_ex_1_ref[3, 2] = [ + [7, 1, -4, 2], + [5, 3, 11, 1], + [12, 2, 10, 3], + [8, 5, 13, -4], + [17, 4, 6, 9], + [6, 14, 18, -5], + [15, 7, 8, 10], + [19, -8, 9, 16], + [13, 12, 15, 11], + [14, 16, 20, -13], + [18, 19, 17, 20], +] incidence_relation_ex_1_ref[3, 3] = Vector{Int64}[] -incidence_relation_ex_1_ref[3, 4] = [[1], [1], [1], [1, 2], [2], [2], [1], [2], [1], [2], [2]] +incidence_relation_ex_1_ref[3, 4] = [ + [1], [1], [1], [1, 2], [2], [2], [1], [2], [1], [2], [2] +] incidence_relation_ex_1_ref[4, 1] = [[1, 2, 3, 4, 5, 6, 7, 8], [7, 6, 2, 3, 12, 11, 9, 10]] -incidence_relation_ex_1_ref[4, 2] = [[-15, -11, -1, -7, -13, -12, -2, 4, -8, -10, -3, -5], [-17, -20, 13, -4, -18, -19, 8, 5, -6, -9, -16, -14]] +incidence_relation_ex_1_ref[4, 2] = [ + [-15, -11, -1, -7, -13, -12, -2, 4, -8, -10, -3, -5], + [-17, -20, 13, -4, -18, -19, 8, 5, -6, -9, -16, -14], +] incidence_relation_ex_1_ref[4, 3] = [[3, 4, 2, 7, 1, 9], [8, 6, 10, 5, -4, 11]] incidence_relation_ex_1_ref[4, 4] = Vector{Int64}[] @@ -106,8 +223,8 @@ face_neighbours_3d_ex_1_ref[1, 1] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_1_ref[1, 2] = [2; 5; 2; 1;;] face_neighbours_3d_ex_1_ref[1, 3] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_1_ref[1, 4] = Matrix{Int64}(undef, 4, 0) -face_neighbours_3d_ex_1_ref[1, 5] = Matrix{Int64}(undef, 4, 0) -face_neighbours_3d_ex_1_ref[1, 6] = Matrix{Int64}(undef, 4, 0) +face_neighbours_3d_ex_1_ref[1, 5] = Matrix{Int64}(undef, 4, 0) +face_neighbours_3d_ex_1_ref[1, 6] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_1_ref[2, 1] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_1_ref[2, 2] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_1_ref[2, 3] = Matrix{Int64}(undef, 4, 0) @@ -131,7 +248,7 @@ edge_neighbours_3d_ex_1_ref[1, 8] = [2; 4; 0; -1;;] edge_neighbours_3d_ex_1_ref[1, 9] = [2; 7; 0; -1;;] edge_neighbours_3d_ex_1_ref[1, 10] = Matrix{Int64}(undef, 4, 0) edge_neighbours_3d_ex_1_ref[1, 11] = Matrix{Int64}(undef, 4, 0) -edge_neighbours_3d_ex_1_ref[1, 12] =[2; 8; 0; -1;;] +edge_neighbours_3d_ex_1_ref[1, 12] = [2; 8; 0; -1;;] edge_neighbours_3d_ex_1_ref[2, 1] = Matrix{Int64}(undef, 4, 0) edge_neighbours_3d_ex_1_ref[2, 2] = Matrix{Int64}(undef, 4, 0) edge_neighbours_3d_ex_1_ref[2, 3] = [1; 5; 0; -1;;] @@ -207,8 +324,6 @@ for i in 1:n_total_patches end end - - # ----------------------------------------------------------------------------- # Test mesh 2: 2 patches, configuration 2 # ----------------------------------------------------------------------------- @@ -216,28 +331,145 @@ end # 2nd patch: 10, 3, 2, 9, 12, 7, 6, 11 (vertex indices) # Define the mesh -mesh_connectivity_3d_ex_2 = [ - [1, 2, 3, 4, 5, 6, 7, 8], - [10, 3, 2, 9, 12, 7, 6, 11] - ] +mesh_connectivity_3d_ex_2 = [[1, 2, 3, 4, 5, 6, 7, 8], [10, 3, 2, 9, 12, 7, 6, 11]] # Define reference results # Incidence relations incidence_relation_ex_2_ref = Matrix{Vector{Vector{Int}}}(undef, 4, 4) incidence_relation_ex_2_ref[1, 1] = Vector{Int64}[] -incidence_relation_ex_2_ref[1, 2] = [[1, 2, 3], [1, 4, 5, 6], [4, 7, 8, 9], [2, 7, 10], [3, 11, 12], [5, 11, 13, 14], [8, 13, 15, 16], [10, 12, 15], [6, 17, 18], [9, 17, 19], [14, 18, 20], [16, 19, 20]] -incidence_relation_ex_2_ref[1, 3] = [[1, 2, 3], [1, 2, 4, 5, 6], [1, 4, 5, 7, 8], [1, 3, 7], [2, 3, 9], [2, 4, 6, 9, 10], [4, 7, 8, 9, 10], [3, 7, 9], [5, 6, 11], [5, 8, 11], [6, 10, 11], [8, 10, 11]] -incidence_relation_ex_2_ref[1, 4] = [[1], [1, 2], [1, 2], [1], [1], [1, 2], [1, 2], [1], [2], [2], [2], [2]] -incidence_relation_ex_2_ref[2, 1] = [[2, 1], [4, 1], [5, 1], [3, 2], [6, 2], [2, 9], [3, 4], [7, 3], [3, 10], [8, 4], [6, 5], [8, 5], [7, 6], [6, 11], [7, 8], [7, 12], [9, 10], [11, 9], [12, 10], [11, 12]] +incidence_relation_ex_2_ref[1, 2] = [ + [1, 2, 3], + [1, 4, 5, 6], + [4, 7, 8, 9], + [2, 7, 10], + [3, 11, 12], + [5, 11, 13, 14], + [8, 13, 15, 16], + [10, 12, 15], + [6, 17, 18], + [9, 17, 19], + [14, 18, 20], + [16, 19, 20], +] +incidence_relation_ex_2_ref[1, 3] = [ + [1, 2, 3], + [1, 2, 4, 5, 6], + [1, 4, 5, 7, 8], + [1, 3, 7], + [2, 3, 9], + [2, 4, 6, 9, 10], + [4, 7, 8, 9, 10], + [3, 7, 9], + [5, 6, 11], + [5, 8, 11], + [6, 10, 11], + [8, 10, 11], +] +incidence_relation_ex_2_ref[1, 4] = [ + [1], [1, 2], [1, 2], [1], [1], [1, 2], [1, 2], [1], [2], [2], [2], [2] +] +incidence_relation_ex_2_ref[2, 1] = [ + [2, 1], + [4, 1], + [5, 1], + [3, 2], + [6, 2], + [2, 9], + [3, 4], + [7, 3], + [3, 10], + [8, 4], + [6, 5], + [8, 5], + [7, 6], + [6, 11], + [7, 8], + [7, 12], + [9, 10], + [11, 9], + [12, 10], + [11, 12], +] incidence_relation_ex_2_ref[2, 2] = Vector{Int64}[] -incidence_relation_ex_2_ref[2, 3] = [[1, 2], [1, 3], [2, 3], [1, 4, 5], [2, 4, 6], [5, 6], [1, 7], [4, 7, 8], [5, 8], [3, 7], [2, 9], [3, 9], [4, 9, 10], [6, 10], [7, 9], [8, 10], [5, 11], [6, 11], [8, 11], [10, 11]] -incidence_relation_ex_2_ref[2, 4] = [[1], [1], [1], [1, 2], [1, 2], [2], [1], [1, 2], [2], [1], [1], [1], [1, 2], [2], [1], [2], [2], [2], [2], [2]] -incidence_relation_ex_2_ref[3, 1] = [[3, 2, 1, 4], [6, 5, 1, 2], [8, 4, 1, 5], [7, 6, 2, 3], [2, 3, 10, 9], [6, 2, 9, 11], [7, 3, 4, 8], [7, 12, 10, 3], [7, 8, 5, 6], [6, 11, 12, 7], [11, 9, 10, 12]] -incidence_relation_ex_2_ref[3, 2] = [[7, 1, 4, 2], [5, 3, 11, 1], [12, 2, 10, 3], [8, 5, 13, 4], [6, 9, -4, 17], [14, 6, 5, 18], [15, 7, 8, 10], [8, 19, 16, 9], [13, 12, 15, 11], [-13, 20, 14, 16], [20, 17, 18, 19]] +incidence_relation_ex_2_ref[2, 3] = [ + [1, 2], + [1, 3], + [2, 3], + [1, 4, 5], + [2, 4, 6], + [5, 6], + [1, 7], + [4, 7, 8], + [5, 8], + [3, 7], + [2, 9], + [3, 9], + [4, 9, 10], + [6, 10], + [7, 9], + [8, 10], + [5, 11], + [6, 11], + [8, 11], + [10, 11], +] +incidence_relation_ex_2_ref[2, 4] = [ + [1], + [1], + [1], + [1, 2], + [1, 2], + [2], + [1], + [1, 2], + [2], + [1], + [1], + [1], + [1, 2], + [2], + [1], + [2], + [2], + [2], + [2], + [2], +] +incidence_relation_ex_2_ref[3, 1] = [ + [3, 2, 1, 4], + [6, 5, 1, 2], + [8, 4, 1, 5], + [7, 6, 2, 3], + [2, 3, 10, 9], + [6, 2, 9, 11], + [7, 3, 4, 8], + [7, 12, 10, 3], + [7, 8, 5, 6], + [6, 11, 12, 7], + [11, 9, 10, 12], +] +incidence_relation_ex_2_ref[3, 2] = [ + [7, 1, 4, 2], + [5, 3, 11, 1], + [12, 2, 10, 3], + [8, 5, 13, 4], + [6, 9, -4, 17], + [14, 6, 5, 18], + [15, 7, 8, 10], + [8, 19, 16, 9], + [13, 12, 15, 11], + [-13, 20, 14, 16], + [20, 17, 18, 19], +] incidence_relation_ex_2_ref[3, 3] = Vector{Int64}[] -incidence_relation_ex_2_ref[3, 4] = [[1], [1], [1], [1, 2], [2], [2], [1], [2], [1], [2], [2]] +incidence_relation_ex_2_ref[3, 4] = [ + [1], [1], [1], [1, 2], [2], [2], [1], [2], [1], [2], [2] +] incidence_relation_ex_2_ref[4, 1] = [[1, 2, 3, 4, 5, 6, 7, 8], [10, 3, 2, 9, 12, 7, 6, 11]] -incidence_relation_ex_2_ref[4, 2] = [[-15, -11, -1, -7, -13, -12, -2, -4, -8, -10, -3, -5], [-14, -16, -9, -6, 13, -20, -17, 4, -5, -18, -19, -8]] +incidence_relation_ex_2_ref[4, 2] = [ + [-15, -11, -1, -7, -13, -12, -2, -4, -8, -10, -3, -5], + [-14, -16, -9, -6, 13, -20, -17, 4, -5, -18, -19, -8], +] incidence_relation_ex_2_ref[4, 3] = [[3, 4, 2, 7, 1, 9], [11, -4, 8, 6, 5, 10]] incidence_relation_ex_2_ref[4, 4] = Vector{Int64}[] @@ -249,8 +481,8 @@ face_neighbours_3d_ex_2_ref[1, 1] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_2_ref[1, 2] = [2; 2; 1; -1;;] face_neighbours_3d_ex_2_ref[1, 3] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_2_ref[1, 4] = Matrix{Int64}(undef, 4, 0) -face_neighbours_3d_ex_2_ref[1, 5] = Matrix{Int64}(undef, 4, 0) -face_neighbours_3d_ex_2_ref[1, 6] = Matrix{Int64}(undef, 4, 0) +face_neighbours_3d_ex_2_ref[1, 5] = Matrix{Int64}(undef, 4, 0) +face_neighbours_3d_ex_2_ref[1, 6] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_2_ref[2, 1] = Matrix{Int64}(undef, 4, 0) face_neighbours_3d_ex_2_ref[2, 2] = [1; 2; 1; -1;;] face_neighbours_3d_ex_2_ref[2, 3] = Matrix{Int64}(undef, 4, 0) @@ -348,8 +580,6 @@ for i in 1:n_total_patches end end - - # ----------------------------------------------------------------------------- # Test 2D mesh topology # ----------------------------------------------------------------------------- @@ -359,18 +589,19 @@ end # ----------------------------------------------------------------------------- # 1st patch: 1, 2, 3, 4 (vertex indices) # 2nd patch: 3, 2, 5, 6 (vertex indices) -mesh_connectivity_2d_ex_1 = [ - [1, 2, 3, 4], - [3, 2, 5, 6] -] +mesh_connectivity_2d_ex_1 = [[1, 2, 3, 4], [3, 2, 5, 6]] # Define reference results # Incidence relations incidence_relation_2d_ex_1_ref = Matrix{Vector{Vector{Int}}}(undef, 3, 3) incidence_relation_2d_ex_1_ref[1, 1] = Vector{Int64}[] -incidence_relation_2d_ex_1_ref[1, 2] = [[1, 2], [1, 3, 4], [3, 5, 6], [2, 5], [4, 7], [6, 7]] +incidence_relation_2d_ex_1_ref[1, 2] = [ + [1, 2], [1, 3, 4], [3, 5, 6], [2, 5], [4, 7], [6, 7] +] incidence_relation_2d_ex_1_ref[1, 3] = [[1], [1, 2], [1, 2], [1], [2], [2]] -incidence_relation_2d_ex_1_ref[2, 1] = [[1, 2], [1, 4], [2, 3], [2, 5], [4, 3], [3, 6], [6, 5]] +incidence_relation_2d_ex_1_ref[2, 1] = [ + [1, 2], [1, 4], [2, 3], [2, 5], [4, 3], [3, 6], [6, 5] +] incidence_relation_2d_ex_1_ref[2, 2] = Vector{Int64}[] incidence_relation_2d_ex_1_ref[2, 3] = [[1], [1], [1, 2], [2], [1], [2], [2]] incidence_relation_2d_ex_1_ref[3, 1] = [[1, 2, 3, 4], [3, 2, 5, 6]] @@ -403,7 +634,6 @@ vertex_neighbours_2d_ex_1_ref[2, 2] = [1; 2; 0; 0;;] vertex_neighbours_2d_ex_1_ref[2, 3] = Matrix{Int64}(undef, 4, 0) vertex_neighbours_2d_ex_1_ref[2, 4] = Matrix{Int64}(undef, 4, 0) - # Test MeshTopology # Compute the mesh topology mesh_topology_2d_ex_1 = Mantis.Mesh.MeshTopology(mesh_connectivity_2d_ex_1) @@ -436,7 +666,6 @@ for i in 1:n_total_patches end end - # ----------------------------------------------------------------------------- # Test 1D mesh topology # ----------------------------------------------------------------------------- @@ -447,11 +676,7 @@ end # 1st patch: 1, 2 (vertex indices) # 2nd patch: 2, 3 (vertex indices) # 3rd patch: 3, 4 (vertex indices) -mesh_connectivity_1d_ex_1 = [ - [1, 2], - [2, 3], - [3, 4] -] +mesh_connectivity_1d_ex_1 = [[1, 2], [2, 3], [3, 4]] # Define reference results # Incidence relations @@ -472,7 +697,6 @@ vertex_neighbours_1d_ex_1_ref[2, 2] = [3; 1; 0; 0;;] vertex_neighbours_1d_ex_1_ref[3, 1] = [2; 2; 0; 0;;] vertex_neighbours_1d_ex_1_ref[3, 2] = Matrix{Int64}(undef, 4, 0) - # Test MeshTopology # Compute the mesh topology mesh_topology_1d_ex_1 = Mantis.Mesh.MeshTopology(mesh_connectivity_1d_ex_1) diff --git a/test/Mesh/PatchIntervalTests.jl b/test/Mesh/PatchIntervalTests.jl index 99252ae41..0eb755101 100644 --- a/test/Mesh/PatchIntervalTests.jl +++ b/test/Mesh/PatchIntervalTests.jl @@ -54,10 +54,10 @@ test_patch = Patch((test_brk, test_brk2, test_brk3, test_brk4)) # Test if the element ids are correctly created. check_ids = NTuple{4, Int}[] -for i in 1:1:n4-1 - for j in 1:1:n3-1 - for k in 1:1:n2-1 - for l in 1:1:n1-1 +for i in 1:1:(n4 - 1) + for j in 1:1:(n3 - 1) + for k in 1:1:(n2 - 1) + for l in 1:1:(n1 - 1) push!(check_ids, (l, k, j, i)) end end diff --git a/test/Mesh/runtests.jl b/test/Mesh/runtests.jl index 690af06c0..ac480d913 100644 --- a/test/Mesh/runtests.jl +++ b/test/Mesh/runtests.jl @@ -2,7 +2,11 @@ module MeshTests using Test -@testset "PatchInterval" begin include("PatchIntervalTests.jl") end -@testset "MeshTopology" begin include("MeshTopologyTests.jl") end +@testset "PatchInterval" begin + include("PatchIntervalTests.jl") +end +@testset "MeshTopology" begin + include("MeshTopologyTests.jl") +end -end \ No newline at end of file +end diff --git a/test/Plot/FormPlotTests.jl b/test/Plot/FormPlotTests.jl index 7cdefce84..edd05bfd2 100644 --- a/test/Plot/FormPlotTests.jl +++ b/test/Plot/FormPlotTests.jl @@ -6,13 +6,14 @@ using Test # Setup the form spaces # Space information -starting_points = (0.0, 0.0); box_sizes = (1.0, 1.0) +starting_points = (0.0, 0.0); +box_sizes = (1.0, 1.0) num_elements = (10, 10) -degrees = (2, 2); regularities = (1, 1) +degrees = (2, 2); +regularities = (1, 1) # Then the form space zero_form_space, one_form_space, top_form_space = Mantis.Forms.create_tensor_product_bspline_de_rham_complex( - starting_points, box_sizes, num_elements, - degrees, regularities + starting_points, box_sizes, num_elements, degrees, regularities ) # Generate the form expressions @@ -23,12 +24,14 @@ zero_form_space, one_form_space, top_form_space = Mantis.Forms.create_tensor_pro num_basis = Mantis.Forms.get_num_basis(zero_form_space) # Compute base directories for data input and output -output_directory_tree = [dirname(dirname(pathof(Mantis))), "test","data","output","Plot"] +output_directory_tree = [dirname(dirname(pathof(Mantis))), "test", "data", "output", "Plot"] out_deg = maximum([1, maximum(degrees)]) zero_form_filename = "zero-form-field-test.vtu" -zero_form_file = Mantis.GeneralHelpers.export_path(output_directory_tree, zero_form_filename) +zero_form_file = Mantis.GeneralHelpers.export_path( + output_directory_tree, zero_form_filename +) one_form_filename = "one-form-field-test.vtu" one_form_file = Mantis.GeneralHelpers.export_path(output_directory_tree, one_form_filename) @@ -36,8 +39,29 @@ one_form_file = Mantis.GeneralHelpers.export_path(output_directory_tree, one_for two_form_filename = "two-form-field-test.vtu" two_form_file = Mantis.GeneralHelpers.export_path(output_directory_tree, two_form_filename) -@test_nowarn Mantis.Plot.plot(α⁰; vtk_filename = zero_form_file, n_subcells = 1, degree = out_deg, ascii = false, compress = false) -@test_nowarn Mantis.Plot.plot(ξ¹; vtk_filename = one_form_file, n_subcells = 1, degree = out_deg, ascii = false, compress = false) -@test_nowarn Mantis.Plot.plot(β²; vtk_filename = two_form_file, n_subcells = 1, degree = out_deg, ascii = false, compress = false) +@test_nowarn Mantis.Plot.plot( + α⁰; + vtk_filename=zero_form_file, + n_subcells=1, + degree=out_deg, + ascii=false, + compress=false, +) +@test_nowarn Mantis.Plot.plot( + ξ¹; + vtk_filename=one_form_file, + n_subcells=1, + degree=out_deg, + ascii=false, + compress=false, +) +@test_nowarn Mantis.Plot.plot( + β²; + vtk_filename=two_form_file, + n_subcells=1, + degree=out_deg, + ascii=false, + compress=false, +) end diff --git a/test/Plot/GeometryPlotTests.jl b/test/Plot/GeometryPlotTests.jl index b2a9ab114..117745178 100644 --- a/test/Plot/GeometryPlotTests.jl +++ b/test/Plot/GeometryPlotTests.jl @@ -8,8 +8,10 @@ using Printf using Test # Compute directory trees for data input and output -reference_directory_tree = [dirname(dirname(pathof(Mantis))), "test", "data", "reference", "Plot"] -output_directory_tree = [dirname(dirname(pathof(Mantis))), "test","data","output","Plot"] +reference_directory_tree = [ + dirname(dirname(pathof(Mantis))), "test", "data", "reference", "Plot" +] +output_directory_tree = [dirname(dirname(pathof(Mantis))), "test", "data", "output", "Plot"] # Test Plotting of 3D Geometry (torus) ------------------------------------------- deg = 2 @@ -213,8 +215,8 @@ geom_coeffs_tp = [ geom_coeffs_θ .* r1 ] geom_coeffs_θrz = [ - geom_coeffs_tp z0.*ones(8) - geom_coeffs_tp z1.*ones(8) + geom_coeffs_tp z0 .* ones(8) + geom_coeffs_tp z1 .* ones(8) ] geom = FunctionSpaces.DiscreteGeometry(TP_θrz, geom_coeffs_θrz) # Generate the plot diff --git a/test/Plot/runtests.jl b/test/Plot/runtests.jl index e41e5e342..3add066c3 100644 --- a/test/Plot/runtests.jl +++ b/test/Plot/runtests.jl @@ -2,7 +2,11 @@ module PlotTests using Test -@testset "GeometryPlot" begin include("GeometryPlotTests.jl") end -@testset "FormPlot" begin include("FormPlotTests.jl") end +@testset "GeometryPlot" begin + include("GeometryPlotTests.jl") +end +@testset "FormPlot" begin + include("FormPlotTests.jl") +end -end \ No newline at end of file +end diff --git a/test/Points/CartesianPoints.jl b/test/Points/CartesianPoints.jl index 430599d60..1acabe700 100644 --- a/test/Points/CartesianPoints.jl +++ b/test/Points/CartesianPoints.jl @@ -146,9 +146,7 @@ points = Mantis.Points.CartesianPoints(1:2, 1:3, 1:4; iteration_order=(3, 1, 2)) @test typeof(Points.CartesianPoints(LinRange(0, 1, 2), [1, 2]).constituent_points) == Tuple{LinRange{Float64, Int}, Vector{Float64}} @test typeof( - Points.CartesianPoints( - 1:2, [1, 2], LinRange(0, 1, 2), zeros(Float32, 2) - ).constituent_points, + Points.CartesianPoints(1:2, [1, 2], LinRange(0, 1, 2), zeros(Float32, 2)).constituent_points, ) == Tuple{Vector{Float64}, Vector{Float64}, LinRange{Float64, Int}, Vector{Float64}} end diff --git a/test/Quadrature/QuadratureTestsSetup.jl b/test/Quadrature/QuadratureTestsSetup.jl index 62fbb898e..70100f033 100644 --- a/test/Quadrature/QuadratureTestsSetup.jl +++ b/test/Quadrature/QuadratureTestsSetup.jl @@ -3,14 +3,13 @@ # Tolerance for comparing floating point numbers. atol = 1e-12 - # Monomial and integrated monomial functions. function monomial(degree::Int, x::Float64) return x^degree end function monomial(degree::Int, x::Vector{Float64}) - return x.^degree + return x .^ degree end function integrated_monomial(degree::Int, x::Float64) @@ -18,11 +17,9 @@ function integrated_monomial(degree::Int, x::Float64) end function integrated_monomial(degree::Int, x::Vector{Float64}) - return x.^(degree+1)./(degree+1) + return x .^ (degree+1) ./ (degree+1) end - - # Chebyshev and integrated Chebyshev functions. # This is a recursive function. To reduce the number of recursive calls, we have hardcoded a # few Chebyshev polynomials. @@ -54,11 +51,10 @@ function chebyshev(degree::Int, x::Vector{Float64}) elseif degree == 10 return @. 512.0*x^10 - 1280.0*x^8 + 1120.0*x^6 - 400.0*x^4 + 50.0*x^2 - 1.0 else - return 2.0.*x.*chebyshev(degree-1, x) .- chebyshev(degree-2, x) + return 2.0 .* x .* chebyshev(degree-1, x) .- chebyshev(degree-2, x) end end - function integrated_chebyshev(degree::Int, x::Float64) if degree == 0 return x @@ -66,7 +62,7 @@ function integrated_chebyshev(degree::Int, x::Float64) return 0.5 * x^2 else return degree * chebyshev(degree+1, x) / (degree^2 - 1) - - x * chebyshev(degree, x) / (degree - 1) + x * chebyshev(degree, x) / (degree - 1) end end @@ -74,16 +70,15 @@ function integrated_chebyshev(degree::Int, x::Vector{Float64}) if degree == 0 return x elseif degree == 1 - return 0.5 .* x.^2 + return 0.5 .* x .^ 2 else return degree .* chebyshev(degree+1, x) ./ (degree^2-1) .- - x .* chebyshev(degree, x) ./ (degree-1) + x .* chebyshev(degree, x) ./ (degree-1) end end - function chebyshev_nd(degrees::NTuple{N, Int}, x::NTuple{N, Vector{Float64}}) where {N} - cheb_1d = [chebyshev(degrees[i], x[i]) for i = 1:N] + cheb_1d = [chebyshev(degrees[i], x[i]) for i in 1:N] result = Vector{Float64}(undef, prod(size.(cheb_1d, 1))) for (linear_idx, poly_all) in enumerate(Iterators.product(cheb_1d...)) result[linear_idx] = prod(poly_all) @@ -94,7 +89,7 @@ end function integrated_chebyshev_nd(degrees::NTuple{N, Int}) where {N} return prod([ - integrated_chebyshev(degrees[i], 1.0) - - integrated_chebyshev(degrees[i], 0.0) for i = 1:N + integrated_chebyshev(degrees[i], 1.0) - integrated_chebyshev(degrees[i], 0.0) for + i in 1:N ]) end diff --git a/test/Quadrature/runtests.jl b/test/Quadrature/runtests.jl index 0a51ee956..a48f13bd4 100644 --- a/test/Quadrature/runtests.jl +++ b/test/Quadrature/runtests.jl @@ -2,9 +2,17 @@ module QuadratureTests using Test -@testset "ClenshawCurtisQuadrature" begin include("ClenshawCurtisTests.jl") end -@testset "GaussQuadrature" begin include("GaussTests.jl") end -@testset "NewtonCotesQuadrature" begin include("NewtonCotesTests.jl") end -@testset "TensorProductQuadrature" begin include("TensorProductTests.jl") end +@testset "ClenshawCurtisQuadrature" begin + include("ClenshawCurtisTests.jl") +end +@testset "GaussQuadrature" begin + include("GaussTests.jl") +end +@testset "NewtonCotesQuadrature" begin + include("NewtonCotesTests.jl") +end +@testset "TensorProductQuadrature" begin + include("TensorProductTests.jl") +end end diff --git a/test/data/output/Geometry/README.md b/test/data/output/Geometry/README.md index 52fdbfa75..1b4b351c7 100644 --- a/test/data/output/Geometry/README.md +++ b/test/data/output/Geometry/README.md @@ -1,2 +1,3 @@ # Data output -This folder is used for outputting test related files related to Geometry.jl tests. Should be kept empty since these files are temporary and used only for testing. The reference files are in `../../reference`. \ No newline at end of file + +This folder is used for outputting test related files related to Geometry.jl tests. Should be kept empty since these files are temporary and used only for testing. The reference files are in `../../reference`. diff --git a/test/data/output/Poisson/README.md b/test/data/output/Poisson/README.md index 4169d047f..dc6752470 100644 --- a/test/data/output/Poisson/README.md +++ b/test/data/output/Poisson/README.md @@ -1,2 +1,3 @@ # Data output -This folder is used for outputting test related files related to AssemblerTests.jl tests. Should be kept empty since these files are temporary and used only for testing. \ No newline at end of file + +This folder is used for outputting test related files related to AssemblerTests.jl tests. Should be kept empty since these files are temporary and used only for testing.