Skip to content

Commit 9acc476

Browse files
committed
Documentation and testing
1 parent ca73852 commit 9acc476

2 files changed

Lines changed: 215 additions & 1 deletion

File tree

docs/source/usage/workflow.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
.. _workflow:
22

3+
Access modes
4+
============
5+
6+
The openPMD-api distinguishes between a number of different access modes:
7+
8+
* **Create mode**: Used for creating a new Series from scratch.
9+
Any file possibly existing in the specified location will be overwritten.
10+
* **Read-only mode**: Used for reading from an existing Series.
11+
No modifications will be made.
12+
* **Read/Write mode**: Creates a new Series if not existing, otherwise opens an existing Series for reading and writing.
13+
New datasets and iterations will be inserted as needed.
14+
Not fully supported by all backends:
15+
* ADIOS1: Automatically coerced to ... mode @todo
16+
* ADIOS2: Automatically coerced to *Create* mode if the file does not exist yet and to *Read-only* mode if it exists.
17+
Since this happens on a per-file level, this mode allows to read from existing iterations and write to new iterations at the same time in file-based iteration encoding.
18+
* **Append mode**: Restricted mode for appending new iterations to an existing Series that is supported by all backends.
19+
The API is equivalent to that of the *Create* mode, meaning that no reading is supported whatsoever.
20+
If the Series does not exist yet, this behaves equivalently to the *Create* mode.
21+
Existing iterations will not be deleted, newly-written iterations will be inserted.
22+
23+
**Warning:** If writing an iteration that already exists, the behavior is implementation-defined and depends on the chosen backend and iteration encoding:
24+
25+
* The new iteration might fully replace the old one.
26+
* The new iteration might be merged into the old one.
27+
* (To be removed in a future update) The old and new iteration might coexist in the resulting dataset.
28+
29+
We suggest to fully define iterations when using Append mode (i.e. as if using Create mode) to avoid implementation-specific behavior.
30+
Appending to an openPMD Series is only supported on a per-iteration level.
31+
32+
**Warning:** There is no reading involved in using Append mode.
33+
It is a user's responsibility to ensure that the appended dataset and the appended-to dataset are compatible with each other.
34+
The results of using incompatible backend configurations are undefined.
35+
336
Workflow
437
========
538

test/SerialIOTest.cpp

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,8 +1629,24 @@ void fileBased_write_test(const std::string & backend)
16291629
REQUIRE(o.iterations[5].time< double >() == 5.0);
16301630
}
16311631

1632-
// extend existing series with new step and auto-detection of iteration padding
1632+
if( backend == "bp" )
16331633
{
1634+
// Append + filebased iteration encoding works for all backends
1635+
Series o = Series("../samples/subdir/serial_fileBased_write%T." + backend, Access::APPEND);
1636+
// Append mode does not support reading anything that already exists
1637+
REQUIRE(o.iterations.size() == 0);
1638+
// write something to trigger opening of the file
1639+
o.iterations[ 6 ].particles[ "e" ][ "position" ][ "x" ].resetDataset(
1640+
{ Datatype::DOUBLE, { 10 } } );
1641+
o.iterations[ 6 ]
1642+
.particles[ "e" ][ "position" ][ "x" ]
1643+
.makeConstant< double >( 1.0 );
1644+
}
1645+
else
1646+
{
1647+
// @todo enable a workflow for ADIOS2 where only either reading from or
1648+
// writing to an iteration works
1649+
// extend existing series with new step and auto-detection of iteration padding
16341650
Series o = Series("../samples/subdir/serial_fileBased_write%T." + backend, Access::READ_WRITE);
16351651

16361652
REQUIRE(o.iterations.size() == 5);
@@ -1673,6 +1689,7 @@ void fileBased_write_test(const std::string & backend)
16731689
}
16741690

