Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ on:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- main
- '[0-9].[0-9]RC'
- '[0-9].[0-9][0-9]RC'
- '[0-9]*.[0-9]*RC'
- '[0-9]*.[0-9]*.[0-9]*RC'

concurrency:
group: ${ {github.event_name }}-${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -95,29 +95,35 @@ jobs:
ctest --output-log ctest.log
- name: Package Artifact CMake
id: pack-cmake
if: ${{ steps.makecheck-cmake.outcome == 'failure' }}
run: |
cd cmake_build
tar -cJf ctest.tar.xz Testing/Temporary ctest.log
- name: Package Artifact CMake-Asan
id: pack-cmake-asan
if: ${{ steps.makecheck-cmake-asan.outcome == 'failure' }}
run: |
cd cmake_asan_build
tar -cJf ctest.tar.xz Testing/Temporary ctest.log
- name: Upload artifacts
if: ${{ steps.makecheck-cmake.outcome == 'failure' || steps.makecheck-cmake-asan.outcome == 'failure' }}
- name: Upload Artifact CMake
if: ${{ steps.makecheck-cmake.outcome == 'failure' }}
uses: actions/upload-artifact@v4
with:
name: silo-cmake-artifacts
path: |
cmake_build/ctest.tar.xz
if-no-files-found: ignore
retention-days: 14

- name: Upload Artifact CMake-Asan
if: ${{ steps.makecheck-cmake-asan.outcome == 'failure' }}
uses: actions/upload-artifact@v4
with:
name: silo-test-artifacts
name: silo-cmake-asan-artifacts
path: |
build/tests/testsuite.dir.tar.xz
cmake_build/tests/ctest.tar.xz
cmake_asan_build/tests/ctest.tar.xz
if-no-files-found: error
cmake_asan_build/ctest.tar.xz
if-no-files-found: ignore
retention-days: 14

# Re-signal the failure so the job ends red.
Expand Down
49 changes: 49 additions & 0 deletions docs/alloc.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,52 @@ This section describes methods to allocate and initialize many of Silo's objects

{{ EndFunc }}

## `DBFreeNamescheme()`

* **Summary:** Free all memory associated with a namescheme object

* **C Signature:**

```
DBFreeNamescheme(DBnamescheme *ns)
```

* **Arguments:**

Arg name | Description
:---|:---
`ns` | Pointer to a namescheme object to be freed

* **Description:**

`DBFreeNamescheme()` will free all memory associated with a `DBnamescheme` object.
What happens with externally-referenced arrays used in the namescheme?
The answer is that it depends on whether `DBMakeNamescheme()` allocated them or the caller did.
In the 3 forms of use of `DBMakeNamescheme()`, the two forms involving external array references differ in whether the caller allocated the memory for the arrays or `DBMakeNamescheme()` did.
In the second form, where the caller allocated the externally referenced arrays, `DBFreeNamescheme()` will **NOT** free them.
In the third form, where `DBMakeNamescheme()` finds in the provided file, allocates and reads the externally referenced arrays, `DBFreeNamescheme()` will also free them.

{{ EndFunc }}

## Freeing library internal caches

* **Summary:** Freeing memory associated with various internal caches used in the library

* **C Signature:**

```
DBSPrintf(0); /* free circular cache entries used by this method */
DBGetName(0,-1); /* free circular cache entries used by this method */
```

* **Arguments:**

Varies by function.

* **Description:**

In various places where Silo returns a small string, the library uses a small, internal, circular cache which is never normally freed.
This can lead to tiny leaks reported by various memory checker tools.
These can be addressed by using calls into the associated routines with arguments specifically designed, see above, to trigger freeing of those caches.

{{ EndFunc }}
58 changes: 33 additions & 25 deletions docs/subsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,40 +475,42 @@ For this reason, where a specific application of MRG trees is desired (to repres

The first character of `ns_str` is treated as the *delimiter character definition*.
Wherever this delimiter character appears (except as the first character), this will indicate the end of one substring within `ns_str` and the beginning of a next substring.
The delimiter character cannot be any of the characters used in the expression language (see below).
The delimiter character divides `ns_str` into substrings.
For constant valued nameschemes, there is no need to divide the namescheme string into substrings.
So, the delimiter character is not necessary and if found to be present will be ignored.
It is best, though not a strict requirement, if the delimiter character is not any of the characters used in the expression language (see below).
It is best, though not a strict requirement, if the delimiter character is not any of the characters that form a valid Silo object name.
The second of these is indeed a *requirement* if the namescheme is constant valued (e.g. contains no other substrings).

The first substring of `ns_strs` (that is the characters from position 1 to the first delimiter character after its definition at index 0) will contain the complete printf-style format string the namescheme will generate.
The remaining substrings will contain simple expressions, one for each conversion specifier found in the format substring, which when evaluated will be used as the corresponding argument in an sprintf call to generate the actual name, when and if needed, on demand.

The expression language for building up the arguments to be used along with the printf-style format string is pretty simple.

It supports the '+', '-', '*', '/', '%' (modulo), '|', '&', '^' integer operators and a variant of the question-mark-colon operator, '? : :' which requires an extra, terminating colon.
It supports the `+`, `-`, `*`, `/`, `%` (modulo), `|`, `&`, `^` integer operators and a variant of the question-mark-colon operator, `? : :` which requires an extra, terminating colon.

It supports grouping via '(' and ')' characters.
It supports grouping via `(` and `)` characters.

It supports grouping of characters into arbitrary strings via the single quote character (').
It supports grouping of characters into arbitrary strings via the single quote character (`'`).
Any characters appearing between enclosing single quotes are treated as a literal string suitable for an argument to be associated with a %s-type conversion specifier in the format string.

It supports references to external, integer valued arrays introduced via a '#' character appearing before an array's name and external, string valued arrays introduced via a '$' character appearing before an array's name.
It supports references to external, integer valued arrays introduced via a `#` character appearing before an array's name and external, string valued arrays introduced via a `$` character appearing before an array's name.

Finally, the special operator 'n' appearing in an expression represents a *natural number* within the sequence of names (zero-origin index).
Finally, the special operator `n` appearing in an expression represents a *natural number* within the sequence of names (zero-origin index).

Except for singly quoted strings which evaluate to a literal string suitable for output via a %s type conversion specifier, and $-type external array references which evaluate to an external string, all other expressions are treated as evaluating to integer values suitable for any of the integer conversion specifiers (%[ouxXdi]) which may be used in the format substring.
Except for singly quoted strings which evaluate to a literal string suitable for output via a `%s` type conversion specifier, and `$`-type external array references which evaluate to an external string, all other expressions are treated as evaluating to integer values suitable for any of the integer conversion specifiers (`%[ouxXdi]`) which may be used in the format substring.

Here are some examples...

`/mesh1`
: There is no delimiter character because the namescheme is constant valued, `/mesh1`.
`"@/mesh1"`
: The delimiter character is `@`.
The namescheme is constant valued, returning `/mesh1` for all `n`.
That is, there are no `%` characters appearing in the the first (only) substring of `ns_str`.
This could be the *block* path part (see `DBOPT_MB_BLOCK_NS` option of [`DBPutMultimesh()`](./parallel.md#dbputmultimesh)) of a namescheme where each block is at the same path but in a *different* Silo file.

`"|slide_%s|(n%2)?'leader':'follower':"`
: The delimiter character is `|`.
The format substring is `slide_%s`.
The expression substring for the argument to the first (and only in this case) conversion specifier (`%s`) is `(n%2)?'leader':'follower':`
`"~slide_%d_%s~n/2~(n%2)?'leader':'follower':"`
: The delimiter character is `~`.
The format substring is `slide_%d_%s`.
The expression substring for the argument to the first conversion specifier (`%d`) is `n/2`.
The expression substring for the argument to the second conversion specifier (`%s`) is `(n%2)?'leader':'follower':`
When this expression is evaluated for a given region, the region's natural number will be inserted for `n`.
The modulo operation with `2` will be applied.
If that result is non-zero, the `?::` expression will evaluate to `'leader'`.
Expand All @@ -517,13 +519,13 @@ For this reason, where a specific application of MRG trees is desired (to repres
This naming scheme might be useful for an array of regions representing, alternately, the two halves of a contact surface.
Note also for the `?::` operator, the caller can assume that only the sub-expression corresponding to the whichever half of the operator is satisfied is actually evaluated.

`"Hblock_%02dx%02dHn/16Hn%16"`
`"Hblock_%02Xx%02XHn/16Hn%16"`
: The delimiter character is `H`.
The format substring is `block_%02dx%02d`.
The expression substring for the argument to the first conversion specifier (`%02d`) is `n/256`.
The expression substring for the argument to the second conversion specifier (also `%02d`) is `n%16`.
The format substring is `block_%02Xx%02X`.
The expression substring for the argument to the first conversion specifier (`%02X`) is `n/256`.
The expression substring for the argument to the second conversion specifier (also `%02X`) is `n%16`.
When this expression is evaluated, the region's natural number will be inserted for `n` and the div and mod operators will be evaluated.
This naming scheme might be useful for a region array of 256 regions to be named as a 2D array of regions with names like "block_09x11".
This naming scheme might be useful for a region array of 256 regions to be named as a 2D array of regions with names like "block_0Ax1C".

`"@domain_%03d@n"`
: The delimiter character is `@`.
Expand All @@ -536,19 +538,25 @@ For this reason, where a specific application of MRG trees is desired (to repres
: This is just like the case above except that region names begin with "domain_001" instead of "domain_000".
This might be useful to deal with different indexing origins; Fortran vs. C.

`"|foo_%03dx%03d|#P[n]|#U[n%4]"`
: The delimiter character is `|`.
`"!foo_%03dx%03d!#P[n]!#U[n%4]"`
: The delimiter character is `!`.
The format substring is `foo_%03dx%03d`.
The expression substring for the first argument is an external array reference `#P[n]` where the index into the array is just the natural number, n.
The expression substring for the second argument is another external array reference, `#U[n%4]` where the index is an expression `n%4` on the natural number n.

If the caller is handling externally referenced arrays explicitly, because `P` is the first externally referenced array in the format string, a pointer to `P` must be the first to appear in the `...` list of additional args to `DBMakeNamescheme`.
Similarly, because `U` appears as the second externally referenced array in the format string, a pointer to `U` must appear second in the `...` as in `DBMakeNamescheme("|foo_%03dx%03d|#P[n]|#U[n%4]", P, U)`
Similarly, because `U` appears as the second externally referenced array in the format string, a pointer to `U` must appear second in the `...` as in `DBMakeNamescheme("!foo_%03dx%03d!#P[n]!#U[n%4]", P, U)`

Alternatively, if the caller wants the Silo library to find `P` and `U` in a Silo file, read the arrays from the file and bind them into the namescheme automatically, then `P` and `U` must be simple arrays in the current working directory of the file that is passed in as the 3-tuple `"(int) 0, (DBfile *) dbfile, 0"` in the `...` argument to `DBMakeNamescheme` as in `DBMakeNamescheme("|foo_%03dx%03d|#P[n]|#U[n%4]", 0, dbfile, 0)`.
Alternatively, if the caller wants the Silo library to find `P` and `U` in a Silo file, read the arrays from the file and bind them into the namescheme automatically, then `P` and `U` must be simple arrays in the current working directory of the file that is passed in as the 3-tuple `"(int) 0, (DBfile *) dbfile, 0"` in the `...` argument to `DBMakeNamescheme` as in `DBMakeNamescheme("!foo_%03dx%03d!#P[n]!#U[n%4]", 0, dbfile, 0)`.

Use `DBFreeNamescheme()` to free up the space associated with a namescheme.
Also note that there are numerous examples of nameschemes in the [nameschemes.c](https://raw.githubusercontent.com/LLNL/Silo/refs/heads/main/tests/nameschemes.c) test on GitHub and in the Silo source release tarball.
Also note that there are numerous examples of nameschemes in the [nameschemes.c](https://raw.githubusercontent.com/LLNL/Silo/refs/heads/main/tests/namescheme.c) test on GitHub and in the Silo source release tarball.
For convenience, some of those are shown below

```{literalinclude} ../tests/namescheme.c
:start-at: "/* Test nameschemes from docs */"
:end-at: "/* Test DBnamescheme construction from arrays in a Silo file */"
```

{{ EndFunc }}

Expand Down
4 changes: 2 additions & 2 deletions src/silo/silo.c
Original file line number Diff line number Diff line change
Expand Up @@ -11377,7 +11377,7 @@ _DBdarrminmax(double arr[], int len, double *arr_min, double *arr_max)
return 0;
}

INTERNAL int
static int
include_point(int ptidx, int ndims, int const *dims,
int const *minidx, int const *maxidx)
{
Expand Down Expand Up @@ -11410,7 +11410,7 @@ include_point(int ptidx, int ndims, int const *dims,
* Mark C. Miller, Mon May 20 12:25:25 PDT 2024
* Adjusted to avoid strict pointer aliasing optimization issues.
*--------------------------------------------------------------------*/
INTERNAL int
static int
_CalcExtents(DBVCP2_t coord_arrays, int datatype, int ndims, int npts,
void *min_extents, void *max_extents,
int const *dims, int const *minidx, int const *maxidx)
Expand Down
25 changes: 18 additions & 7 deletions src/silo/silo_ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,22 +424,33 @@ DBMakeNamescheme(char const *fmt, ...)
*/
if (rv->ncspecs == 0)
{
int rm_unnecessary_delim = 0;
free(rv->fmt);

if (n > 2 && fmt[0] == fmt[n-1])
rm_unnecessary_delim = !db_VariableNameValid(fmt);
/* If whole string is valid, take all of it. */
if (db_VariableNameValid(fmt))
{
rv->fmt = STRNDUP(&fmt[0],n);
rv->fmtlen = n;
return rv;
}

free(rv->fmt);
/* If whole string but first char is valid, take all but first char */
if (db_VariableNameValid(&fmt[1]))
{
rv->fmt = STRNDUP(&fmt[1],n-1);
rv->fmtlen = n-1;
return rv;
}

if (rm_unnecessary_delim)
if (fmt[0] == fmt[n-1])
{
rv->fmt = STRNDUP(&fmt[1],n-2);
rv->fmtlen = n-2;
}
else
{
rv->fmt = STRNDUP(&fmt[0],n);
rv->fmtlen = n;
rv->fmt = STRNDUP(&fmt[0],n-1);
rv->fmtlen = n-1;
}

return rv;
Expand Down
45 changes: 28 additions & 17 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@
# cat Testing/Temporary/MemoryChecker.*
#

set(CMAKE_BUILD_RPATH "@loader_path/../../lib")

###-------------------------------------------------------------------------------------
# Set the output dir for test executables
###-------------------------------------------------------------------------------------
Expand Down Expand Up @@ -230,18 +232,16 @@ endif()

set(SILO_TESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})

if(SILO_ENABLE_HDF5 AND HDF5_FOUND)
set(HDF5_ONLY_SOURCES
compression.c
grab.c
largefile.c
memfile_simple.c
mk_nasf_h5.c
partial_io.c
readstuff.c
testhdf5.c
)
endif()
set(HDF5_ONLY_SOURCES
compression.c
grab.c
largefile.c
memfile_simple.c
mk_nasf_h5.c
partial_io.c
readstuff.c
testhdf5.c
)

set(PDB_ONLY_SOURCES
mk_nasf_pdb.c
Expand Down Expand Up @@ -344,6 +344,9 @@ target_include_directories(rocket_silo PRIVATE
${silo_build_include_dir}
${Silo_SOURCE_DIR}/src/silo
${SILO_TESTS_SOURCE_DIR})
if(APPLE)
target_link_options(rocket_silo PRIVATE "-Wl,-undefined,dynamic_lookup")
endif()
set_target_properties(rocket_silo PROPERTIES PREFIX "")

add_library(testlib_obj OBJECT testlib.c)
Expand All @@ -365,6 +368,11 @@ foreach(src IN LISTS C_TEST_SOURCES CXX_TEST_SOURCES F_TEST_SOURCES)
continue()
endif()

# Skip any HDF5 specific executables if we're not building with HDF5
if(srcFile IN_LIST HDF5_ONLY_SOURCES AND NOT (SILO_ENABLE_HDF5 AND HDF5_FOUND))
continue()
endif()

add_executable(${base} ${src})
add_dependencies(${base} silo)
target_compile_definitions(${base} PRIVATE PDB_LITE)
Expand Down Expand Up @@ -417,11 +425,14 @@ foreach(src IN LISTS C_TEST_SOURCES CXX_TEST_SOURCES F_TEST_SOURCES)
# Add default tests for HDF5 or PDB drivers or BOTH
#
if(srcFile IN_LIST HDF5_ONLY_SOURCES)
add_test(NAME ${base}-hdf5 COMMAND $<TARGET_FILE:${base}> DB_HDF5)
set_tests_properties(${base}-hdf5 PROPERTIES LABELS "hdf5")
set_tests_properties(${base}-hdf5 PROPERTIES SKIP_RETURN_CODE ${_silo_test_skip_retval})
if(src IN_LIST F_TEST_SOURCES)
set_tests_properties(${base}-hdf5 PROPERTIES LABELS "fortran")
if(SILO_ENABLE_HDF5 AND HDF5_FOUND)
message(FATAL "adding hdf5 test \"${srcFile}\"")
add_test(NAME ${base}-hdf5 COMMAND $<TARGET_FILE:${base}> DB_HDF5)
set_tests_properties(${base}-hdf5 PROPERTIES LABELS "hdf5")
set_tests_properties(${base}-hdf5 PROPERTIES SKIP_RETURN_CODE ${_silo_test_skip_retval})
if(src IN_LIST F_TEST_SOURCES)
set_tests_properties(${base}-hdf5 PROPERTIES LABELS "fortran")
endif()
endif()
elseif(srcFile IN_LIST PDB_ONLY_SOURCES)
add_test(NAME ${base} COMMAND $<TARGET_FILE:${base}>)
Expand Down
Loading