Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variable-Based iteration layout #855

Merged
merged 11 commits into from
Apr 23, 2021
8 changes: 7 additions & 1 deletion include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "openPMD/auxiliary/Option.hpp"
#include "openPMD/backend/Writable.hpp"
#include "openPMD/config.hpp"
#include "openPMD/IterationEncoding.hpp"

#if openPMD_HAVE_ADIOS2
# include <adios2.h>
Expand Down Expand Up @@ -224,6 +225,11 @@ class ADIOS2IOHandlerImpl

private:
adios2::ADIOS m_ADIOS;
/*
* If the iteration encoding is variableBased, we default to using the
* 2021_02_09 schema since it allows mutable attributes.
*/
IterationEncoding m_iterationEncoding = IterationEncoding::groupBased;
/**
* The ADIOS2 engine type, to be passed to adios2::IO::SetEngine
*/
Expand Down Expand Up @@ -1386,7 +1392,7 @@ friend class ADIOS2IOHandlerImpl;
}
catch( ... )
{
std::cerr << "[~ADIOS2IOHandler] An error occurred." << std::endl;
std::cerr << "[~ADIOS2IOHandler] An error occurred." << std::endl;
}
}

Expand Down
1 change: 1 addition & 0 deletions include/openPMD/IO/AbstractIOHandlerImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class AbstractIOHandlerImpl
*
* The operation should overwrite existing file positions, even when the Writable was already marked written.
* The path parameters.path may contain multiple levels (e.g. first/second/third/). This path should be relative (i.e. it should not start with a slash "/").
* The number of levels may be zero, i.e. parameters.path may be an empty string.
* The Writables file position should correspond to the complete opened path (i.e. first/second/third/ should be assigned to the Writables file position).
* The Writable should be marked written when the operation completes successfully.
*/
Expand Down
14 changes: 12 additions & 2 deletions include/openPMD/IO/IOTask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "openPMD/backend/Attribute.hpp"
#include "openPMD/ChunkInfo.hpp"
#include "openPMD/Dataset.hpp"
#include "openPMD/IterationEncoding.hpp"

#include <memory>
#include <map>
Expand Down Expand Up @@ -108,7 +109,8 @@ template<>
struct OPENPMDAPI_EXPORT Parameter< Operation::CREATE_FILE > : public AbstractParameter
{
Parameter() = default;
Parameter(Parameter const & p) : AbstractParameter(), name(p.name) {}
Parameter(Parameter const & p) :
AbstractParameter(), name(p.name), encoding(p.encoding) {}

std::unique_ptr< AbstractParameter >
clone() const override
Expand All @@ -118,13 +120,15 @@ struct OPENPMDAPI_EXPORT Parameter< Operation::CREATE_FILE > : public AbstractPa
}

std::string name = "";
IterationEncoding encoding = IterationEncoding::groupBased;
};

template<>
struct OPENPMDAPI_EXPORT Parameter< Operation::OPEN_FILE > : public AbstractParameter
{
Parameter() = default;
Parameter(Parameter const & p) : AbstractParameter(), name(p.name) {}
Parameter(Parameter const & p) :
AbstractParameter(), name(p.name), encoding(p.encoding) {}

std::unique_ptr< AbstractParameter >
clone() const override
Expand All @@ -134,6 +138,12 @@ struct OPENPMDAPI_EXPORT Parameter< Operation::OPEN_FILE > : public AbstractPara
}

std::string name = "";
/*
* The backends might need to ensure availability of certain features
* for some iteration encodings, e.g. availability of ADIOS steps for
* variableBased encoding.
*/
IterationEncoding encoding = IterationEncoding::groupBased;
};

template<>
Expand Down
26 changes: 24 additions & 2 deletions include/openPMD/Iteration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,31 @@ class Iteration : public LegacyAttributable

struct DeferredParseAccess
{
std::string index;
/**
* The group path within /data containing this iteration.
* Example: "1" for iteration 1, "" in variable-based iteration
* encoding.
*/
std::string path;
/**
* The iteration index as accessed by the user in series.iterations[i]
*/
uint64_t iteration = 0;
/**
* If this iteration is part of a Series with file-based layout.
* (Group- and variable-based parsing shares the same code logic.)
*/
bool fileBased = false;
/**
* If fileBased == true, the file name (without file path) of the file
* containing this iteration.
*/
std::string filename;
};

void flushFileBased(std::string const&, uint64_t);
void flushGroupBased(uint64_t);
void flushVariableBased(uint64_t);
void flush();
void deferParseAccess( DeferredParseAccess );
/*
Expand All @@ -177,11 +195,15 @@ class Iteration : public LegacyAttributable
* allow for those different control flows.
* Finally, read_impl() is called which contains the common parsing
* logic for an iteration.
*
* reread() reads again an Iteration that has been previously read.
* Calling it on an Iteration not yet parsed is an error.
*
*/
void read();
void reread( std::string const & path );
void readFileBased( std::string filePath, std::string const & groupPath );
void readGroupBased( std::string const & groupPath );
void readGorVBased( std::string const & groupPath );
void read_impl( std::string const & groupPath );

