39 #include <boost/format.hpp>
45 namespace berrc = boost::system::errc;
49 namespace LibUtilities
63 :
FieldIO(pComm, sharedFilesystem)
88 std::vector<FieldDefinitionsSharedPtr> &fielddefs,
89 std::vector<std::vector<NekDouble> > &fielddata,
93 double tm0 = 0.0, tm1 = 0.0;
94 if (
m_comm->GetRank() == 0)
100 ASSERTL1(fielddefs.size() == fielddata.size(),
101 "Length of fielddefs and fielddata incompatible");
102 for (
int f = 0; f < fielddefs.size(); ++f)
105 "Fielddata vector must contain at least one value.");
108 fielddefs[f]->m_fields.size() *
110 "Invalid size of fielddata vector.");
116 std::string filename =
SetUpOutput(outFile,
true, backup);
121 TiXmlDeclaration *decl =
new TiXmlDeclaration(
"1.0",
"utf-8",
"");
122 doc.LinkEndChild(decl);
124 TiXmlElement *root =
new TiXmlElement(
"NEKTAR");
125 doc.LinkEndChild(root);
129 for (
int f = 0; f < fielddefs.size(); ++f)
133 TiXmlElement *elemTag =
new TiXmlElement(
"ELEMENTS");
134 root->LinkEndChild(elemTag);
137 std::string fieldsString;
139 std::stringstream fieldsStringStream;
141 for (std::vector<int>::size_type i = 0;
142 i < fielddefs[f]->m_fields.size();
147 fieldsStringStream <<
",";
149 fieldsStringStream << fielddefs[f]->m_fields[i];
152 fieldsString = fieldsStringStream.str();
154 elemTag->SetAttribute(
"FIELDS", fieldsString);
157 std::string shapeString;
159 std::stringstream shapeStringStream;
160 shapeStringStream <<
ShapeTypeMap[fielddefs[f]->m_shapeType];
161 if (fielddefs[f]->m_numHomogeneousDir == 1)
163 shapeStringStream <<
"-HomogenousExp1D";
165 else if (fielddefs[f]->m_numHomogeneousDir == 2)
167 shapeStringStream <<
"-HomogenousExp2D";
170 if (fielddefs[f]->m_homoStrips)
172 shapeStringStream <<
"-Strips";
175 shapeString = shapeStringStream.str();
177 elemTag->SetAttribute(
"SHAPE", shapeString);
180 std::string basisString;
182 std::stringstream basisStringStream;
184 for (std::vector<BasisType>::size_type i = 0;
185 i < fielddefs[f]->m_basis.size();
190 basisStringStream <<
",";
192 basisStringStream <<
BasisTypeMap[fielddefs[f]->m_basis[i]];
195 basisString = basisStringStream.str();
197 elemTag->SetAttribute(
"BASIS", basisString);
200 if (fielddefs[f]->m_numHomogeneousDir)
202 std::string homoLenString;
204 std::stringstream homoLenStringStream;
206 for (
int i = 0; i < fielddefs[f]->m_numHomogeneousDir; ++i)
209 homoLenStringStream <<
",";
211 << fielddefs[f]->m_homogeneousLengths[i];
214 homoLenString = homoLenStringStream.str();
216 elemTag->SetAttribute(
"HOMOGENEOUSLENGTHS", homoLenString);
220 if (fielddefs[f]->m_numHomogeneousDir)
222 if (fielddefs[f]->m_homogeneousYIDs.size() > 0)
224 std::string homoYIDsString;
226 std::stringstream homoYIDsStringStream;
228 for (
int i = 0; i < fielddefs[f]->m_homogeneousYIDs.size();
233 homoYIDsStringStream <<
",";
236 << fielddefs[f]->m_homogeneousYIDs[i];
239 homoYIDsString = homoYIDsStringStream.str();
241 elemTag->SetAttribute(
"HOMOGENEOUSYIDS", homoYIDsString);
244 if (fielddefs[f]->m_homogeneousZIDs.size() > 0)
246 std::string homoZIDsString;
248 std::stringstream homoZIDsStringStream;
250 for (
int i = 0; i < fielddefs[f]->m_homogeneousZIDs.size();
255 homoZIDsStringStream <<
",";
258 << fielddefs[f]->m_homogeneousZIDs[i];
261 homoZIDsString = homoZIDsStringStream.str();
263 elemTag->SetAttribute(
"HOMOGENEOUSZIDS", homoZIDsString);
266 if (fielddefs[f]->m_homogeneousSIDs.size() > 0)
268 std::string homoSIDsString;
270 std::stringstream homoSIDsStringStream;
272 for (
int i = 0; i < fielddefs[f]->m_homogeneousSIDs.size();
277 homoSIDsStringStream <<
",";
280 << fielddefs[f]->m_homogeneousSIDs[i];
283 homoSIDsString = homoSIDsStringStream.str();
285 elemTag->SetAttribute(
"HOMOGENEOUSSIDS", homoSIDsString);
290 std::string numModesString;
292 std::stringstream numModesStringStream;
294 if (fielddefs[f]->m_uniOrder)
296 numModesStringStream <<
"UNIORDER:";
299 for (std::vector<int>::size_type i = 0;
300 i < fielddefs[f]->m_basis.size();
305 numModesStringStream <<
",";
307 numModesStringStream << fielddefs[f]->m_numModes[i];
313 numModesStringStream <<
"MIXORDER:";
315 for (std::vector<int>::size_type i = 0;
316 i < fielddefs[f]->m_numModes.size();
321 numModesStringStream <<
",";
323 numModesStringStream << fielddefs[f]->m_numModes[i];
328 numModesString = numModesStringStream.str();
330 elemTag->SetAttribute(
"NUMMODESPERDIR", numModesString);
335 std::string idString;
337 std::stringstream idStringStream;
340 elemTag->SetAttribute(
"ID", idString);
341 elemTag->SetAttribute(
"COMPRESSED",
347 elemTag->SetAttribute(
"BITSIZE",
349 std::string base64string;
352 "Failed to compress field data.");
354 elemTag->LinkEndChild(
new TiXmlText(base64string));
356 doc.SaveFile(filename);
361 if (
m_comm->GetRank() == 0)
364 cout <<
" (" << tm1 - tm0 <<
"s, XML)" << endl;
381 const std::string &outFile,
382 const std::vector<std::string> fileNames,
383 std::vector<std::vector<unsigned int> > &elementList,
387 TiXmlDeclaration *decl =
new TiXmlDeclaration(
"1.0",
"utf-8",
"");
388 doc.LinkEndChild(decl);
390 ASSERTL0(fileNames.size() == elementList.size(),
391 "Outfile names and list of elements ids does not match");
393 TiXmlElement *root =
new TiXmlElement(
"NEKTAR");
394 doc.LinkEndChild(root);
398 for (
int t = 0; t < fileNames.size(); ++t)
400 if (elementList[t].size())
402 TiXmlElement *elemIDs =
new TiXmlElement(
"Partition");
403 root->LinkEndChild(elemIDs);
405 elemIDs->SetAttribute(
"FileName", fileNames[t]);
409 elemIDs->LinkEndChild(
new TiXmlText(IDstring));
413 doc.SaveFile(outFile);
429 const std::string &inFile,
430 std::vector<std::string> &fileNames,
431 std::vector<std::vector<unsigned int> > &elementList,
434 TiXmlDocument doc(inFile);
435 bool loadOkay = doc.LoadFile();
437 std::stringstream errstr;
438 errstr <<
"Unable to load file: " << inFile << std::endl;
439 errstr <<
"Reason: " << doc.ErrorDesc() << std::endl;
440 errstr <<
"Position: Line " << doc.ErrorRow() <<
", Column "
441 << doc.ErrorCol() << std::endl;
445 TiXmlHandle docHandle(&doc);
449 TiXmlElement *master = doc.FirstChildElement(
"NEKTAR");
450 ASSERTL0(master,
"Unable to find NEKTAR tag in file.");
453 std::string strPartition =
"Partition";
456 TiXmlElement *fldfileIDs = master->FirstChildElement(strPartition.c_str());
460 strPartition =
"MultipleFldFiles";
461 fldfileIDs = master->FirstChildElement(
"MultipleFldFiles");
464 "Unable to find 'Partition' or 'MultipleFldFiles' tag "
465 "within nektar tag.");
470 const char *attr = fldfileIDs->Attribute(
"FileName");
472 "'FileName' not provided as an attribute of '" + strPartition +
474 fileNames.push_back(std::string(attr));
476 const char *elementIDs = fldfileIDs->GetText();
477 ASSERTL0(elementIDs,
"Element IDs not specified.");
479 std::string elementIDsStr(elementIDs);
481 std::vector<unsigned int> idvec;
484 elementList.push_back(idvec);
486 fldfileIDs = fldfileIDs->NextSiblingElement(strPartition.c_str());
503 std::vector<FieldDefinitionsSharedPtr> &fielddefs,
504 std::vector<std::vector<NekDouble> > &fielddata,
508 std::string infile = infilename;
510 fs::path pinfilename(infilename);
514 if (fs::is_directory(pinfilename))
516 fs::path infofile(
"Info.xml");
517 fs::path fullpath = pinfilename / infofile;
520 std::vector<std::string> filenames;
521 std::vector<std::vector<unsigned int> > elementIDs_OnPartitions;
524 infile, filenames, elementIDs_OnPartitions, fieldinfomap);
531 for (
int i = 0; i < filenames.size(); ++i)
533 fs::path pfilename(filenames[i]);
534 fullpath = pinfilename / pfilename;
547 map<int, vector<int> > FileIDs;
551 for (i = 0; i < elementIDs_OnPartitions.size(); ++i)
553 for (j = 0; j < elementIDs_OnPartitions[i].size(); ++j)
555 FileIDs[elementIDs_OnPartitions[i][j]].push_back(i);
559 for (i = 0; i < ElementIDs.num_elements(); ++i)
561 it = FileIDs.find(ElementIDs[i]);
562 if (it != FileIDs.end())
564 for (j = 0; j < it->second.size(); ++j)
566 LoadFile.insert(it->second[j]);
572 for (iter = LoadFile.begin(); iter != LoadFile.end(); ++iter)
574 fs::path pfilename(filenames[*iter]);
575 fullpath = pinfilename / pfilename;
610 TiXmlElement *metadata = 0;
611 TiXmlElement *master = 0;
614 master = xml->
Get().FirstChildElement(
"NEKTAR");
615 ASSERTL0(master,
"Unable to find NEKTAR tag in file.");
616 std::string strLoop =
"NEKTAR";
620 metadata = master->FirstChildElement(
"FIELDMETADATA");
623 TiXmlElement *param = metadata->FirstChildElement(
"P");
627 TiXmlAttribute *paramAttr = param->FirstAttribute();
628 std::string attrName(paramAttr->Name());
629 std::string paramString;
631 if (attrName ==
"PARAM")
633 paramString.insert(0, paramAttr->Value());
637 ASSERTL0(
false,
"PARAM not provided as an attribute in "
638 "FIELDMETADATA section");
642 std::string paramBodyStr;
644 TiXmlNode *paramBody = param->FirstChild();
646 paramBodyStr += paramBody->ToText()->Value();
648 fieldmetadatamap[paramString] = paramBodyStr;
649 param = param->NextSiblingElement(
"P");
654 metadata = master->FirstChildElement(
"Metadata");
657 TiXmlElement *param = metadata->FirstChildElement();
661 std::string paramString = param->Value();
662 if (paramString !=
"Provenance")
665 TiXmlNode *paramBody = param->FirstChild();
666 std::string paramBodyStr = paramBody->ToText()->Value();
668 fieldmetadatamap[paramString] = paramBodyStr;
670 param = param->NextSiblingElement();
691 const std::string &outname,
692 const vector<FieldDefinitionsSharedPtr> &fielddefs,
695 ASSERTL0(!outname.empty(),
"Empty path given to SetUpFieldMetaData()");
697 int nprocs =
m_comm->GetSize();
698 int rank =
m_comm->GetRank();
700 fs::path specPath(outname);
705 std::vector<unsigned int> elmtnums(nprocs, 0);
706 std::vector<unsigned int> idlist;
708 for (i = 0; i < fielddefs.size(); ++i)
710 elmtnums[rank] += fielddefs[i]->m_elementIDs.size();
711 idlist.insert(idlist.end(),
712 fielddefs[i]->m_elementIDs.begin(),
713 fielddefs[i]->m_elementIDs.end());
721 std::vector<std::vector<unsigned int> > ElementIDs(nprocs);
724 ElementIDs[0] = idlist;
725 for (i = 1; i < nprocs; ++i)
727 std::vector<unsigned int> tmp(elmtnums[i]);
733 std::vector<std::string> filenames;
734 for (
int i = 0; i < nprocs; ++i)
738 filenames.push_back(pad.str());
764 std::vector<FieldDefinitionsSharedPtr> &fielddefs,
769 TiXmlElement *master =
772 master = xml->
Get().FirstChildElement(
"NEKTAR");
773 ASSERTL0(master,
"Unable to find NEKTAR tag in file.");
774 std::string strLoop =
"NEKTAR";
775 TiXmlElement *loopXml = master;
777 TiXmlElement *expansionTypes;
780 expansionTypes = master->FirstChildElement(
"EXPANSIONS");
781 ASSERTL0(expansionTypes,
"Unable to find EXPANSIONS tag in file.");
782 loopXml = expansionTypes;
783 strLoop =
"EXPANSIONS";
789 TiXmlElement *element = loopXml->FirstChildElement(
"ELEMENTS");
790 ASSERTL0(element,
"Unable to find ELEMENTS tag within nektar tag.");
795 std::string idString;
796 std::string shapeString;
797 std::string basisString;
798 std::string homoLengthsString;
799 std::string homoSIDsString;
800 std::string homoZIDsString;
801 std::string homoYIDsString;
802 std::string numModesString;
803 std::string numPointsString;
804 std::string fieldsString;
805 std::string pointsString;
806 bool pointDef =
false;
807 bool numPointDef =
false;
808 TiXmlAttribute *attr = element->FirstAttribute();
811 std::string attrName(attr->Name());
812 if (attrName ==
"FIELDS")
814 fieldsString.insert(0, attr->Value());
816 else if (attrName ==
"SHAPE")
818 shapeString.insert(0, attr->Value());
820 else if (attrName ==
"BASIS")
822 basisString.insert(0, attr->Value());
824 else if (attrName ==
"HOMOGENEOUSLENGTHS")
826 homoLengthsString.insert(0, attr->Value());
828 else if (attrName ==
"HOMOGENEOUSSIDS")
830 homoSIDsString.insert(0, attr->Value());
832 else if (attrName ==
"HOMOGENEOUSZIDS")
834 homoZIDsString.insert(0, attr->Value());
836 else if (attrName ==
"HOMOGENEOUSYIDS")
838 homoYIDsString.insert(0, attr->Value());
840 else if (attrName ==
"NUMMODESPERDIR")
842 numModesString.insert(0, attr->Value());
844 else if (attrName ==
"ID")
846 idString.insert(0, attr->Value());
848 else if (attrName ==
"POINTSTYPE")
850 pointsString.insert(0, attr->Value());
853 else if (attrName ==
"NUMPOINTSPERDIR")
855 numPointsString.insert(0, attr->Value());
858 else if (attrName ==
"COMPRESSED")
862 "Compressed formats do not "
863 "match. Expected: " +
865 " but got " + string(attr->Value()));
867 else if (attrName ==
"BITSIZE")
875 std::string errstr(
"Unknown attribute: ");
886 if (shapeString.find(
"Strips") != string::npos)
896 if ((loc = shapeString.find_first_of(
"-")) != string::npos)
898 if (shapeString.find(
"Exp1D") != string::npos)
907 shapeString.erase(loc, shapeString.length());
911 std::vector<unsigned int> elementIds;
915 ASSERTL0(valid,
"Unable to correctly parse the element ids.");
932 std::string(
"Unable to correctly parse the shape type: ")
937 std::vector<std::string> basisStrings;
938 std::vector<BasisType> basis;
941 ASSERTL0(valid,
"Unable to correctly parse the basis types.");
942 for (std::vector<std::string>::size_type i = 0;
943 i < basisStrings.size();
958 std::string(
"Unable to correctly parse the basis type: ")
959 .append(basisStrings[i])
964 std::vector<NekDouble> homoLengths;
968 homoLengthsString.c_str(), homoLengths);
969 ASSERTL0(valid,
"Unable to correctly parse the number of "
970 "homogeneous lengths.");
974 std::vector<unsigned int> homoSIDs;
980 "Unable to correctly parse homogeneous strips IDs.");
984 std::vector<unsigned int> homoZIDs;
985 std::vector<unsigned int> homoYIDs;
992 "Unable to correctly parse homogeneous planes IDs.");
999 ASSERTL0(valid,
"Unable to correctly parse homogeneous lines "
1000 "IDs in z-direction.");
1003 ASSERTL0(valid,
"Unable to correctly parse homogeneous lines "
1004 "IDs in y-direction.");
1008 std::vector<PointsType> points;
1012 std::vector<std::string> pointsStrings;
1014 pointsString.c_str(), pointsStrings);
1015 ASSERTL0(valid,
"Unable to correctly parse the points types.");
1016 for (std::vector<std::string>::size_type i = 0;
1017 i < pointsStrings.size();
1033 "Unable to correctly parse the points type: ")
1034 .append(pointsStrings[i])
1040 std::vector<unsigned int> numModes;
1041 bool UniOrder =
false;
1043 if (strstr(numModesString.c_str(),
"UNIORDER:"))
1049 numModesString.c_str() + 9, numModes);
1050 ASSERTL0(valid,
"Unable to correctly parse the number of modes.");
1053 std::vector<unsigned int> numPoints;
1057 numPointsString.c_str(), numPoints);
1059 "Unable to correctly parse the number of points.");
1063 std::vector<std::string> Fields;
1065 fieldsString.c_str(), Fields);
1066 ASSERTL0(valid,
"Unable to correctly parse the number of fields.");
1086 fielddefs.push_back(fielddef);
1088 element = element->NextSiblingElement(
"ELEMENTS");
1090 loopXml = loopXml->NextSiblingElement(strLoop);
1103 const std::vector<FieldDefinitionsSharedPtr> &fielddefs,
1104 std::vector<std::vector<NekDouble> > &fielddata)
1110 TiXmlElement *master =
1113 master = xml->
Get().FirstChildElement(
"NEKTAR");
1114 ASSERTL0(master,
"Unable to find NEKTAR tag in file.");
1119 TiXmlElement *element = master->FirstChildElement(
"ELEMENTS");
1120 ASSERTL0(element,
"Unable to find ELEMENTS tag within nektar tag.");
1124 TiXmlNode *elementChild = element->FirstChild();
1126 "Unable to extract the data from the element tag.");
1127 std::string elementStr;
1128 while (elementChild)
1130 if (elementChild->Type() == TiXmlNode::TINYXML_TEXT)
1132 elementStr += elementChild->ToText()->ValueStr();
1134 elementChild = elementChild->NextSibling();
1137 std::vector<NekDouble> elementFieldData;
1140 const char *CompressStr = element->Attribute(
"COMPRESSED");
1145 "Compressed formats do not match. "
1148 " but got " +
string(CompressStr));
1152 elementStr, elementFieldData),
1153 "Failed to decompress field data.");
1154 fielddata.push_back(elementFieldData);
1158 fielddata[cntdumps].size() ==
1159 datasize * fielddefs[cntdumps]->m_fields.size(),
1160 "Input data is not the same length as header infoarmation");
1164 element = element->NextSiblingElement(
"ELEMENTS");
1166 master = master->NextSiblingElement(
"NEKTAR");
#define ASSERTL0(condition, msg)
static bool GenerateOrderedStringVector(const char *const str, std::vector< std::string > &vec)
static bool GenerateOrderedVector(const char *const str, std::vector< unsigned int > &vec)
static boost::shared_ptr< DataType > AllocateSharedPtr()
Allocate a shared pointer from the memory pool.
const char *const BasisTypeMap[]
const std::string kPointsTypeStr[]
FieldIOXml(LibUtilities::CommSharedPtr pComm, bool sharedFilesystem)
Default constructor.
void ImportMultiFldFileIDs(const std::string &inFile, std::vector< std::string > &fileNames, std::vector< std::vector< unsigned int > > &elementList, FieldMetaDataMap &fieldmetadatamap)
Read file containing element ID to partition mapping.
boost::shared_ptr< XmlTagWriter > XmlTagWriterSharedPtr
boost::shared_ptr< FieldDefinitions > FieldDefinitionsSharedPtr
TiXmlDocument & Get()
Return the TinyXML document of this source.
virtual void v_Write(const std::string &outFile, std::vector< FieldDefinitionsSharedPtr > &fielddefs, std::vector< std::vector< NekDouble > > &fielddata, const FieldMetaDataMap &fieldinfomap=NullFieldMetaDataMap, const bool backup=false)
Write an XML file to outFile given the field definitions fielddefs, field data fielddata and metadata...
static std::string GenerateSeqString(const std::vector< unsigned int > &elmtids)
static std::string className
Name of class.
FieldIOFactory & GetFieldIOFactory()
Returns the FieldIO factory.
static bool GenerateSeqVector(const char *const str, std::vector< unsigned int > &vec)
std::map< std::string, std::string > FieldMetaDataMap
int CheckFieldDefinition(const FieldDefinitionsSharedPtr &fielddefs)
Check field definitions for correctness and return storage size.
const char *const ShapeTypeMap[]
std::string GetCompressString(void)
boost::shared_ptr< DataSource > DataSourceSharedPtr
boost::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
void v_Import(const std::string &infilename, std::vector< FieldDefinitionsSharedPtr > &fielddefs, std::vector< std::vector< NekDouble > > &fielddata=NullVectorNekDoubleVector, FieldMetaDataMap &fieldinfomap=NullFieldMetaDataMap, const Array< OneD, int > &ElementIDs=NullInt1DArray)
Import an XML format file.
static DataSourceSharedPtr create(const std::string &fn)
Create a new XML data source based on the filename.
#define WARNINGL0(condition, msg)
static std::vector< std::vector< NekDouble > > NullVectorNekDoubleVector
static FieldIOSharedPtr create(LibUtilities::CommSharedPtr pComm, bool sharedFilesystem)
Creates an instance of this class.
DataSourceSharedPtr ImportFieldMetaData(const std::string &filename, FieldMetaDataMap &fieldmetadatamap)
Import the metadata from a field file.
std::string PortablePath(const boost::filesystem::path &path)
create portable path on different platforms for boost::filesystem path
void ImportFieldData(DataSourceSharedPtr dataSource, const std::vector< FieldDefinitionsSharedPtr > &fielddefs, std::vector< std::vector< NekDouble > > &fielddata)
Import field data from a target file.
void WriteMultiFldFileIDs(const std::string &outfile, const std::vector< std::string > fileNames, std::vector< std::vector< unsigned int > > &elementList, const FieldMetaDataMap &fieldinfomap=NullFieldMetaDataMap)
Write out a file containing element ID to partition mapping.
std::string SetUpOutput(const std::string outname, bool perRank, bool backup=false)
Set up the filesystem ready for output.
LibUtilities::CommSharedPtr m_comm
Communicator to use when writing parallel format.
int ZlibEncodeToBase64Str(std::vector< T > &in, std::string &out64)
StandardMatrixTag boost::call_traits< LhsDataType >::const_reference rhs typedef NekMatrix< LhsDataType, StandardMatrixTag >::iterator iterator
std::string GetBitSizeStr(void)
boost::shared_ptr< XmlDataSource > XmlDataSourceSharedPtr
Class for operating on Nektar++ input/output files.
virtual std::string GetFileEnding() const
Helper function that determines default file extension.
void SetUpFieldMetaData(const std::string &outname, const std::vector< FieldDefinitionsSharedPtr > &fielddefs, const FieldMetaDataMap &fieldmetadatamap)
Set up field meta data map.
void ImportFieldDefs(DataSourceSharedPtr dataSource, std::vector< FieldDefinitionsSharedPtr > &fielddefs, bool expChild)
Import field definitions from the target file.
static Array< OneD, int > NullInt1DArray
static bool GenerateUnOrderedVector(const char *const str, std::vector< NekDouble > &vec)
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
void AddInfoTag(TagWriterSharedPtr root, const FieldMetaDataMap &fieldmetadatamap)
Add provenance information to the field metadata map.
virtual DataSourceSharedPtr v_ImportFieldMetaData(const std::string &filename, FieldMetaDataMap &fieldmetadatamap)
Import field metadata from filename and return the data source which wraps filename.
int ZlibDecodeFromBase64Str(std::string &in64, std::vector< T > &out)
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, tDescription pDesc="")
Register a class with the factory.