archive now support python exported objects

This commit is contained in:
Christopher Lackner 2018-12-20 17:01:27 +01:00
parent ab92bff3a0
commit 829defd3eb
9 changed files with 182 additions and 145 deletions

View File

@ -18,6 +18,10 @@
#include "type_traits.hpp" // for all_of_tmpl #include "type_traits.hpp" // for all_of_tmpl
#include "version.hpp" // for VersionInfo #include "version.hpp" // for VersionInfo
#ifdef NG_PYTHON
#include <pybind11/pybind11.h>
#endif // NG_PYTHON
namespace ngcore namespace ngcore
{ {
// Libraries using this archive can store their version here to implement backwards compatibility // Libraries using this archive can store their version here to implement backwards compatibility
@ -98,7 +102,8 @@ namespace ngcore
// vectors for storing the unarchived (shared) pointers // vectors for storing the unarchived (shared) pointers
std::vector<std::shared_ptr<void>> nr2shared_ptr; std::vector<std::shared_ptr<void>> nr2shared_ptr;
std::vector<void*> nr2ptr; std::vector<void*> nr2ptr;
protected:
bool shallow_to_python = false;
public: public:
Archive() = delete; Archive() = delete;
Archive(const Archive&) = delete; Archive(const Archive&) = delete;
@ -108,6 +113,31 @@ namespace ngcore
virtual ~Archive() { ; } virtual ~Archive() { ; }
template<typename T>
Archive& Shallow(T& val)
{
static_assert(detail::is_any_pointer<T>, "ShallowArchive must be given pointer type!");
#ifdef NG_PYTHON
if(shallow_to_python)
{
if(is_output)
ShallowOutPython(pybind11::cast(val));
else
val = pybind11::cast<T>(ShallowInPython());
}
else
#endif // NG_PYTHON
*this & val;
return *this;
}
#ifdef NG_PYTHON
virtual void ShallowOutPython(pybind11::object /*unused*/) // NOLINT (copy by val is ok for this virt func)
{ throw std::runtime_error("Should not get in ShallowToPython base class implementation!"); }
virtual pybind11::object ShallowInPython()
{ throw std::runtime_error("Should not get in ShallowFromPython base class implementation!"); }
#endif // NG_PYTHON
Archive& operator=(const Archive&) = delete; Archive& operator=(const Archive&) = delete;
Archive& operator=(Archive&&) = delete; Archive& operator=(Archive&&) = delete;
@ -526,16 +556,15 @@ namespace ngcore
static constexpr size_t BUFFERSIZE = 1024; static constexpr size_t BUFFERSIZE = 1024;
char buffer[BUFFERSIZE] = {}; char buffer[BUFFERSIZE] = {};
size_t ptr = 0; size_t ptr = 0;
std::shared_ptr<std::ostream> fout; protected:
std::shared_ptr<std::ostream> stream;
public: public:
BinaryOutArchive() = delete; BinaryOutArchive() = delete;
BinaryOutArchive(const BinaryOutArchive&) = delete; BinaryOutArchive(const BinaryOutArchive&) = delete;
BinaryOutArchive(BinaryOutArchive&&) = delete; BinaryOutArchive(BinaryOutArchive&&) = delete;
BinaryOutArchive(std::shared_ptr<std::ostream>&& afout) BinaryOutArchive(std::shared_ptr<std::ostream>&& astream)
: Archive(true), fout(std::move(afout)) : Archive(true), stream(std::move(astream))
{ { }
(*this) & GetLibraryVersions();
}
BinaryOutArchive(const std::string& filename) BinaryOutArchive(const std::string& filename)
: BinaryOutArchive(std::make_shared<std::ofstream>(filename)) {} : BinaryOutArchive(std::make_shared<std::ofstream>(filename)) {}
~BinaryOutArchive () override { FlushBuffer(); } ~BinaryOutArchive () override { FlushBuffer(); }
@ -543,9 +572,6 @@ namespace ngcore
BinaryOutArchive& operator=(const BinaryOutArchive&) = delete; BinaryOutArchive& operator=(const BinaryOutArchive&) = delete;
BinaryOutArchive& operator=(BinaryOutArchive&&) = delete; BinaryOutArchive& operator=(BinaryOutArchive&&) = delete;
const VersionInfo& GetVersion(const std::string& library) override
{ return GetLibraryVersions()[library]; }
using Archive::operator&; using Archive::operator&;
Archive & operator & (double & d) override Archive & operator & (double & d) override
{ return Write(d); } { return Write(d); }
@ -567,7 +593,7 @@ namespace ngcore
(*this) & len; (*this) & len;
FlushBuffer(); FlushBuffer();
if(len) if(len)
fout->write (&str[0], len); stream->write (&str[0], len);
return *this; return *this;
} }
Archive & operator & (char *& str) override Archive & operator & (char *& str) override
@ -576,14 +602,14 @@ namespace ngcore
(*this) & len; (*this) & len;
FlushBuffer(); FlushBuffer();
if(len > 0) if(len > 0)
fout->write (&str[0], len); // NOLINT stream->write (&str[0], len); // NOLINT
return *this; return *this;
} }
void FlushBuffer() override void FlushBuffer() override
{ {
if (ptr > 0) if (ptr > 0)
{ {
fout->write(&buffer[0], ptr); stream->write(&buffer[0], ptr);
ptr = 0; ptr = 0;
} }
} }
@ -594,7 +620,7 @@ namespace ngcore
{ {
if (unlikely(ptr > BUFFERSIZE-sizeof(T))) if (unlikely(ptr > BUFFERSIZE-sizeof(T)))
{ {
fout->write(&buffer[0], ptr); stream->write(&buffer[0], ptr);
*reinterpret_cast<T*>(&buffer[0]) = x; // NOLINT *reinterpret_cast<T*>(&buffer[0]) = x; // NOLINT
ptr = sizeof(T); ptr = sizeof(T);
return *this; return *this;
@ -608,20 +634,15 @@ namespace ngcore
// BinaryInArchive ====================================================================== // BinaryInArchive ======================================================================
class NGCORE_API BinaryInArchive : public Archive class NGCORE_API BinaryInArchive : public Archive
{ {
std::map<std::string, VersionInfo> vinfo{}; protected:
std::shared_ptr<std::istream> fin; std::shared_ptr<std::istream> stream;
public: public:
BinaryInArchive (std::shared_ptr<std::istream>&& afin) BinaryInArchive (std::shared_ptr<std::istream>&& astream)
: Archive(false), fin(std::move(afin)) : Archive(false), stream(std::move(astream))
{ { }
(*this) & vinfo;
}
BinaryInArchive (const std::string& filename) BinaryInArchive (const std::string& filename)
: BinaryInArchive(std::make_shared<std::ifstream>(filename)) { ; } : BinaryInArchive(std::make_shared<std::ifstream>(filename)) { ; }
const VersionInfo& GetVersion(const std::string& library) override
{ return vinfo[library]; }
using Archive::operator&; using Archive::operator&;
Archive & operator & (double & d) override Archive & operator & (double & d) override
{ Read(d); return *this; } { Read(d); return *this; }
@ -643,7 +664,7 @@ namespace ngcore
(*this) & len; (*this) & len;
str.resize(len); str.resize(len);
if(len) if(len)
fin->read(&str[0], len); // NOLINT stream->read(&str[0], len); // NOLINT
return *this; return *this;
} }
Archive & operator & (char *& str) override Archive & operator & (char *& str) override
@ -655,64 +676,60 @@ namespace ngcore
else else
{ {
str = new char[len+1]; // NOLINT str = new char[len+1]; // NOLINT
fin->read(&str[0], len); // NOLINT stream->read(&str[0], len); // NOLINT
str[len] = '\0'; // NOLINT str[len] = '\0'; // NOLINT
} }
return *this; return *this;
} }
Archive & Do (double * d, size_t n) override Archive & Do (double * d, size_t n) override
{ fin->read(reinterpret_cast<char*>(d), n*sizeof(double)); return *this; } // NOLINT { stream->read(reinterpret_cast<char*>(d), n*sizeof(double)); return *this; } // NOLINT
Archive & Do (int * i, size_t n) override Archive & Do (int * i, size_t n) override
{ fin->read(reinterpret_cast<char*>(i), n*sizeof(int)); return *this; } // NOLINT { stream->read(reinterpret_cast<char*>(i), n*sizeof(int)); return *this; } // NOLINT
Archive & Do (size_t * i, size_t n) override Archive & Do (size_t * i, size_t n) override
{ fin->read(reinterpret_cast<char*>(i), n*sizeof(size_t)); return *this; } // NOLINT { stream->read(reinterpret_cast<char*>(i), n*sizeof(size_t)); return *this; } // NOLINT
private: private:
template<typename T> template<typename T>
inline void Read(T& val) inline void Read(T& val)
{ fin->read(reinterpret_cast<char*>(&val), sizeof(T)); } // NOLINT { stream->read(reinterpret_cast<char*>(&val), sizeof(T)); } // NOLINT
}; };
// TextOutArchive ====================================================================== // TextOutArchive ======================================================================
class NGCORE_API TextOutArchive : public Archive class NGCORE_API TextOutArchive : public Archive
{ {
std::shared_ptr<std::ostream> fout; protected:
std::shared_ptr<std::ostream> stream;
public: public:
TextOutArchive (std::shared_ptr<std::ostream>&& afout) TextOutArchive (std::shared_ptr<std::ostream>&& astream)
: Archive(true), fout(std::move(afout)) : Archive(true), stream(std::move(astream))
{ { }
(*this) & GetLibraryVersions();
}
TextOutArchive (const std::string& filename) : TextOutArchive (const std::string& filename) :
TextOutArchive(std::make_shared<std::ofstream>(filename)) { } TextOutArchive(std::make_shared<std::ofstream>(filename)) { }
const VersionInfo& GetVersion(const std::string& library) override
{ return GetLibraryVersions()[library]; }
using Archive::operator&; using Archive::operator&;
Archive & operator & (double & d) override Archive & operator & (double & d) override
{ *fout << d << '\n'; return *this; } { *stream << d << '\n'; return *this; }
Archive & operator & (int & i) override Archive & operator & (int & i) override
{ *fout << i << '\n'; return *this; } { *stream << i << '\n'; return *this; }
Archive & operator & (short & i) override Archive & operator & (short & i) override
{ *fout << i << '\n'; return *this; } { *stream << i << '\n'; return *this; }
Archive & operator & (long & i) override Archive & operator & (long & i) override
{ *fout << i << '\n'; return *this; } { *stream << i << '\n'; return *this; }
Archive & operator & (size_t & i) override Archive & operator & (size_t & i) override
{ *fout << i << '\n'; return *this; } { *stream << i << '\n'; return *this; }
Archive & operator & (unsigned char & i) override Archive & operator & (unsigned char & i) override
{ *fout << int(i) << '\n'; return *this; } { *stream << int(i) << '\n'; return *this; }
Archive & operator & (bool & b) override Archive & operator & (bool & b) override
{ *fout << (b ? 't' : 'f') << '\n'; return *this; } { *stream << (b ? 't' : 'f') << '\n'; return *this; }
Archive & operator & (std::string & str) override Archive & operator & (std::string & str) override
{ {
int len = str.length(); int len = str.length();
*fout << len << '\n'; *stream << len << '\n';
if(len) if(len)
{ {
fout->write(&str[0], len); // NOLINT stream->write(&str[0], len); // NOLINT
*fout << '\n'; *stream << '\n';
} }
return *this; return *this;
} }
@ -722,8 +739,8 @@ namespace ngcore
*this & len; *this & len;
if(len > 0) if(len > 0)
{ {
fout->write (&str[0], len); // NOLINT stream->write (&str[0], len); // NOLINT
*fout << '\n'; *stream << '\n';
} }
return *this; return *this;
} }
@ -732,44 +749,39 @@ namespace ngcore
// TextInArchive ====================================================================== // TextInArchive ======================================================================
class NGCORE_API TextInArchive : public Archive class NGCORE_API TextInArchive : public Archive
{ {
std::map<std::string, VersionInfo> vinfo{}; protected:
std::shared_ptr<std::istream> fin; std::shared_ptr<std::istream> stream;
public: public:
TextInArchive (std::shared_ptr<std::istream>&& afin) : TextInArchive (std::shared_ptr<std::istream>&& astream) :
Archive(false), fin(std::move(afin)) Archive(false), stream(std::move(astream))
{ { }
(*this) & vinfo;
}
TextInArchive (const std::string& filename) TextInArchive (const std::string& filename)
: TextInArchive(std::make_shared<std::ifstream>(filename)) {} : TextInArchive(std::make_shared<std::ifstream>(filename)) {}
const VersionInfo& GetVersion(const std::string& library) override
{ return vinfo[library]; }
using Archive::operator&; using Archive::operator&;
Archive & operator & (double & d) override Archive & operator & (double & d) override
{ *fin >> d; return *this; } { *stream >> d; return *this; }
Archive & operator & (int & i) override Archive & operator & (int & i) override
{ *fin >> i; return *this; } { *stream >> i; return *this; }
Archive & operator & (short & i) override Archive & operator & (short & i) override
{ *fin >> i; return *this; } { *stream >> i; return *this; }
Archive & operator & (long & i) override Archive & operator & (long & i) override
{ *fin >> i; return *this; } { *stream >> i; return *this; }
Archive & operator & (size_t & i) override Archive & operator & (size_t & i) override
{ *fin >> i; return *this; } { *stream >> i; return *this; }
Archive & operator & (unsigned char & i) override Archive & operator & (unsigned char & i) override
{ int _i; *fin >> _i; i = _i; return *this; } { int _i; *stream >> _i; i = _i; return *this; }
Archive & operator & (bool & b) override Archive & operator & (bool & b) override
{ char c; *fin >> c; b = (c=='t'); return *this; } { char c; *stream >> c; b = (c=='t'); return *this; }
Archive & operator & (std::string & str) override Archive & operator & (std::string & str) override
{ {
int len; int len;
*fin >> len; *stream >> len;
char ch; char ch;
fin->get(ch); // '\n' stream->get(ch); // '\n'
str.resize(len); str.resize(len);
if(len) if(len)
fin->get(&str[0], len+1, '\0'); stream->get(&str[0], len+1, '\0');
return *this; return *this;
} }
Archive & operator & (char *& str) override Archive & operator & (char *& str) override
@ -785,13 +797,70 @@ namespace ngcore
str = new char[len+1]; // NOLINT str = new char[len+1]; // NOLINT
if(len) if(len)
{ {
fin->get(ch); // \n stream->get(ch); // \n
fin->get(&str[0], len+1, '\0'); // NOLINT stream->get(&str[0], len+1, '\0'); // NOLINT
} }
str[len] = '\0'; // NOLINT str[len] = '\0'; // NOLINT
return *this; return *this;
} }
}; };
#ifdef NG_PYTHON
namespace py = pybind11;
template<typename ARCHIVE>
class PyArchive : public ARCHIVE
{
private:
py::list lst;
size_t index = 0;
using ARCHIVE::stream;
public:
PyArchive(const py::object& alst = py::none()) :
ARCHIVE(std::make_shared<std::stringstream>()),
lst(alst.is_none() ? py::list() : py::cast<py::list>(alst))
{
ARCHIVE::shallow_to_python = true;
if(Input())
stream = std::make_shared<std::stringstream>(py::cast<py::bytes>(lst[py::len(lst)-1]));
}
using ARCHIVE::Output;
using ARCHIVE::Input;
using ARCHIVE::FlushBuffer;
using ARCHIVE::operator&;
using ARCHIVE::operator<<;
using ARCHIVE::GetVersion;
void ShallowOutPython(py::object val) override { lst.append(val); }
py::object ShallowInPython() override { return lst[index++]; }
py::list WriteOut()
{
FlushBuffer();
lst.append(py::bytes(std::static_pointer_cast<std::stringstream>(stream)->str()));
return lst;
}
};
template<typename T, typename T_ARCHIVE_OUT=BinaryOutArchive, typename T_ARCHIVE_IN=BinaryInArchive>
auto NGSPickle()
{
return py::pickle([](T& self)
{
PyArchive<T_ARCHIVE_OUT> ar;
ar & self;
return py::make_tuple(ar.WriteOut());
},
[](py::tuple state)
{
auto val = std::make_unique<T>();
PyArchive<T_ARCHIVE_IN> ar(state[0]);
ar & *val;
return std::move(val);
});
}
#endif // NG_PYTHON
} // namespace ngcore } // namespace ngcore
#endif // NETGEN_CORE_ARCHIVE_HPP #endif // NETGEN_CORE_ARCHIVE_HPP

