From f86f65f09b41f5f0996e78985e5b59f4ce77a515 Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Thu, 9 May 2024 19:57:29 -0700 Subject: [PATCH 1/9] start with docs/src/conventions --- docs/src/conventions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 4f9e0ec4e6..6e8c6be498 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -106,3 +106,5 @@ based on the following rules. - `wrap_array` should be used as default option. `wrap_array_native` should only be used when necessary, e.g., to avoid additional overhead when interfacing with external C libraries such as HDF5, MPI, or visualization. + +# Numerical Types and Stability \ No newline at end of file From f24c1eab07baa886cdabcabe25f321c3888e21f5 Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Fri, 10 May 2024 12:30:42 -0700 Subject: [PATCH 2/9] check doc style --- docs/src/conventions.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 6e8c6be498..623fbdd400 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -41,7 +41,7 @@ following naming conventions: use these indices. -# Keywords in elixirs +## Keywords in elixirs Trixi.jl is distributed with several examples in the form of elixirs, small Julia scripts containing everything to set up and run a simulation. Working @@ -61,9 +61,9 @@ can only perform simple replacements. Some standard variables names are Moreover, [`convergence_test`](@ref) requires that the spatial resolution is set via the keywords - `initial_refinement_level` - (an integer, e.g. for the [`TreeMesh`](@ref) and the [`P4estMesh`](@ref)) or + (an integer, e.g., for the [`TreeMesh`](@ref) and the [`P4estMesh`](@ref)) or - `cells_per_dimension` - (a tuple of integers, one per spatial dimension, e.g. for the [`StructuredMesh`](@ref) + (a tuple of integers, one per spatial dimension, e.g., for the [`StructuredMesh`](@ref) and the [`DGMultiMesh`](@ref)). @@ -101,10 +101,25 @@ based on the following rules. (or `wrap_array_native(u_ode, semi)`) for further processing. - When some solution is passed together with the `mesh, equations, solver, cache, ...`, it is already wrapped via `wrap_array` (or `wrap_array_native`). -- Exceptions of this rule are possible, e.g. for AMR, but must be documented in +- Exceptions of this rule are possible, e.g., for AMR, but must be documented in the code. - `wrap_array` should be used as default option. `wrap_array_native` should only be used when necessary, e.g., to avoid additional overhead when interfacing with external C libraries such as HDF5, MPI, or visualization. -# Numerical Types and Stability \ No newline at end of file +## Numeric types and type stability +In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the type stability of these numeric types throughout the development process. Below are some guidelines to apply in various scenarios. + +- Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. + +- For the real numbers that fail to be represented in machine (e.g., `0.1`, `1/3`, `pi`), we can use `convert` to make them consistent with the type of function input. A typical use would be `convert(0.1, RealT)` and here we assign a numeric type to the `RealT` before calling it. + +- **Exact floating-point numbers**: Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. + +- **Non-exact floating-point numbers**: + For real numbers that cannot be exactly represented in machine precision (e.g., `0.1`, `1/3`, `pi`), use the `convert` function to make them consistent with the type of the function input. + + - **Example Usage**: To convert a number like `0.1` to match the type `RealT`, use the following syntax, ensuring that `RealT` is assigned to the appropriate numeric type before calling the `convert` function: + ```julia + convert(RealT, 0.1) + ``` \ No newline at end of file From eb7d831b63af3006b1edd6604fce27b7ebc81a5c Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Fri, 10 May 2024 13:58:23 -0700 Subject: [PATCH 3/9] initial docs version --- docs/src/conventions.md | 51 ++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 623fbdd400..b90c110b06 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -110,16 +110,41 @@ based on the following rules. ## Numeric types and type stability In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the type stability of these numeric types throughout the development process. Below are some guidelines to apply in various scenarios. -- Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. - -- For the real numbers that fail to be represented in machine (e.g., `0.1`, `1/3`, `pi`), we can use `convert` to make them consistent with the type of function input. A typical use would be `convert(0.1, RealT)` and here we assign a numeric type to the `RealT` before calling it. - -- **Exact floating-point numbers**: Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. - -- **Non-exact floating-point numbers**: - For real numbers that cannot be exactly represented in machine precision (e.g., `0.1`, `1/3`, `pi`), use the `convert` function to make them consistent with the type of the function input. - - - **Example Usage**: To convert a number like `0.1` to match the type `RealT`, use the following syntax, ensuring that `RealT` is assigned to the appropriate numeric type before calling the `convert` function: - ```julia - convert(RealT, 0.1) - ``` \ No newline at end of file +- **Exact floating-point numbers**: Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. For example, + ```julia + # Assume we have `0.25`, `0.5`, `1/2` in function + 0.25f0, 0.5f0, 0.5f0 # corresponding numbers + ``` + +- **Non-exact floating-point numbers**: For real numbers that cannot be exactly represented in machine precision (e.g., `0.1`, `1/3`, `pi`), use the `convert` function to make them consistent with the type of the function input. For example, + ```julia + # Assume we are handling `pi` in function + function foo(input1, input2, input3, ...) + RealT = eletype(input1) # good practice to always use the first input to extract numeric type + # ... + c1 = convert(RealT, pi) * c2 # sample operation + # ... + end + ``` + +- **Integer numbers**: Integers need special consideration. Using functions like `convert` (as mentioned above), `zero`, and `one` to change integers to a specific type or keeping them in integer format is feasible in most cases. We should balance code readability and consistency while maintaining type stability. Here are some examples to guide our developers. + ```julia + # The first example - `SVector`, keep code consistency within `SVector` + SVector(0, 0, 0) + SVector(zero(RealT), zero(RealT), zero(RealT)) + Svector(one(RealT), one(RealT), one(RealT)) + + # The second example - inner functions, keep them type-stable as well + function foo(input1, input2, input3, ...) + RealT = eletype(input1) # good practice to always use the first input to extract numeric type + # ... + c1 = c2 > 0.5f0 ? one(RealT) : convert(RealT, 0.1) + # ... + end + + # The third example - some operations (like `/`, `sprt`, `inv`, and so on) + c1 = convert(RealT, 4) # suppose we get RealT before + c2 = 1 / c1 + c3 = sprt(c1) + c4 = inv(c1) + ``` \ No newline at end of file From 3967c6453b0afaebc378b3659e01c7153c5926b6 Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Fri, 10 May 2024 14:07:49 -0700 Subject: [PATCH 4/9] check and add more --- docs/src/conventions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index b90c110b06..916839d171 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -147,4 +147,5 @@ In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the ty c2 = 1 / c1 c3 = sprt(c1) c4 = inv(c1) - ``` \ No newline at end of file + ``` + In general, for integer numbers, our developers should apply a case-by-case strategy to maintain type stability. \ No newline at end of file From f2dc062cc6e1bbbb550ffed8e54e4e46ae64c3fd Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Fri, 10 May 2024 14:26:43 -0700 Subject: [PATCH 5/9] add more check --- docs/src/conventions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 916839d171..a1ff69ac4c 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -138,14 +138,14 @@ In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the ty function foo(input1, input2, input3, ...) RealT = eletype(input1) # good practice to always use the first input to extract numeric type # ... - c1 = c2 > 0.5f0 ? one(RealT) : convert(RealT, 0.1) + c1 = c2 > 0.5f0 ? one(RealT) : convert(RealT, 0.1) # make type-stable # ... end - # The third example - some operations (like `/`, `sprt`, `inv`, and so on) + # The third example - some operations (e.g., `/`, `sprt`, `inv`), convert them definitely c1 = convert(RealT, 4) # suppose we get RealT before c2 = 1 / c1 c3 = sprt(c1) c4 = inv(c1) ``` - In general, for integer numbers, our developers should apply a case-by-case strategy to maintain type stability. \ No newline at end of file + In general, in the case of integer numbers, our developers should apply a case-by-case strategy to maintain type stability. \ No newline at end of file From b167ad2e3027be56b199c41d62af44fbe84ad2e6 Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Mon, 13 May 2024 18:25:57 -0700 Subject: [PATCH 6/9] apply suggestions from review --- docs/src/conventions.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index a1ff69ac4c..8711cba518 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -119,8 +119,8 @@ In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the ty - **Non-exact floating-point numbers**: For real numbers that cannot be exactly represented in machine precision (e.g., `0.1`, `1/3`, `pi`), use the `convert` function to make them consistent with the type of the function input. For example, ```julia # Assume we are handling `pi` in function - function foo(input1, input2, input3, ...) - RealT = eletype(input1) # good practice to always use the first input to extract numeric type + function foo(..., input, ...) + RealT = eltype(input) # see **notes** below # ... c1 = convert(RealT, pi) * c2 # sample operation # ... @@ -135,17 +135,22 @@ In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the ty Svector(one(RealT), one(RealT), one(RealT)) # The second example - inner functions, keep them type-stable as well - function foo(input1, input2, input3, ...) - RealT = eletype(input1) # good practice to always use the first input to extract numeric type + function foo(..., input, ...) + RealT = eltype(input) # see **notes** below # ... c1 = c2 > 0.5f0 ? one(RealT) : convert(RealT, 0.1) # make type-stable # ... end - # The third example - some operations (e.g., `/`, `sprt`, `inv`), convert them definitely + # The third example - some operations (e.g., `/`, `sqrt`, `inv`), convert them definitely c1 = convert(RealT, 4) # suppose we get RealT before c2 = 1 / c1 - c3 = sprt(c1) + c3 = sqrt(c1) c4 = inv(c1) ``` - In general, in the case of integer numbers, our developers should apply a case-by-case strategy to maintain type stability. \ No newline at end of file + In general, in the case of integer numbers, our developers should apply a case-by-case strategy to maintain type stability. + + **Notes:** + 1. If the function gets a local pointwise vector of the solution variables `u` such as `flux(u, equations)`, use `u` to determine the real type `eltype(u)`. + 2. If `u` is not passed as an argument but a vector of coordinates `x` such as `initial_condition(x, t, equations)`, use `eltype(x)` instead. + 3. Choose an appropriate argument to determine the real type otherwise. \ No newline at end of file From a33e2084ab82eb497edcb932a55e30a609a2cced Mon Sep 17 00:00:00 2001 From: Huiyu Xie Date: Tue, 14 May 2024 13:22:58 -0700 Subject: [PATCH 7/9] apply suggestions from code review Co-authored-by: Michael Schlottke-Lakemper --- docs/src/conventions.md | 88 ++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 8711cba518..5094f44317 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -108,49 +108,55 @@ based on the following rules. with external C libraries such as HDF5, MPI, or visualization. ## Numeric types and type stability -In Trixi.jl, `Float32` and `Float64` types are fully supported. We ensure the type stability of these numeric types throughout the development process. Below are some guidelines to apply in various scenarios. +In Trixi.jl, we use generic programming to support custom data types to store the numerical simulation data, including standard floating point types and automatic differentiation types. +Specifically, `Float32` and `Float64` types are fully supported, including the ability to run Trixi.jl on hardware that only supports `Float32` types. +We ensure the type stability of these numeric types throughout the development process. +Below are some guidelines to apply in various scenarios. - **Exact floating-point numbers**: Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. For example, - ```julia - # Assume we have `0.25`, `0.5`, `1/2` in function - 0.25f0, 0.5f0, 0.5f0 # corresponding numbers - ``` - -- **Non-exact floating-point numbers**: For real numbers that cannot be exactly represented in machine precision (e.g., `0.1`, `1/3`, `pi`), use the `convert` function to make them consistent with the type of the function input. For example, - ```julia - # Assume we are handling `pi` in function - function foo(..., input, ...) - RealT = eltype(input) # see **notes** below - # ... - c1 = convert(RealT, pi) * c2 # sample operation - # ... - end - ``` - -- **Integer numbers**: Integers need special consideration. Using functions like `convert` (as mentioned above), `zero`, and `one` to change integers to a specific type or keeping them in integer format is feasible in most cases. We should balance code readability and consistency while maintaining type stability. Here are some examples to guide our developers. - ```julia - # The first example - `SVector`, keep code consistency within `SVector` - SVector(0, 0, 0) - SVector(zero(RealT), zero(RealT), zero(RealT)) - Svector(one(RealT), one(RealT), one(RealT)) - - # The second example - inner functions, keep them type-stable as well - function foo(..., input, ...) +```julia +# Assume we have `0.25`, `0.5`, `1/2` in function +0.25f0, 0.5f0, 0.5f0 # corresponding numbers + +### Non-exact floating-point numbers + +For real numbers that cannot be exactly represented in machine precision (e.g., `0.1`, `1/3`, `pi`), use the `convert` function to make them consistent with the type of the function input. For example, +```julia +# Assume we are handling `pi` in function +function foo(..., input, ...) RealT = eltype(input) # see **notes** below # ... - c1 = c2 > 0.5f0 ? one(RealT) : convert(RealT, 0.1) # make type-stable + c1 = convert(RealT, pi) * c2 # sample operation # ... - end - - # The third example - some operations (e.g., `/`, `sqrt`, `inv`), convert them definitely - c1 = convert(RealT, 4) # suppose we get RealT before - c2 = 1 / c1 - c3 = sqrt(c1) - c4 = inv(c1) - ``` - In general, in the case of integer numbers, our developers should apply a case-by-case strategy to maintain type stability. - - **Notes:** - 1. If the function gets a local pointwise vector of the solution variables `u` such as `flux(u, equations)`, use `u` to determine the real type `eltype(u)`. - 2. If `u` is not passed as an argument but a vector of coordinates `x` such as `initial_condition(x, t, equations)`, use `eltype(x)` instead. - 3. Choose an appropriate argument to determine the real type otherwise. \ No newline at end of file +end +``` + +### Integer numbers + +Integers need special consideration. Using functions like `convert` (as mentioned above), `zero`, and `one` to change integers to a specific type or keeping them in integer format is feasible in most cases. We aim to balance code readability and consistency while maintaining type stability. Here are some examples to guide our developers. +```julia +# The first example - `SVector`, keep code consistency within `SVector` +SVector(0, 0, 0) +SVector(zero(RealT), zero(RealT), zero(RealT)) +Svector(one(RealT), one(RealT), one(RealT)) + +# The second example - inner functions, keep them type-stable as well +function foo(..., input, ...) +RealT = eltype(input) # see **notes** below +# ... +c1 = c2 > 0.5f0 ? one(RealT) : convert(RealT, 0.1) # make type-stable +# ... +end + +# The third example - some operations (e.g., `/`, `sqrt`, `inv`), convert them definitely +c1 = convert(RealT, 4) # suppose we get RealT before +c2 = 1 / c1 +c3 = sqrt(c1) +c4 = inv(c1) +``` +In general, in the case of integer numbers, our developers should apply a case-by-case strategy to maintain type stability. + +### Notes +1. If the function gets a local pointwise vector of the solution variables `u` such as `flux(u, equations)`, use `u` to determine the real type `eltype(u)`. +2. If `u` is not passed as an argument but a vector of coordinates `x` such as `initial_condition(x, t, equations)`, use `eltype(x)` instead. +3. Choose an appropriate argument to determine the real type otherwise. \ No newline at end of file From e73e66b2702a34951452092ed4f233a44dfa5d72 Mon Sep 17 00:00:00 2001 From: huiyuxie Date: Tue, 14 May 2024 13:34:41 -0700 Subject: [PATCH 8/9] revise again --- docs/src/conventions.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 5094f44317..54b1f34012 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -108,15 +108,19 @@ based on the following rules. with external C libraries such as HDF5, MPI, or visualization. ## Numeric types and type stability + In Trixi.jl, we use generic programming to support custom data types to store the numerical simulation data, including standard floating point types and automatic differentiation types. Specifically, `Float32` and `Float64` types are fully supported, including the ability to run Trixi.jl on hardware that only supports `Float32` types. We ensure the type stability of these numeric types throughout the development process. Below are some guidelines to apply in various scenarios. -- **Exact floating-point numbers**: Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. For example, +### Exact floating-point numbers + +Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. For example, ```julia # Assume we have `0.25`, `0.5`, `1/2` in function 0.25f0, 0.5f0, 0.5f0 # corresponding numbers +``` ### Non-exact floating-point numbers From 77e8d9c71dae8d9d27ad8d0d7828621d08a9e566 Mon Sep 17 00:00:00 2001 From: Huiyu Xie Date: Wed, 15 May 2024 07:19:50 -0700 Subject: [PATCH 9/9] apply suggestions from code review Co-authored-by: Michael Schlottke-Lakemper --- docs/src/conventions.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/src/conventions.md b/docs/src/conventions.md index 54b1f34012..dee860d675 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -116,11 +116,16 @@ Below are some guidelines to apply in various scenarios. ### Exact floating-point numbers -Some real numbers can be represented exactly in machine (e.g., `0.25`, `0.5`, `1/2`) and we prefer to use `Float32` type of them to achieve a concise way of possible type promotion. For example, +Some real numbers can be represented exactly as both `Float64` and `Float32` values (e.g., `0.25`, `0.5`, `1/2`). We prefer to use `Float32` type for these numbers to achieve a concise way of possible type promotion. For example, ```julia # Assume we have `0.25`, `0.5`, `1/2` in function 0.25f0, 0.5f0, 0.5f0 # corresponding numbers ``` +Generally, this equivalence is true for integer multiples of powers of two. That is, numbers that can be written as ``m 2^n``, where ``m, n \in \mathbb{Z}``, and where ``m`` and ``n`` are such that the result is representable as a [single precision floating point](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) value. If a decimal value `v` is exactly representable in `Float32`, the expression +```julia +Float32(v) == v +``` +will evaluate to `true`. ### Non-exact floating-point numbers