/**
Expand Down
2 changes: 1 addition & 1 deletion include/openPMD/IterationEncoding.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace openPMD
*/
enum class IterationEncoding
{
fileBased, groupBased
fileBased, groupBased, variableBased
};

std::ostream&
Expand Down
7 changes: 3 additions & 4 deletions include/openPMD/RecordComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,18 +286,17 @@ class RecordComponent : public BaseRecordComponent
bool dirtyRecursive() const;

protected:
/**
* Make sure to parse a RecordComponent only once.
*/
std::shared_ptr< bool > hasBeenRead = std::make_shared< bool >( false );

/**
* The same std::string that the parent class would pass as parameter to
* RecordComponent::flush().
* This is stored only upon RecordComponent::flush() if
* AbstractIOHandler::flushLevel is set to FlushLevel::SkeletonOnly
* (for use by the Span<T>-based overload of RecordComponent::storeChunk()).
* @todo Merge functionality with ownKeyInParent?
*/
std::shared_ptr< std::string > m_name = std::make_shared< std::string >();

}; // RecordComponent
} // namespace openPMD

Expand Down
19 changes: 13 additions & 6 deletions include/openPMD/Series.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ class SeriesImpl : public AttributableImpl
*/
IterationEncoding iterationEncoding() const;
/** Set the <A HREF="https://github.com/openPMD/openPMD-standard/blob/latest/STANDARD.md#iterations-and-time-series">encoding style</A> for multiple iterations in this series.
* A preview on the <A HREF="https://github.com/openPMD/openPMD-standard/pull/250">openPMD 2.0 variable-based iteration encoding</A> can be activated with this call.
* Making full use of the variable-based iteration encoding requires (1) explicit support by the backend (available only in ADIOS2) and (2) use of the openPMD streaming API.
* In other backends and without the streaming API, only one iteration/snapshot may be written in the variable-based encoding, making this encoding a good choice for single-snapshot data dumps.
*
* @param iterationEncoding Desired <A HREF="https://github.com/openPMD/openPMD-standard/blob/latest/STANDARD.md#iterations-and-time-series">encoding style</A> for multiple iterations in this series.
* @return Reference to modified series.
Expand Down Expand Up @@ -342,13 +345,20 @@ class SeriesImpl : public AttributableImpl

std::unique_ptr< ParsedInput > parseInput(std::string);
void init(std::shared_ptr< AbstractIOHandler >, std::unique_ptr< ParsedInput >);
void initDefaults();
void initDefaults( IterationEncoding );
std::future< void > flush_impl(
iterations_iterator begin,
iterations_iterator end,
FlushLevel );
void flushFileBased( iterations_iterator begin, iterations_iterator end );
void flushGroupBased( iterations_iterator begin, iterations_iterator end );
/*
* Group-based and variable-based iteration layouts share a lot of logic
* (realistically, the variable-based iteration layout only throws out
* one layer in the hierarchy).
* As a convention, methods that deal with both layouts are called
* .*GorVBased, short for .*GroupOrVariableBased
*/
void flushGorVBased( iterations_iterator begin, iterations_iterator end );
void flushMeshesPath();
void flushParticlesPath();
void readFileBased( );
Expand All @@ -357,11 +367,8 @@ class SeriesImpl : public AttributableImpl
* Note on re-parsing of a Series:
* If init == false, the parsing process will seek for new
* Iterations/Records/Record Components etc.
* Re-parsing of objects that have already been parsed is not implemented
* as of yet. Such a facility will be required upon implementing things such
* as resizable datasets.
*/
void readGroupBased( bool init = true );
void readGorVBased( bool init = true );
void readBase();
std::string iterationFilename( uint64_t i );
void openIteration( uint64_t index, Iteration iteration );
Expand Down
19 changes: 18 additions & 1 deletion include/openPMD/backend/Attributable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,24 @@ class AttributableImpl
std::vector< std::string > myPath() const;

void flushAttributes();
void readAttributes();
enum ReadMode {
/**
* Don't read an attribute from the backend if it has been previously
* read.
*/
IgnoreExisting,
/**
* Read all the attributes that the backend has to offer and override
* if it has been read previously.
*/
OverrideExisting,
/**
* Remove all attributes that have been read previously and read
* everything that the backend currently has to offer.
*/
FullyReread
};
void readAttributes( ReadMode );

