Loading [MathJax]/extensions/tex2jax.js
LSST Applications g0d33ba9806+3d1aa5cd78,g0fba68d861+6c07529581,g1e78f5e6d3+aa4d1c21f1,g1ec0fe41b4+f536777771,g1fd858c14a+c6963eae98,g35bb328faa+fcb1d3bbc8,g4af146b050+58cb980876,g4d2262a081+bfae794ebc,g53246c7159+fcb1d3bbc8,g5a012ec0e7+b20b785ecb,g60b5630c4e+3d1aa5cd78,g67b6fd64d1+4086c0989b,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g7b71ed6315+fcb1d3bbc8,g87b7deb4dc+a8b896a16a,g8852436030+75f93ca278,g89139ef638+4086c0989b,g9125e01d80+fcb1d3bbc8,g94187f82dc+3d1aa5cd78,g989de1cb63+4086c0989b,g9f33ca652e+94dd9a85be,g9f7030ddb1+3f50642ad9,ga2b97cdc51+3d1aa5cd78,ga44b1db4f6+7d2d5e68ea,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+96cb2ddcf2,gb58c049af0+f03b321e39,gb89ab40317+4086c0989b,gcf25f946ba+75f93ca278,gd6cbbdb0b4+af3c3595f5,gd9a9a58781+fcb1d3bbc8,gde0f65d7ad+ed90e8109d,ge278dab8ac+d65b3c2b70,ge410e46f29+4086c0989b,gf67bdafdda+4086c0989b,gfe06eef73a+e2ab3e8e4f,v29.0.0.rc5
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Table-Based Persistence

Overview

The classes in afw::table::io provide an interface for persisting arbitrary objects to afw::table Catalog objects, and through that interface a way to save them to FITS binrary tables. The interface consists of four main classes:

  • Persistable is a base class for all objects that can be persisted to tables. It contains virtual member functions for writing the object to an OutputArchive, and concrete member functions that write individual objects directly to FITS files.
  • PersistableFactory is a base class for factory objects that can reconstruct Persistables. These are held in a singleton registry and associated with a particular string, so we can save this string and use it to locate the correct factory at load time.
  • OutputArchive is a concrete class that represents a collection of Persistables that have been converted to table form. It maps Persistable pointers to unique integer IDs, making it possible to persist multiple shared_ptrs without duplication.
  • InputArchive is a concrete class that represents a collection of Persistables that have been loaded from their table form. It maps unique IDs to Persistable pointers, allowing shared_ptr relationships to be reconstructed correctly.

There are also a few smaller, "helper" classes:

Implementing a Persistable Subclass

Persistable itself doesn't have any pure virtual member functions (instead, it has default implementations that throw exceptions), so it's generally safe to make an existing class inherit from Persistable even if you don't implement Persistence. If you do actually want to implement table-based persistence, there are a few steps to follow:

Here's a complete example for an abstract base class Base and derived class Derived, starting with the header file:

using namespace lsst::afw::table::io; // just for brevity; not actually recommended
class Base : public PersistableFacade<Base>, public Persistable {
// no new code needed here, unless the base class is also concrete
// (see Wcs for an example of that).
};
class Derived : public PersistableFacade<Derived>, public Base {
public:
// some data members, just to give us interesting stuff to save
double var1;
virtual bool isPersistable() const { return true; }
protected:
virtual std::string getPersistenceName() const { return "Derived"; }
virtual void write(OutputArchiveHandle & handle) const;
};
A CRTP facade class for subclasses of Persistable.
A base class for objects that can be persisted via afw::table::io Archive classes.
Definition Persistable.h:74

We'll save this in two catalogs - one will contain var1 and an archive ID that refers to var3, and will always have exactly one record. The second will contain var2, with as many records as there are elements in the vector.

Here's the source file:

