diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index a190464..63ea3b1 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.0","generation_timestamp":"2024-02-08T12:29:24","documenter_version":"1.2.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.3","generation_timestamp":"2024-05-13T11:16:53","documenter_version":"1.4.1"}} \ No newline at end of file diff --git a/dev/Non-Parameterized/index.html b/dev/Non-Parameterized/index.html index da6b779..f38d109 100644 --- a/dev/Non-Parameterized/index.html +++ b/dev/Non-Parameterized/index.html @@ -1,2 +1,2 @@ -Non-Parameterized · SauterSchwabQuadrature.jl

Non-Parameterized

The called function in this implementation looks like:

sauterschwabintegral(sourcechart, testchart, integrand, accuracy, accuracy_pd).

sourcechart and testchart can be created by

testchart = simplex(P1,P2,P3); sourcechart = simplex(P4,P5,P6).

The order of the input arguments within the simplex() function does not matter.

simplex() generates the mapping and needs input arguments of type SVector{3,Float64}; the points P1 to P6 can be created by

P = point(x,y,z).

x, y and z are the coordinates of that particular point and point() creates a position vector which is of type SVector{3,Float64}.

The integrand must be defined as a function with two input arguments; the input arguments must be 3D vectors. The name of this function is the input argument.

Later on, the last argument accuracy will be discussed.

Since simplex() and point() are functions of CompScienceMeshes, CompScienceMeshes does not just have to be installed on the user's machine, but also be available in the current workspace; the same applies for this package as well. The two packages can be made available by

using SauterSchwabQuadrature and using CompScienceMeshes.

These two commands must always be run at the beginning, if using this type of implementation.

sauterschwabintegral() first modifies testchart and sourcechart with respect to the order of the arguments, within their simplex() functions. Secondly, depending on how many vertices both charts have in common, it generates an object of some type that contains the information of the accuracy and the integration strategy. After all of this has been done, this function will call another function with input arguments of the two modified charts, the original integrand and that new object.

To understand the arguments accuracy, accuracy_pd and the examples stored in the examples folder, the 'another called function' will be presented next:

Integration

According to item 1 on the homepage, four different constellations of the two triangles are possible:

  • Equal triangles $\to$ Common Face
  • Two vertices in common $\to$ Common Edge
  • One vertex in common $\to$ Common Vertex
  • Both triangles do not touch at all $\to$ Positive Distance

As each of those four constellations has its own integration method (because of a possible singularity in the kernel), the function sauterschwabintegral() has to call another function that handles the situation suitably; hence, it has four methods.

In the case sauterschwabintegral() has to deal with a situation of the first three cases, the two area-integrals will be transformed to four 1D integrals from zero to one; accuracy gives the number of quadrature points on that integration path, therefore, accuracy is of type unsigned Int64. In the case sauterschwabintegral() has to deal with a situation of the last case, accuracy_pd, which is again of type unsigned Int64, will be considered. It is a rule of how many quadrature points are created on both triangles. accuracy_pd =

  • 1 $\to$ 1
  • 2 $\to$ 3
  • 3 $\to$ 4
  • 4 $\to$ 6
  • 5 $\to$ 7
  • 6 $\to$ 12
  • 7 $\to$ 13
  • 8 $\to$ 36
  • 9 $\to$ 79
  • 10 $\to$ 105
  • 11 $\to$ 120
  • 12 $\to$ 400
  • 13 $\to$ 900

quadrature point(s) is(are) created on each triangle.

The user is now able to understand the examples in the '...non_parameterized.jl' files, or rather their titles. The order of the points within the two simplex() functions of Sourcechart and Testchart can be changed arbitrarily, the result will always remain the same. For those, who are interested in the 'called function', or want to skip sauterschwabintegral() and call the integration directly, which is actually only a sorting process, may read on now.

The called function by sauterschwabintegral() is:

sauterschwab_nonparameterized(sourcechart, testchart, integrand, method).

sourcechart and testchart are the modified versions of the original charts; integrand is the same as at the beginning, and method is that created object. The type of method is responsible for what method of sauterschwab_nonparameterized is chosen. The four methods will be listed now:

Common Face

$\Gamma$ and $\Gamma'$ are equal; hence, sourcechart and testchart are equal as well. The two charts have to be created by

testchart = sourcechart = simplex(P1,P2,P3);

where, P1, P2 and P3 are the vertices of that particular triangle. Note, that both charts must be equal, which means that the first argument of both charts must be equal, the second argument of both charts must be equal, and the last argument of both charts must be equal.

The last argument can be created by

cf = CommonFace(x).