/** Retrieve the value of a floating point Attribute of user-defined precision with ensured type-safety.
*
Expand Down
76 changes: 73 additions & 3 deletions include/openPMD/backend/Container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
#include "openPMD/backend/Attributable.hpp"

#include <initializer_list>
#include <type_traits>
#include <stdexcept>
#include <map>
#include <set>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>

// expose private and protected members for invasive testing
#ifndef OPENPMD_protected
Expand Down Expand Up @@ -106,14 +108,16 @@ class Container : public LegacyAttributable
static_assert(
std::is_base_of< AttributableImpl, T >::value,
"Type of container element must be derived from Writable");
using InternalContainer = T_container;

friend class Iteration;
friend class ParticleSpecies;
friend class internal::SeriesData;
friend class SeriesImpl;
friend class Series;

protected:
using InternalContainer = T_container;

public:
using key_type = typename InternalContainer::key_type;
using mapped_type = typename InternalContainer::mapped_type;
Expand Down Expand Up @@ -321,6 +325,72 @@ class Container : public LegacyAttributable
}

std::shared_ptr< InternalContainer > m_container;

/**
* This class wraps a Container and forwards operator[]() and at() to it.
* It remembers the keys used for accessing. Upon going out of scope, all
* keys not yet accessed are removed from the Container.
* Note that the container is stored by non-owning reference, thus
* requiring that the original Container stay in scope while using this
* class.
*/
class EraseStaleEntries
{
std::set< key_type > m_accessedKeys;
/*
* Note: Putting a copy here leads to weird bugs due to destructors
* being called too eagerly upon destruction.
* Should be avoidable by extending the frontend redesign to the
* Container class template
* (https://github.com/openPMD/openPMD-api/pull/886)
*/
Container & m_originalContainer;

public:
explicit EraseStaleEntries( Container & container_in )
: m_originalContainer( container_in )
{
}

template< typename K >
mapped_type & operator[]( K && k )
{
m_accessedKeys.insert( k ); // copy
return m_originalContainer[ std::forward< K >( k ) ];
}

template< typename K >
mapped_type & at( K && k )
{
m_accessedKeys.insert( k ); // copy
return m_originalContainer.at( std::forward< K >( k ) );
}

~EraseStaleEntries()
{
auto & map = *m_originalContainer.m_container;
using iterator_t = typename InternalContainer::const_iterator;
std::vector< iterator_t > deleteMe;
deleteMe.reserve( map.size() - m_accessedKeys.size() );
for( iterator_t it = map.begin(); it != map.end(); ++it )
{
auto lookup = m_accessedKeys.find( it->first );
if( lookup == m_accessedKeys.end() )
{
deleteMe.push_back( it );
}
}
for( auto & it : deleteMe )
{
map.erase( it );
}
}
};

EraseStaleEntries eraseStaleEntries()
{
return EraseStaleEntries( *this );
}
};

} // openPMD
1 change: 0 additions & 1 deletion include/openPMD/backend/PatchRecordComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ class PatchRecordComponent : public BaseRecordComponent
void read();

std::shared_ptr< std::queue< IOTask > > m_chunks;
std::shared_ptr< bool > hasBeenRead = std::make_shared< bool >( false );

/**
* @brief Check recursively whether this RecordComponent is dirty.
Expand Down
7 changes: 4 additions & 3 deletions src/IO/ADIOS/ADIOS1IOHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ ADIOS1IOHandlerImpl::flush()
case O::CREATE_PATH:
createPath(i.writable, deref_dynamic_cast< Parameter< O::CREATE_PATH > >(i.parameter.get()));
break;
case O::OPEN_PATH:
openPath(i.writable, deref_dynamic_cast< Parameter< O::OPEN_PATH > >(i.parameter.get()));
break;
case O::CREATE_DATASET:
createDataset(i.writable, deref_dynamic_cast< Parameter< O::CREATE_DATASET > >(i.parameter.get()));
break;
Expand Down Expand Up @@ -129,9 +132,6 @@ ADIOS1IOHandlerImpl::flush()
case O::EXTEND_DATASET:
extendDataset(i.writable, deref_dynamic_cast< Parameter< O::EXTEND_DATASET > >(i.parameter.get()));
break;
case O::OPEN_PATH:
openPath(i.writable, deref_dynamic_cast< Parameter< O::OPEN_PATH > >(i.parameter.get()));
break;
case O::CLOSE_PATH:
closePath(i.writable, deref_dynamic_cast< Parameter< O::CLOSE_PATH > >(i.parameter.get()));
break;
Expand Down Expand Up @@ -243,6 +243,7 @@ ADIOS1IOHandler::enqueue(IOTask const& i)
{
case Operation::CREATE_FILE:
case Operation::CREATE_PATH:
case Operation::OPEN_PATH:
case Operation::CREATE_DATASET:
case Operation::OPEN_FILE:
case Operation::WRITE_ATT:
Expand Down
Loading