#include "BaseAndDerived.h" # the example header above
// Some the additional includes - together, these pull in almost all of afw::table, so we don't
// want to include them in the header.
namespace {
// singleton class to hold schema and keys, since those are fixed at compile-time in this case
struct DerivedSchema : private boost::noncopyable {
Schema schema1;
Schema schema2;
Key<int> var3;
Key<int> var2first;
Key<float> var2second;
static DerivedSchema const & get() {
static DerivedSchema instance;
return instance;
}
private:
DerivedSchema() : schema1(), schema2(),
var1(schema1.addKey<double>("var1", "var1")),
var3(schema1.addKey<int>("var3", "archive ID for var3")),
var2first(schema2.addKey<int>("var2first", "var2[...].first")),
var2second(schema2.addKey<float>("var2second", "var2[...].second"))
{
schema1.getCitizen().markPersistent();
schema2.getCitizen().markPersistent();
}
};
class DerivedFactory : public PersistableFactory {
public:
virtual std::shared_ptr<Persistable> read(InputArchive const & archive, CatalogVector const & catalogs) const {
DerivedSchema const & keys = DerivedSchema::get()
assert(catalogs.size() == 2u);
BaseCatalog const & cat1 = catalogs.front();
assert(cat1.getSchema() == keys.schema1);
BaseCatalog const & cat2 = catalogs.back();
assert(cat2.getSchema() == keys.schema2);
std::shared_ptr<Derived> result(new Derived);
result->var1 = cat1.front().get(keys.var1);
int var3id = cat1.front().get(keys.var3)
result->var3 = archive.get(var3id);
for (auto i = cat2.begin(); i != cat2.end(); ++i) {
result->var2.push_back(std::make_pair(i->get(keys.var2first), i->get(keys.var2second)));
}
return result;
}
DerivedFactory(std::string const & name) : PersistableFactory(name) {}
};
DerivedFactory registration("Derived");
} // anonymous
void Derived::write(OutputArchiveHandle & handle) const {
DerivedSchema const & keys = DerivedSchema::get()
int var3id = handle.put(var3); // save the nested thing and get an ID we can save instead
BaseCatalog catalog1 = handle.makeCatalog(keys.schema1);
BaseCatalog catalog2 = handle.makeCatalog(keys.schema2);
std::shared_ptr<BaseRecord> record1 = catalog1.addNew();
record1->set(keys.var1, var1);
record1->set(keys.var3, var3id);
for (auto i = var2.begin(); i != var2.end(); ++i) {
std::shared_ptr<BaseRecord> record2 = catalog2.addNew();
record2->set(keys.var2first, i->first);
record2->set(keys.var2second, i->second);
}
handle.saveCatalog(catalog1);
handle.saveCatalog(catalog2);
}
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
Definition Catalog.h:489
A class used as a handle to a particular field in a table.
Definition Key.h:53
Defines the fields and offsets for a table.
Definition Schema.h:51
An object passed to Persistable::write to allow it to persist itself.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
A base class for factory classes used to reconstruct objects from records.
T make_pair(T... args)
CatalogT< BaseRecord > BaseCatalog
Definition fwd.h:72
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override

Archive Catalog Format

Archives contain a sequence of afw::table::BaseCatalog objects, which map simply to FITS binary tables on disk. While these catalogs contain additional metadata, which are also written to FITS headers, the only needed header entries actually used in unpersisting an archive is the 'AR_NCAT' key, which gives the total number of catalogs in the archive. Other keywords are merely checked for consistency.

The first catalog is an index into the others, with a schema described more completely in the ArchiveIndexSchema class. The schemas in subsequent catalogs are defined by the actual persisted objects. Multiple objects can be present in the same catalog (in blocks of rows) if they share the same schema (usually, but not always, because they have the same type).

Because the schemas are fully documented in the headers, the archive catalog format is largely self-describing. This means it can be inefficient for archives that contain a small number of distinct objects rather than a large number of similar objects, especially because the FITS standard specifies a minimum size for FITS headers, resulting in a lot of wasted space.