16751691
// write with auto-detection and in-consistent padding
1692+
if( backend != "bp" )
16761693
{
16771694
REQUIRE_THROWS_WITH(Series("../samples/subdir/serial_fileBased_write%T." + backend, Access::READ_WRITE),
16781695
Catch::Equals("Cannot write to a series with inconsistent iteration padding. Please specify '%0<N>T' or open as read-only."));
@@ -4617,3 +4634,167 @@ TEST_CASE( "late_setting_of_iterationencoding", "[serial]" )
46174634
REQUIRE( auxiliary::file_exists(
46184635
"../samples/change_name_and_encoding_10.json" ) );
46194636
}
4637+
4638+
void append_mode( std::string const & extension )
4639+
{
4640+
std::string jsonConfig = R"END(
4641+
{
4642+
"adios2":
4643+
{
4644+
"schema": 20210209,
4645+
"engine":
4646+
{
4647+
"usesteps" : true
4648+
}
4649+
}
4650+
})END";
4651+
auto writeSomeIterations = []( WriteIterations && writeIterations,
4652+
std::vector< uint64_t > indices )
4653+
{
4654+
for( auto index : indices )
4655+
{
4656+
auto it = writeIterations[ index ];
4657+
auto dataset = it.meshes[ "E" ][ "x" ];
4658+
dataset.resetDataset( { Datatype::INT, { 1 } } );
4659+
dataset.makeConstant< int >( 0 );
4660+
// test that it works without closing too
4661+
it.close();
4662+
}
4663+
};
4664+
{
4665+
Series write(
4666+
"../samples/append." + extension, Access::CREATE, jsonConfig );
4667+
writeSomeIterations(
4668+
write.writeIterations(), std::vector< uint64_t >{ 0, 1 } );
4669+
}
4670+
{
4671+
Series write(
4672+
"../samples/append." + extension, Access::APPEND, jsonConfig );
4673+
if( write.backend() == "ADIOS1" )
4674+
{
4675+
REQUIRE_THROWS_AS(
4676+
write.flush(), error::OperationUnsupportedInBackend );
4677+
// destructor will be noisy now
4678+
return;
4679+
}
4680+
4681+
writeSomeIterations(
4682+
write.writeIterations(), std::vector< uint64_t >{ 2, 3 } );
4683+
write.flush();
4684+
}
4685+
{
4686+
Series write(
4687+
"../samples/append." + extension, Access::APPEND, jsonConfig );
4688+
if( write.backend() == "ADIOS1" )
4689+
{
4690+
REQUIRE_THROWS_AS(
4691+
write.flush(), error::OperationUnsupportedInBackend );
4692+
// destructor will be noisy now
4693+
return;
4694+
}
4695+
4696+
writeSomeIterations(
4697+
write.writeIterations(), std::vector< uint64_t >{ 4, 3 } );
4698+
write.flush();
4699+
}
4700+
{
4701+
Series read( "../samples/append." + extension, Access::READ_ONLY );
4702+
REQUIRE( read.iterations.size() == 5 );
4703+
/*
4704+
* Roadmap: for now, reading this should work by ignoring the last
4705+
* duplicate iteration.
4706+
* After merging https://github.com/openPMD/openPMD-api/pull/949, we
4707+
* should see both instances when reading.
4708+
* Final goal: Read only the last instance.
4709+
*/
4710+
helper::listSeries( read );
4711+
}
4712+
}
4713+
4714+
TEST_CASE( "append_mode", "[serial]" )
4715+
{
4716+
for( auto const & t : testedFileExtensions() )
4717+
{
4718+
append_mode( t );
4719+
}
4720+
}
4721+
4722+
void append_mode_filebased( std::string const & extension )
4723+
{
4724+
std::string jsonConfig = R"END(
4725+
{
4726+
"adios2":
4727+
{
4728+
"schema": 20210209,
4729+
"engine":
4730+
{
4731+
"usesteps" : true
4732+
}
4733+
}
4734+
})END";
4735+
auto writeSomeIterations = []( WriteIterations && writeIterations,
4736+
std::vector< uint64_t > indices ) {
4737+
for( auto index : indices )
4738+
{
4739+
auto it = writeIterations[ index ];
4740+
auto dataset = it.meshes[ "E" ][ "x" ];
4741+
dataset.resetDataset( { Datatype::INT, { 1 } } );
4742+
dataset.makeConstant< int >( 0 );
4743+
// test that it works without closing too
4744+
it.close();
4745+
}
4746+
};
4747+
if( auxiliary::directory_exists( "../samples/append" ) )
4748+
{
4749+
auxiliary::remove_directory( "../samples/append" );
4750+
}
4751+
{
4752+
Series write(
4753+
"../samples/append/append_%T." + extension,
4754+
Access::CREATE,
4755+
jsonConfig );
4756+
writeSomeIterations(
4757+
write.writeIterations(), std::vector< uint64_t >{ 0, 1 } );
4758+
}
4759+
{
4760+
Series write(
4761+
"../samples/append/append_%T." + extension,
4762+
Access::APPEND,
4763+
jsonConfig );
4764+
writeSomeIterations(
4765+
write.writeIterations(), std::vector< uint64_t >{ 4, 5 } );
4766+
write.flush();
4767+
}
4768+
{
4769+
Series write(
4770+
"../samples/append/append_%T." + extension,
4771+
Access::APPEND,
4772+
jsonConfig );
4773+
writeSomeIterations(
4774+
write.writeIterations(), std::vector< uint64_t >{ 2, 3 } );
4775+
write.flush();
4776+
}
4777+
{
4778+
Series write(
4779+
"../samples/append/append_%T." + extension,
4780+
Access::APPEND,
4781+
jsonConfig );
4782+
// overwrite a previous iteration
4783+
writeSomeIterations(
4784+
write.writeIterations(), std::vector< uint64_t >{ 4, 123 } );
4785+
write.flush();
4786+
}
4787+
{
4788+
Series read(
4789+
"../samples/append/append_%T." + extension, Access::READ_ONLY );
4790+
REQUIRE( read.iterations.size() == 7 );
4791+
}
4792+
}
4793+
4794+
TEST_CASE( "append_mode_filebased", "[serial]" )
4795+
{
4796+
for( auto const & t : testedFileExtensions() )
4797+
{
4798+
append_mode_filebased( t );
4799+
}
4800+
}

0 commit comments

Comments
 (0)