View File

@ -1,6 +1,7 @@
#ifndef NETGEN_CORE_TYPE_TRAITS_HPP #ifndef NETGEN_CORE_TYPE_TRAITS_HPP
#define NETGEN_CORE_TYPE_TRAITS_HPP #define NETGEN_CORE_TYPE_TRAITS_HPP
#include <memory>
#include <type_traits> #include <type_traits>
namespace ngcore namespace ngcore
@ -11,6 +12,21 @@ namespace ngcore
template<bool ... vals> template<bool ... vals>
constexpr bool all_of_tmpl = std::is_same<_BoolArray<vals...>, _BoolArray<(vals || true)...>>::value; // NOLINT constexpr bool all_of_tmpl = std::is_same<_BoolArray<vals...>, _BoolArray<(vals || true)...>>::value; // NOLINT
template<typename T>
struct is_any_pointer_impl : std::false_type {};
template<typename T>
struct is_any_pointer_impl<T*> : std::true_type {};
template<typename T>
struct is_any_pointer_impl<std::shared_ptr<T>> : std::true_type {};
template<typename T>
struct is_any_pointer_impl<std::unique_ptr<T>> : std::true_type {};
template<typename T>
constexpr bool is_any_pointer = is_any_pointer_impl<T>::value;
} // namespace detail } // namespace detail
} // namespace ngcore } // namespace ngcore