cf is an object of type CommonFace(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found at the end of the commonfacenon_parameterized.jl file in the examples folder.

Common Edge

$\Gamma$ and $\Gamma'$ are now different; hence, sourcechart and testchart are different as well. The two charts have to be created in the following manner:

testchart = simplex(P1,P2,P3); sourcechart = simplex(P1,P4,P3).

Again, the order of the input arguments must be taken into account: The first argument of both charts must be equal, and the last argument of both charts must be equal. Consequently, the first and the last argument are the vertices which both triangles have in common.

The last argument can be created by

ce = CommonEdge(x).

ce is an object of type CommonEdge(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found at the end of the commonedgenon_parameterized.jl file in the examples folder.

Common Vertex

The two triangles and charts are again different. The two charts have to be created in the following manner:

sourcechart = simplex(P1,P2,P3); testchart = simplex(P1,P4,P5).

Again, the order of the input arguments must be taken into account: The first argument of both charts must be equal, the order of P2 and P3 with respect to sourcechart, and the order of P4 and P5 with respect to testchart, does not matter. Consequently, the first argument is the vertex both triangles have in common.

The last argument is created by

cv = CommonVertex(x).

cv is an object of type CommonVertex(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found at the end of the commonvertexnon_parameterized.jl file in the examples folder.

Positive Distance

As the triangles do not touch at all, the integration can directly be calculated with Gauss´s quadrature. Therefore, the order of the arguments within the two simplex() functions do not matter.

The last argument can be created by

pd = PositiveDistance(x).

pd is an object of type PositiveDistance(); x is the rule of how many quadrature points are created on both triangles.

An example for this case can be found at the end of the positivedistancenon_parameterized.jl file in the examples folder.

+Non-Parameterized · SauterSchwabQuadrature.jl

Non-Parameterized

The called function in this implementation looks like:

sauterschwabintegral(sourcechart, testchart, integrand, accuracy, accuracy_pd).

sourcechart and testchart can be created by

testchart = simplex(P1,P2,P3); sourcechart = simplex(P4,P5,P6).

The order of the input arguments within the simplex() function does not matter.

simplex() generates the mapping and needs input arguments of type SVector{3,Float64}; the points P1 to P6 can be created by

P = point(x,y,z).

x, y and z are the coordinates of that particular point and point() creates a position vector which is of type SVector{3,Float64}.

The integrand must be defined as a function with two input arguments; the input arguments must be 3D vectors. The name of this function is the input argument.

Later on, the last argument accuracy will be discussed.

Since simplex() and point() are functions of CompScienceMeshes, CompScienceMeshes does not just have to be installed on the user's machine, but also be available in the current workspace; the same applies for this package as well. The two packages can be made available by

using SauterSchwabQuadrature and using CompScienceMeshes.

These two commands must always be run at the beginning, if using this type of implementation.

sauterschwabintegral() first modifies testchart and sourcechart with respect to the order of the arguments, within their simplex() functions. Secondly, depending on how many vertices both charts have in common, it generates an object of some type that contains the information of the accuracy and the integration strategy. After all of this has been done, this function will call another function with input arguments of the two modified charts, the original integrand and that new object.

To understand the arguments accuracy, accuracy_pd and the examples stored in the examples folder, the 'another called function' will be presented next:

Integration

According to item 1 on the homepage, four different constellations of the two triangles are possible:

  • Equal triangles $\to$ Common Face
  • Two vertices in common $\to$ Common Edge
  • One vertex in common $\to$ Common Vertex
  • Both triangles do not touch at all $\to$ Positive Distance

As each of those four constellations has its own integration method (because of a possible singularity in the kernel), the function sauterschwabintegral() has to call another function that handles the situation suitably; hence, it has four methods.

In the case sauterschwabintegral() has to deal with a situation of the first three cases, the two area-integrals will be transformed to four 1D integrals from zero to one; accuracy gives the number of quadrature points on that integration path, therefore, accuracy is of type unsigned Int64. In the case sauterschwabintegral() has to deal with a situation of the last case, accuracy_pd, which is again of type unsigned Int64, will be considered. It is a rule of how many quadrature points are created on both triangles. accuracy_pd =

  • 1 $\to$ 1
  • 2 $\to$ 3
  • 3 $\to$ 4
  • 4 $\to$ 6
  • 5 $\to$ 7
  • 6 $\to$ 12
  • 7 $\to$ 13
  • 8 $\to$ 36
  • 9 $\to$ 79
  • 10 $\to$ 105
  • 11 $\to$ 120
  • 12 $\to$ 400
  • 13 $\to$ 900

quadrature point(s) is(are) created on each triangle.

The user is now able to understand the examples in the '...non_parameterized.jl' files, or rather their titles. The order of the points within the two simplex() functions of Sourcechart and Testchart can be changed arbitrarily, the result will always remain the same. For those, who are interested in the 'called function', or want to skip sauterschwabintegral() and call the integration directly, which is actually only a sorting process, may read on now.

The called function by sauterschwabintegral() is:

sauterschwab_nonparameterized(sourcechart, testchart, integrand, method).

sourcechart and testchart are the modified versions of the original charts; integrand is the same as at the beginning, and method is that created object. The type of method is responsible for what method of sauterschwab_nonparameterized is chosen. The four methods will be listed now:

Common Face

$\Gamma$ and $\Gamma'$ are equal; hence, sourcechart and testchart are equal as well. The two charts have to be created by

testchart = sourcechart = simplex(P1,P2,P3);

where, P1, P2 and P3 are the vertices of that particular triangle. Note, that both charts must be equal, which means that the first argument of both charts must be equal, the second argument of both charts must be equal, and the last argument of both charts must be equal.

The last argument can be created by

cf = CommonFace(x).

cf is an object of type CommonFace(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found at the end of the commonfacenon_parameterized.jl file in the examples folder.

Common Edge

$\Gamma$ and $\Gamma'$ are now different; hence, sourcechart and testchart are different as well. The two charts have to be created in the following manner:

testchart = simplex(P1,P2,P3); sourcechart = simplex(P1,P4,P3).

Again, the order of the input arguments must be taken into account: The first argument of both charts must be equal, and the last argument of both charts must be equal. Consequently, the first and the last argument are the vertices which both triangles have in common.

The last argument can be created by

ce = CommonEdge(x).

ce is an object of type CommonEdge(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found at the end of the commonedgenon_parameterized.jl file in the examples folder.

Common Vertex

The two triangles and charts are again different. The two charts have to be created in the following manner:

sourcechart = simplex(P1,P2,P3); testchart = simplex(P1,P4,P5).

Again, the order of the input arguments must be taken into account: The first argument of both charts must be equal, the order of P2 and P3 with respect to sourcechart, and the order of P4 and P5 with respect to testchart, does not matter. Consequently, the first argument is the vertex both triangles have in common.

The last argument is created by

cv = CommonVertex(x).

cv is an object of type CommonVertex(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found at the end of the commonvertexnon_parameterized.jl file in the examples folder.

Positive Distance

As the triangles do not touch at all, the integration can directly be calculated with Gauss´s quadrature. Therefore, the order of the arguments within the two simplex() functions do not matter.

The last argument can be created by

pd = PositiveDistance(x).

pd is an object of type PositiveDistance(); x is the rule of how many quadrature points are created on both triangles.

An example for this case can be found at the end of the positivedistancenon_parameterized.jl file in the examples folder.

diff --git a/dev/Parameterized/index.html b/dev/Parameterized/index.html index c369400..ff84bfa 100644 --- a/dev/Parameterized/index.html +++ b/dev/Parameterized/index.html @@ -1,2 +1,2 @@ -Parameterized · SauterSchwabQuadrature.jl

Parameterized

The called function in this implementation looks like:

sauterschwab_parameterized(integrand, method).

As on the homepage already mentioned, the user now has to parameterize the integration areas by himself; that means, that integrand is no more the original function that has to be integrated; integrand is now the parameterized version of the original integrand, including the two surface elements of both charts.

Before the parameterizations/charts (parameterization = chart) are built, the user has to figure out which integration method should be applied, and decide how accurate the integration shall be done. It is recommended, that the user read the page 'Non-Parameterized' before continuing to read here. Because otherwise, he may not be able to apply the concepts of 'integration method' and 'accuracy'.

The parameterization of the sourcetriangle will be called $\chi_t$, and the parameterization of the testtriangle will be called $\chi_\tau$. In the following, the parameterization of every single integration method will be presented.

Common Face

$\Gamma$ and $\Gamma'$ are equal, and both parameterizations must be equal as well: $\chi_t(u',v') = \chi_\tau(u,v)$.

The user's task is to find a parameterization which maps the reference triangle (right) onto the real triangle (left). The reference triangle is throughout this package always the same.

The original integrand, which is a function of $\textbf{x}$ and $\textbf{y}$, becomes:

\[f(\chi_\tau(u,v),\chi_t(u',v')) \cdot \|\frac{\partial \chi_\tau}{\partial u}\times\frac{\partial \chi_\tau}{\partial v}\| \cdot\|\frac{\partial \chi_t}{\partial u'}\times\frac{\partial \chi_t}{\partial v'}\|\]

.

This function method as well as the following methods, transform the two area integrals in parameters domain into four 1D integrals from zero to one; therefore, the last argument is created by

cf = CommonFace(x).

cf is an object of type CommonFace(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the commonfaceparameterized.jl file in the examples folder.

Common Edge

$\Gamma$ and $\Gamma'$ have an edge in common, and both parameterizations must fulfill the condition $\chi_t(s,0) = \chi_\tau(s,0)$. For example, this condition could be met if the points $(u\in[0,1];0)$ and $(u'\in[0,1];0)$ are mapped on the same point on the common edge.

The modified integrand looks like in the case Common Face.

The last argument can be created by

ce = CommonEdge(x).

ce is an object of type CommonEdge(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the commonedgeparameterized.jl file in the examples folder.

Common Vertex

$\Gamma$ and $\Gamma'$ have one vertex in common, and both parameterizations must fulfill the condition $\chi_t(0,0) = \chi_\tau(0,0)$. This condition means, that the origin of both reference triangles is mapped on the common vertex.

The modified integrand looks like in the case Common Face.

The last argument can be created by

cv = CommonVertex(x).

cv is an object of type CommonVertex(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the commonvertexparameterized.jl file in the examples folder.

Positive Distance

The two triangles do not touch at all, and both parameterizations only need to map from the reference triangle onto the real triangle.

The modified integrand looks like in the case Common Face.

The last argument can be created by

pd = PositiveDistance(x).

pd is an object of type PositiveDistance(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the positivedistanceparameterized.jl file in the examples folder.

+Parameterized · SauterSchwabQuadrature.jl

Parameterized

The called function in this implementation looks like:

sauterschwab_parameterized(integrand, method).

As on the homepage already mentioned, the user now has to parameterize the integration areas by himself; that means, that integrand is no more the original function that has to be integrated; integrand is now the parameterized version of the original integrand, including the two surface elements of both charts.

Before the parameterizations/charts (parameterization = chart) are built, the user has to figure out which integration method should be applied, and decide how accurate the integration shall be done. It is recommended, that the user read the page 'Non-Parameterized' before continuing to read here. Because otherwise, he may not be able to apply the concepts of 'integration method' and 'accuracy'.

The parameterization of the sourcetriangle will be called $\chi_t$, and the parameterization of the testtriangle will be called $\chi_\tau$. In the following, the parameterization of every single integration method will be presented.

Common Face

$\Gamma$ and $\Gamma'$ are equal, and both parameterizations must be equal as well: $\chi_t(u',v') = \chi_\tau(u,v)$.

The user's task is to find a parameterization which maps the reference triangle (right) onto the real triangle (left). The reference triangle is throughout this package always the same.

The original integrand, which is a function of $\textbf{x}$ and $\textbf{y}$, becomes:

\[f(\chi_\tau(u,v),\chi_t(u',v')) \cdot \|\frac{\partial \chi_\tau}{\partial u}\times\frac{\partial \chi_\tau}{\partial v}\| \cdot\|\frac{\partial \chi_t}{\partial u'}\times\frac{\partial \chi_t}{\partial v'}\|\]

.

This function method as well as the following methods, transform the two area integrals in parameters domain into four 1D integrals from zero to one; therefore, the last argument is created by

cf = CommonFace(x).

cf is an object of type CommonFace(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the commonfaceparameterized.jl file in the examples folder.

Common Edge

$\Gamma$ and $\Gamma'$ have an edge in common, and both parameterizations must fulfill the condition $\chi_t(s,0) = \chi_\tau(s,0)$. For example, this condition could be met if the points $(u\in[0,1];0)$ and $(u'\in[0,1];0)$ are mapped on the same point on the common edge.

The modified integrand looks like in the case Common Face.

The last argument can be created by

ce = CommonEdge(x).

ce is an object of type CommonEdge(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the commonedgeparameterized.jl file in the examples folder.

Common Vertex

$\Gamma$ and $\Gamma'$ have one vertex in common, and both parameterizations must fulfill the condition $\chi_t(0,0) = \chi_\tau(0,0)$. This condition means, that the origin of both reference triangles is mapped on the common vertex.

The modified integrand looks like in the case Common Face.

The last argument can be created by

cv = CommonVertex(x).

cv is an object of type CommonVertex(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the commonvertexparameterized.jl file in the examples folder.

Positive Distance

The two triangles do not touch at all, and both parameterizations only need to map from the reference triangle onto the real triangle.

The modified integrand looks like in the case Common Face.

The last argument can be created by

pd = PositiveDistance(x).

pd is an object of type PositiveDistance(); x is the number of quadrature points on the integration path $[0,1]$.

An example for this case can be found in the positivedistanceparameterized.jl file in the examples folder.

diff --git a/dev/apiref/index.html b/dev/apiref/index.html index dc2513d..fe4b2b0 100644 --- a/dev/apiref/index.html +++ b/dev/apiref/index.html @@ -1,7 +1,7 @@ -API Reference · SauterSchwabQuadrature.jl

API Reference

SauterSchwabQuadrature.sauterschwab_parameterizedMethod
sauterschwab_parameterized(integrand, method::SauterSchwabStrategy)

Compute interaction integrals using the quadrature introduced in [1].

Here, integrand is the pull-back of the integrand into the parametric domain of the two triangles that define the integration domain.

The second argument 'strategy' is an object whose type is for triangles one of

- `CommonFace`
+API Reference · SauterSchwabQuadrature.jl

API Reference

SauterSchwabQuadrature.sauterschwab_parameterizedMethod
sauterschwab_parameterized(integrand, method::SauterSchwabStrategy)

Compute interaction integrals using the quadrature introduced in [1].

Here, integrand is the pull-back of the integrand into the parametric domain of the two triangles that define the integration domain.

The second argument 'strategy' is an object whose type is for triangles one of

- `CommonFace`
 - `CommonEdge`
 - `CommonVertex`
 - `PositiveDistance`

and for quadrilaterals one of

- `CommonFaceQuad`
 - `CommonEdgeQuad`
-- `CommonVertexQuad`

according to the configuration of the two patches defining the domain of integration. The constructors of these classes take a single argument acc that defines the number of quadrature points along each of the four axes of the final rectangular (ξ,η) integration domain (see [1], Ch 5).

Note that here we use for a planar triangle the representation:

x = x[3] + u*(x[1]-x[3]) + v*(x[2]-x[3])

with u ranging from 0 to 1 and v ranging from 0 to 1-u. This parameter domain and representation is different from the one used in [1].

[1] Sauter. Schwwab, 'Boundary Element Methods', Springer Berlin Heidelberg, 2011

source
+- `CommonVertexQuad`

according to the configuration of the two patches defining the domain of integration. The constructors of these classes take a single argument acc that defines the number of quadrature points along each of the four axes of the final rectangular (ξ,η) integration domain (see [1], Ch 5).

Note that here we use for a planar triangle the representation:

x = x[3] + u*(x[1]-x[3]) + v*(x[2]-x[3])

with u ranging from 0 to 1 and v ranging from 0 to 1-u. This parameter domain and representation is different from the one used in [1].

[1] Sauter. Schwwab, 'Boundary Element Methods', Springer Berlin Heidelberg, 2011

source
diff --git a/dev/assets/documenter.js b/dev/assets/documenter.js index f531160..c6562b5 100644 --- a/dev/assets/documenter.js +++ b/dev/assets/documenter.js @@ -4,7 +4,6 @@ requirejs.config({ 'highlight-julia': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia.min', 'headroom': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/headroom.min', 'jqueryui': 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min', - 'minisearch': 'https://cdn.jsdelivr.net/npm/minisearch@6.1.0/dist/umd/index.min', 'katex-auto-render': 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min', 'jquery': 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min', 'headroom-jquery': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/jQuery.headroom.min', @@ -103,9 +102,10 @@ $(document).on("click", ".docstring header", function () { }); }); -$(document).on("click", ".docs-article-toggle-button", function () { +$(document).on("click", ".docs-article-toggle-button", function (event) { let articleToggleTitle = "Expand docstring"; let navArticleToggleTitle = "Expand all docstrings"; + let animationSpeed = event.noToggleAnimation ? 0 : 400; debounce(() => { if (isExpanded) { @@ -116,7 +116,7 @@ $(document).on("click", ".docs-article-toggle-button", function () { isExpanded = false; - $(".docstring section").slideUp(); + $(".docstring section").slideUp(animationSpeed); } else { $(this).removeClass("fa-chevron-down").addClass("fa-chevron-up"); $(".docstring-article-toggle-button") @@ -127,7 +127,7 @@ $(document).on("click", ".docs-article-toggle-button", function () { articleToggleTitle = "Collapse docstring"; navArticleToggleTitle = "Collapse all docstrings"; - $(".docstring section").slideDown(); + $(".docstring section").slideDown(animationSpeed); } $(this).prop("title", navArticleToggleTitle); @@ -224,224 +224,465 @@ $(document).ready(function () { }) //////////////////////////////////////////////////////////////////////////////// -require(['jquery', 'minisearch'], function($, minisearch) { - -// In general, most search related things will have "search" as a prefix. -// To get an in-depth about the thought process you can refer: https://hetarth02.hashnode.dev/series/gsoc +require(['jquery'], function($) { -let results = []; -let timer = undefined; +$(document).ready(function () { + let meta = $("div[data-docstringscollapsed]").data(); -let data = documenterSearchIndex["docs"].map((x, key) => { - x["id"] = key; // minisearch requires a unique for each object - return x; + if (meta?.docstringscollapsed) { + $("#documenter-article-toggle-button").trigger({ + type: "click", + noToggleAnimation: true, + }); + } }); -// list below is the lunr 2.1.3 list minus the intersect with names(Base) -// (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with) -// ideally we'd just filter the original list but it's not available as a variable -const stopWords = new Set([ - "a", - "able", - "about", - "across", - "after", - "almost", - "also", - "am", - "among", - "an", - "and", - "are", - "as", - "at", - "be", - "because", - "been", - "but", - "by", - "can", - "cannot", - "could", - "dear", - "did", - "does", - "either", - "ever", - "every", - "from", - "got", - "had", - "has", - "have", - "he", - "her", - "hers", - "him", - "his", - "how", - "however", - "i", - "if", - "into", - "it", - "its", - "just", - "least", - "like", - "likely", - "may", - "me", - "might", - "most", - "must", - "my", - "neither", - "no", - "nor", - "not", - "of", - "off", - "often", - "on", - "or", - "other", - "our", - "own", - "rather", - "said", - "say", - "says", - "she", - "should", - "since", - "so", - "some", - "than", - "that", - "the", - "their", - "them", - "then", - "there", - "these", - "they", - "this", - "tis", - "to", - "too", - "twas", - "us", - "wants", - "was", - "we", - "were", - "what", - "when", - "who", - "whom", - "why", - "will", - "would", - "yet", - "you", - "your", -]); - -let index = new minisearch({ - fields: ["title", "text"], // fields to index for full-text search - storeFields: ["location", "title", "text", "category", "page"], // fields to return with search results - processTerm: (term) => { - let word = stopWords.has(term) ? null : term; - if (word) { - // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names - word = word - .replace(/^[^a-zA-Z0-9@!]+/, "") - .replace(/[^a-zA-Z0-9@!]+$/, ""); - } +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { - return word ?? null; - }, - // add . as a separator, because otherwise "title": "Documenter.Anchors.add!", would not find anything if searching for "add!", only for the entire qualification - tokenize: (string) => string.split(/[\s\-\.]+/), - // options which will be applied during the search - searchOptions: { - boost: { title: 100 }, - fuzzy: 2, +/* +To get an in-depth about the thought process you can refer: https://hetarth02.hashnode.dev/series/gsoc + +PSEUDOCODE: + +Searching happens automatically as the user types or adjusts the selected filters. +To preserve responsiveness, as much as possible of the slow parts of the search are done +in a web worker. Searching and result generation are done in the worker, and filtering and +DOM updates are done in the main thread. The filters are in the main thread as they should +be very quick to apply. This lets filters be changed without re-searching with minisearch +(which is possible even if filtering is on the worker thread) and also lets filters be +changed _while_ the worker is searching and without message passing (neither of which are +possible if filtering is on the worker thread) + +SEARCH WORKER: + +Import minisearch + +Build index + +On message from main thread + run search + find the first 200 unique results from each category, and compute their divs for display + note that this is necessary and sufficient information for the main thread to find the + first 200 unique results from any given filter set + post results to main thread + +MAIN: + +Launch worker + +Declare nonconstant globals (worker_is_running, last_search_text, unfiltered_results) + +On text update + if worker is not running, launch_search() + +launch_search + set worker_is_running to true, set last_search_text to the search text + post the search query to worker + +on message from worker + if last_search_text is not the same as the text in the search field, + the latest search result is not reflective of the latest search query, so update again + launch_search() + otherwise + set worker_is_running to false + + regardless, display the new search results to the user + save the unfiltered_results as a global + update_search() + +on filter click + adjust the filter selection + update_search() + +update_search + apply search filters by looping through the unfiltered_results and finding the first 200 + unique results that match the filters + + Update the DOM +*/ + +/////// SEARCH WORKER /////// + +function worker_function(documenterSearchIndex, documenterBaseURL, filters) { + importScripts( + "https://cdn.jsdelivr.net/npm/minisearch@6.1.0/dist/umd/index.min.js" + ); + + let data = documenterSearchIndex.map((x, key) => { + x["id"] = key; // minisearch requires a unique for each object + return x; + }); + + // list below is the lunr 2.1.3 list minus the intersect with names(Base) + // (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with) + // ideally we'd just filter the original list but it's not available as a variable + const stopWords = new Set([ + "a", + "able", + "about", + "across", + "after", + "almost", + "also", + "am", + "among", + "an", + "and", + "are", + "as", + "at", + "be", + "because", + "been", + "but", + "by", + "can", + "cannot", + "could", + "dear", + "did", + "does", + "either", + "ever", + "every", + "from", + "got", + "had", + "has", + "have", + "he", + "her", + "hers", + "him", + "his", + "how", + "however", + "i", + "if", + "into", + "it", + "its", + "just", + "least", + "like", + "likely", + "may", + "me", + "might", + "most", + "must", + "my", + "neither", + "no", + "nor", + "not", + "of", + "off", + "often", + "on", + "or", + "other", + "our", + "own", + "rather", + "said", + "say", + "says", + "she", + "should", + "since", + "so", + "some", + "than", + "that", + "the", + "their", + "them", + "then", + "there", + "these", + "they", + "this", + "tis", + "to", + "too", + "twas", + "us", + "wants", + "was", + "we", + "were", + "what", + "when", + "who", + "whom", + "why", + "will", + "would", + "yet", + "you", + "your", + ]); + + let index = new MiniSearch({ + fields: ["title", "text"], // fields to index for full-text search + storeFields: ["location", "title", "text", "category", "page"], // fields to return with results processTerm: (term) => { let word = stopWords.has(term) ? null : term; if (word) { + // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names word = word .replace(/^[^a-zA-Z0-9@!]+/, "") .replace(/[^a-zA-Z0-9@!]+$/, ""); + + word = word.toLowerCase(); } return word ?? null; }, + // add . as a separator, because otherwise "title": "Documenter.Anchors.add!", would not + // find anything if searching for "add!", only for the entire qualification tokenize: (string) => string.split(/[\s\-\.]+/), - }, -}); + // options which will be applied during the search + searchOptions: { + prefix: true, + boost: { title: 100 }, + fuzzy: 2, + }, + }); -index.addAll(data); + index.addAll(data); + + /** + * Used to map characters to HTML entities. + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + const htmlEscapes = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + }; + + /** + * Used to match HTML entities and HTML characters. + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + const reUnescapedHtml = /[&<>"']/g; + const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** + * Escape function from lodash + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + function escape(string) { + return string && reHasUnescapedHtml.test(string) + ? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) + : string || ""; + } -let filters = [...new Set(data.map((x) => x.category))]; -var modal_filters = make_modal_body_filters(filters); -var filter_results = []; + /** + * Make the result component given a minisearch result data object and the value + * of the search input as queryString. To view the result object structure, refer: + * https://lucaong.github.io/minisearch/modules/_minisearch_.html#searchresult + * + * @param {object} result + * @param {string} querystring + * @returns string + */ + function make_search_result(result, querystring) { + let search_divider = `
`; + let display_link = + result.location.slice(Math.max(0), Math.min(50, result.location.length)) + + (result.location.length > 30 ? "..." : ""); // To cut-off the link because it messes with the overflow of the whole div + + if (result.page !== "") { + display_link += ` (${result.page})`; + } -$(document).on("keyup", ".documenter-search-input", function (event) { - // Adding a debounce to prevent disruptions from super-speed typing! - debounce(() => update_search(filter_results), 300); + let textindex = new RegExp(`${querystring}`, "i").exec(result.text); + let text = + textindex !== null + ? result.text.slice( + Math.max(textindex.index - 100, 0), + Math.min( + textindex.index + querystring.length + 100, + result.text.length + ) + ) + : ""; // cut-off text before and after from the match + + text = text.length ? escape(text) : ""; + + let display_result = text.length + ? "..." + + text.replace( + new RegExp(`${escape(querystring)}`, "i"), // For first occurrence + '$&' + ) + + "..." + : ""; // highlights the match + + let in_code = false; + if (!["page", "section"].includes(result.category.toLowerCase())) { + in_code = true; + } + + // We encode the full url to escape some special characters which can lead to broken links + let result_div = ` + +
+
${escape(result.title)}
+
${result.category}
+
+

+ ${display_result} +

+
+ ${display_link} +
+
+ ${search_divider} + `; + + return result_div; + } + + self.onmessage = function (e) { + let query = e.data; + let results = index.search(query, { + filter: (result) => { + // Only return relevant results + return result.score >= 1; + }, + }); + + // Pre-filter to deduplicate and limit to 200 per category to the extent + // possible without knowing what the filters are. + let filtered_results = []; + let counts = {}; + for (let filter of filters) { + counts[filter] = 0; + } + let present = {}; + + for (let result of results) { + cat = result.category; + cnt = counts[cat]; + if (cnt < 200) { + id = cat + "---" + result.location; + if (present[id]) { + continue; + } + present[id] = true; + filtered_results.push({ + location: result.location, + category: cat, + div: make_search_result(result, query), + }); + } + } + + postMessage(filtered_results); + }; +} + +// `worker = Threads.@spawn worker_function(documenterSearchIndex)`, but in JavaScript! +const filters = [ + ...new Set(documenterSearchIndex["docs"].map((x) => x.category)), +]; +const worker_str = + "(" + + worker_function.toString() + + ")(" + + JSON.stringify(documenterSearchIndex["docs"]) + + "," + + JSON.stringify(documenterBaseURL) + + "," + + JSON.stringify(filters) + + ")"; +const worker_blob = new Blob([worker_str], { type: "text/javascript" }); +const worker = new Worker(URL.createObjectURL(worker_blob)); + +/////// SEARCH MAIN /////// + +// Whether the worker is currently handling a search. This is a boolean +// as the worker only ever handles 1 or 0 searches at a time. +var worker_is_running = false; + +// The last search text that was sent to the worker. This is used to determine +// if the worker should be launched again when it reports back results. +var last_search_text = ""; + +// The results of the last search. This, in combination with the state of the filters +// in the DOM, is used compute the results to display on calls to update_search. +var unfiltered_results = []; + +// Which filter is currently selected +var selected_filter = ""; + +$(document).on("input", ".documenter-search-input", function (event) { + if (!worker_is_running) { + launch_search(); + } }); +function launch_search() { + worker_is_running = true; + last_search_text = $(".documenter-search-input").val(); + worker.postMessage(last_search_text); +} + +worker.onmessage = function (e) { + if (last_search_text !== $(".documenter-search-input").val()) { + launch_search(); + } else { + worker_is_running = false; + } + + unfiltered_results = e.data; + update_search(); +}; + $(document).on("click", ".search-filter", function () { if ($(this).hasClass("search-filter-selected")) { - $(this).removeClass("search-filter-selected"); + selected_filter = ""; } else { - $(this).addClass("search-filter-selected"); + selected_filter = $(this).text().toLowerCase(); } - // Adding a debounce to prevent disruptions from crazy clicking! - debounce(() => get_filters(), 300); + // This updates search results and toggles classes for UI: + update_search(); }); -/** - * A debounce function, takes a function and an optional timeout in milliseconds - * - * @function callback - * @param {number} timeout - */ -function debounce(callback, timeout = 300) { - clearTimeout(timer); - timer = setTimeout(callback, timeout); -} - /** * Make/Update the search component - * - * @param {string[]} selected_filters */ -function update_search(selected_filters = []) { - let initial_search_body = ` -
Type something to get started!
- `; - +function update_search() { let querystring = $(".documenter-search-input").val(); if (querystring.trim()) { - results = index.search(querystring, { - filter: (result) => { - // Filtering results - if (selected_filters.length === 0) { - return result.score >= 1; - } else { - return ( - result.score >= 1 && selected_filters.includes(result.category) - ); - } - }, - }); + if (selected_filter == "") { + results = unfiltered_results; + } else { + results = unfiltered_results.filter((result) => { + return selected_filter == result.category.toLowerCase(); + }); + } let search_result_container = ``; + let modal_filters = make_modal_body_filters(); let search_divider = `
`; if (results.length) { @@ -449,19 +690,23 @@ function update_search(selected_filters = []) { let count = 0; let search_results = ""; - results.forEach(function (result) { - if (result.location) { - // Checking for duplication of results for the same page - if (!links.includes(result.location)) { - search_results += make_search_result(result, querystring); - count++; - } - + for (var i = 0, n = results.length; i < n && count < 200; ++i) { + let result = results[i]; + if (result.location && !links.includes(result.location)) { + search_results += result.div; + count++; links.push(result.location); } - }); + } - let result_count = `
${count} result(s)
`; + if (count == 1) { + count_str = "1 result"; + } else if (count == 200) { + count_str = "200+ results"; + } else { + count_str = count + " results"; + } + let result_count = `
${count_str}
`; search_result_container = `
@@ -490,125 +735,37 @@ function update_search(selected_filters = []) { $(".search-modal-card-body").html(search_result_container); } else { - filter_results = []; - modal_filters = make_modal_body_filters(filters, filter_results); - if (!$(".search-modal-card-body").hasClass("is-justify-content-center")) { $(".search-modal-card-body").addClass("is-justify-content-center"); } - $(".search-modal-card-body").html(initial_search_body); + $(".search-modal-card-body").html(` +
Type something to get started!
+ `); } } /** * Make the modal filter html * - * @param {string[]} filters - * @param {string[]} selected_filters * @returns string */ -function make_modal_body_filters(filters, selected_filters = []) { - let str = ``; - - filters.forEach((val) => { - if (selected_filters.includes(val)) { - str += `${val}`; - } else { - str += `${val}`; - } - }); +function make_modal_body_filters() { + let str = filters + .map((val) => { + if (selected_filter == val.toLowerCase()) { + return `${val}`; + } else { + return `${val}`; + } + }) + .join(""); - let filter_html = ` + return `
Filters: ${str} -
- `; - - return filter_html; -} - -/** - * Make the result component given a minisearch result data object and the value of the search input as queryString. - * To view the result object structure, refer: https://lucaong.github.io/minisearch/modules/_minisearch_.html#searchresult - * - * @param {object} result - * @param {string} querystring - * @returns string - */ -function make_search_result(result, querystring) { - let search_divider = `
`; - let display_link = - result.location.slice(Math.max(0), Math.min(50, result.location.length)) + - (result.location.length > 30 ? "..." : ""); // To cut-off the link because it messes with the overflow of the whole div - - if (result.page !== "") { - display_link += ` (${result.page})`; - } - - let textindex = new RegExp(`\\b${querystring}\\b`, "i").exec(result.text); - let text = - textindex !== null - ? result.text.slice( - Math.max(textindex.index - 100, 0), - Math.min( - textindex.index + querystring.length + 100, - result.text.length - ) - ) - : ""; // cut-off text before and after from the match - - let display_result = text.length - ? "..." + - text.replace( - new RegExp(`\\b${querystring}\\b`, "i"), // For first occurrence - '$&' - ) + - "..." - : ""; // highlights the match - - let in_code = false; - if (!["page", "section"].includes(result.category.toLowerCase())) { - in_code = true; - } - - // We encode the full url to escape some special characters which can lead to broken links - let result_div = ` - -
-
${result.title}
-
${result.category}
-
-

- ${display_result} -

-
- ${display_link} -
-
- ${search_divider} - `; - - return result_div; -} - -/** - * Get selected filters, remake the filter html and lastly update the search modal - */ -function get_filters() { - let ele = $(".search-filters .search-filter-selected").get(); - filter_results = ele.map((x) => $(x).text().toLowerCase()); - modal_filters = make_modal_body_filters(filters, filter_results); - update_search(filter_results); +
`; } }) @@ -635,103 +792,107 @@ $(document).ready(function () { //////////////////////////////////////////////////////////////////////////////// require(['jquery'], function($) { -let search_modal_header = ` - -`; - -let initial_search_body = ` -
Type something to get started!
-`; - -let search_modal_footer = ` - -`; - -$(document.body).append( - ` - +
diff --git a/dev/index.html b/dev/index.html index 68e1264..8c29a43 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Introduction · SauterSchwabQuadrature.jl

Introduction

This package provides the Sauter-Schwab regularizing coordinate transformations [1] such that 4D integrals of the form

\[\int_{\Gamma}\int_{\Gamma'}b_i(\bm{x})\,k(\bm{x},\bm{y})\, b_j(\bm{y})\,\mathrm{d}S(\bm{y})\,\mathrm{d}S(\bm{x})\]

with Cauchy-singular integral kernels $k(\bm{x},\bm{y})$ can be integrated via numerical quadrature. The integrals denote double surface integrals over

  • triangles (curved or flat) or
  • quadrilaterals (curved or flat)

$\Gamma$ and $\Gamma'$ in 3D Space. The functions $b_i(\bm{x})$ and $b_i(\bm{y})$ are assumed to be real valued and non-singular.

These kind of integrals occur in the area of boundary element methods (BEM) for solving elliptic partial differential equations. It can be interpreted as the interaction of the two basisfunctions $b_i(\bm{x})$ and $b_i(\bm{y})$, with respect to their domains $\Gamma$ and $\Gamma'$, which, for instance, correspond to the cells of a meshed surface.

Info

The triangles or quadrilaterals must be either equal, have two vertices in common, have one vertex in common or do not touch at all. A partial overlap is forbidden.

In the current implementation $\Gamma$ and $\Gamma'$ have to be both either triangles or quadrilatersls. However, mixed cases can be implemented, too.

References

[1] Sauter S. Schwab C., "Boundary Element Methods (Springer Series in Computational Mathematics)", Chapter 5, Springer, 2010.

+Introduction · SauterSchwabQuadrature.jl

Introduction

This package provides the Sauter-Schwab regularizing coordinate transformations [1] such that 4D integrals of the form

\[\int_{\Gamma}\int_{\Gamma'}b_i(\bm{x})\,k(\bm{x},\bm{y})\, b_j(\bm{y})\,\mathrm{d}S(\bm{y})\,\mathrm{d}S(\bm{x})\]

with Cauchy-singular integral kernels $k(\bm{x},\bm{y})$ can be integrated via numerical quadrature. The integrals denote double surface integrals over

  • triangles (curved or flat) or
  • quadrilaterals (curved or flat)

$\Gamma$ and $\Gamma'$ in 3D Space. The functions $b_i(\bm{x})$ and $b_i(\bm{y})$ are assumed to be real valued and non-singular.

These kind of integrals occur in the area of boundary element methods (BEM) for solving elliptic partial differential equations. It can be interpreted as the interaction of the two basisfunctions $b_i(\bm{x})$ and $b_i(\bm{y})$, with respect to their domains $\Gamma$ and $\Gamma'$, which, for instance, correspond to the cells of a meshed surface.

Info

The triangles or quadrilaterals must be either equal, have two vertices in common, have one vertex in common or do not touch at all. A partial overlap is forbidden.

In the current implementation $\Gamma$ and $\Gamma'$ have to be both either triangles or quadrilatersls. However, mixed cases can be implemented, too.

References

[1] Sauter S. Schwab C., "Boundary Element Methods (Springer Series in Computational Mathematics)", Chapter 5, Springer, 2010.

diff --git a/dev/manual/index.html b/dev/manual/index.html index 02c71af..7203ed6 100644 --- a/dev/manual/index.html +++ b/dev/manual/index.html @@ -1,3 +1,3 @@ Manual · SauterSchwabQuadrature.jl

Manual

Fundamentally, one function is provided:

sauterschwab_parameterized(integrand, strategy)

The first argument integrand is the parameterized integrand $k'(\chi_\tau(u,v), \chi_t(u',v'))$. That is, it takes as argument two tuples:

integrand((u,v), (u',v'))

The second argument strategy specifies the reparametrization and is one of (the by this package provided) structs:

For triangles

  • CommonFace
  • CommonEdge
  • CommonVertex
  • PositiveDistance

For quadrilaterals

  • CommonFaceQuad
  • CommonEdgeQuad
  • CommonVertexQuad

Each such struct takes one argument specifying the quadrature rule, e.g.,

strategy = CommonEdge(qrule)

where qrule is a vector of (point, weight) tuples for a quadrature on the domain $[0,1]$.

Tip

We recommend the FastGaussQuadrature.jl package. For a Gauss-Legendre quadrature a method is provided that maps to the $[0,1]$ domain:

order = 10
-qrule = SauterSchwabQuadrature._legendre(order, 0, 1)
+qrule = SauterSchwabQuadrature._legendre(order, 0, 1) diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 0000000..375fa0b Binary files /dev/null and b/dev/objects.inv differ