View File

@ -368,23 +368,7 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails!
geo->FindIdenticSurfaces(1e-8 * geo->MaxSize()); geo->FindIdenticSurfaces(1e-8 * geo->MaxSize());
return geo; return geo;
}), py::arg("filename")) }), py::arg("filename"))
.def(py::pickle( .def(NGSPickle<CSGeometry>())
[](CSGeometry& self)
{
auto ss = make_shared<stringstream>();
BinaryOutArchive archive(ss);
archive & self;
archive.FlushBuffer();
return py::make_tuple(py::bytes(ss->str()));
},
[](py::tuple state)
{
auto geo = make_shared<CSGeometry>();
auto ss = make_shared<stringstream> (py::cast<py::bytes>(state[0]));
BinaryInArchive archive(ss);
archive & (*geo);
return geo;
}))
.def("Save", FunctionPointer([] (CSGeometry & self, string filename) .def("Save", FunctionPointer([] (CSGeometry & self, string filename)
{ {
cout << "save geometry to file " << filename << endl; cout << "save geometry to file " << filename << endl;

View File

@ -26,25 +26,8 @@ DLL_HEADER void ExportGeom2d(py::module &m)
ng_geometry = geo; ng_geometry = geo;
return geo; return geo;
})) }))
.def(py::pickle( .def(NGSPickle<SplineGeometry2d>())
[](SplineGeometry2d& self) .def("Load",&SplineGeometry2d::Load)
{
auto ss = make_shared<stringstream>();
BinaryOutArchive archive(ss);
archive & self;
archive.FlushBuffer();
return py::make_tuple(py::bytes(ss->str()));
},
[](py::tuple state)
{
auto geo = make_shared<SplineGeometry2d>();
auto ss = make_shared<stringstream> (py::cast<py::bytes>(state[0]));
BinaryInArchive archive(ss);
archive & (*geo);
return geo;
}))
.def("Load",&SplineGeometry2d::Load)
.def("AppendPoint", FunctionPointer .def("AppendPoint", FunctionPointer
([](SplineGeometry2d &self, double px, double py, double maxh, double hpref, string name) ([](SplineGeometry2d &self, double px, double py, double maxh, double hpref, string name)
{ {

View File

@ -1316,7 +1316,7 @@ namespace netgen
archive & *ident; archive & *ident;
archive & geometry; archive.Shallow(geometry);
archive & *curvedelems; archive & *curvedelems;
if (archive.Input()) if (archive.Input())

View File

@ -493,6 +493,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m)
} ), } ),
py::arg("dim")=3 py::arg("dim")=3
) )
.def(NGSPickle<Mesh>())
/* /*
.def("__init__", .def("__init__",

View File

@ -18,23 +18,7 @@ DLL_HEADER void ExportNgOCC(py::module &m)
{ {
py::class_<OCCGeometry, shared_ptr<OCCGeometry>, NetgenGeometry> (m, "OCCGeometry", R"raw_string(Use LoadOCCGeometry to load the geometry from a *.step file.)raw_string") py::class_<OCCGeometry, shared_ptr<OCCGeometry>, NetgenGeometry> (m, "OCCGeometry", R"raw_string(Use LoadOCCGeometry to load the geometry from a *.step file.)raw_string")
.def(py::init<>()) .def(py::init<>())
.def(py::pickle( .def(NGSPickle<OCCGeometry>())
[](OCCGeometry& self)
{
auto ss = make_shared<stringstream>();
BinaryOutArchive archive(ss);
archive & self;
archive.FlushBuffer();
return py::make_tuple(py::bytes(ss->str()));
},
[](py::tuple state)
{
auto geo = make_shared<OCCGeometry>();
auto ss = make_shared<stringstream> (py::cast<py::bytes>(state[0]));
BinaryInArchive archive(ss);
archive & (*geo);
return geo;
}))
.def("Heal",[](OCCGeometry & self, double tolerance, bool fixsmalledges, bool fixspotstripfaces, bool sewfaces, bool makesolids, bool splitpartitions) .def("Heal",[](OCCGeometry & self, double tolerance, bool fixsmalledges, bool fixspotstripfaces, bool sewfaces, bool makesolids, bool splitpartitions)
{ {
self.tolerance = tolerance; self.tolerance = tolerance;

View File

@ -20,23 +20,7 @@ DLL_HEADER void ExportSTL(py::module & m)
{ {
py::class_<STLGeometry,shared_ptr<STLGeometry>, NetgenGeometry> (m,"STLGeometry") py::class_<STLGeometry,shared_ptr<STLGeometry>, NetgenGeometry> (m,"STLGeometry")
.def(py::init<>()) .def(py::init<>())
.def(py::pickle( .def(NGSPickle<STLGeometry>())
[](STLGeometry& self)
{
auto ss = make_shared<stringstream>();
BinaryOutArchive archive(ss);
archive & self;
archive.FlushBuffer();
return py::make_tuple(py::bytes(ss->str()));
},
[](py::tuple state)
{
auto geo = make_shared<STLGeometry>();
auto ss = make_shared<stringstream> (py::cast<py::bytes>(state[0]));
BinaryInArchive archive(ss);
archive & (*geo);
return geo;
}))
.def("_visualizationData", [](shared_ptr<STLGeometry> stl_geo) .def("_visualizationData", [](shared_ptr<STLGeometry> stl_geo)
{ {
std::vector<float> vertices; std::vector<float> vertices;

View File

@ -85,5 +85,21 @@ def test_pickle_geom2d():
for val1, val2 in zip(vd1.values(), vd2.values()): for val1, val2 in zip(vd1.values(), vd2.values()):
assert numpy.array_equal(val1, val2) assert numpy.array_equal(val1, val2)
def test_pickle_mesh():
import netgen.csg as csg
geo = csg.CSGeometry()
brick = csg.OrthoBrick(csg.Pnt(-3,-3,-3), csg.Pnt(3,3,3))
mesh = geo.GenerateMesh(maxh=0.2)
assert geo == mesh.GetGeometry()
dump = pickle.dumps([geo,mesh])
geo2, mesh2 = pickle.loads(dump)
assert geo2 == mesh2.GetGeometry()
mesh.Save("msh1.vol.gz")
mesh2.Save("msh2.vol.gz")
import filecmp, os
assert filecmp.cmp("msh1.vol.gz", "msh2.vol.gz")
os.remove("msh1.vol.gz")
os.remove("msh2.vol.gz")
if __name__ == "__main__": if __name__ == "__main__":
test_pickle_csg() test_pickle_mesh()