3
0
mirror of https://github.com/triqs/dft_tools synced 2024-12-27 06:43:40 +01:00
dft_tools/triqs/parameters/opaque_object_h5.hpp

332 lines
17 KiB
C++
Raw Normal View History

/*******************************************************************************
*
* TRIQS: a Toolbox for Research in Interacting Quantum Systems
*
* Copyright (C) 2013 by H. Hafermann, O. Parcollet
*
* TRIQS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* TRIQS. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
#ifndef TRIQS_UTILITY_OPAQUE_OBJECT_H5_H
#define TRIQS_UTILITY_OPAQUE_OBJECT_H5_H
#include <triqs/utility/first_include.hpp>
#include <string>
#include <complex>
#include <memory>
#include <map>
#include <boost/lexical_cast.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/serialization/map.hpp>
#include <triqs/utility/exceptions.hpp>
#include <triqs/utility/serialization.hpp>
#include <triqs/arrays.hpp>
#include <triqs/python_tools/array_interface.hpp>
namespace triqs { namespace utility {
template<typename T>
std::ostream & operator<<(std::ostream & out, std::vector<T> const & v) {
out << "["; int c = 0; for (auto const & x : v) out << ( c++==0 ? ", " :" ") << x;
return out << "]";
}
// All the predefined cast of _object
#define TRIQS_UTIL_OPAQUE_OBJECT_PREDEFINED_CAST (int)(long)(long long)(unsigned int)(unsigned long)(unsigned long long)(double)(bool)(std::string)
// a trait to compute the type actually stored in the opaque object.
// T except for integers, which are all stored as long
template<typename T> struct _object_collapse_type_impl : std::conditional<std::is_integral<T>::value, long, T > {};
// We store the array/matrix/vector as array_c (a view repackaged as a value) since :
// in python : it allows to extract a view
// in C++ : it allows to extract a value (array) with a copy, or a view (array_view) without a copy
template<class T, int R, ull_t Opt, ull_t To> struct _object_collapse_type_impl< arrays::array<T,R,Opt,To>> { typedef arrays::array_c<T,R,Opt,To> type;};
template<class T, ull_t Opt, ull_t To> struct _object_collapse_type_impl< arrays::matrix<T,Opt,To>> { typedef arrays::array_c<T,2,Opt,To> type;};
template<class T, ull_t Opt> struct _object_collapse_type_impl< arrays::vector<T,Opt>> { typedef arrays::array_c<T,1,Opt> type;};
//same thing for the view
template<class T, int R, ull_t Opt, ull_t To> struct _object_collapse_type_impl< arrays::array_view<T,R,Opt,To>> { typedef arrays::array_c<T,R,Opt,To> type;};
template<class T, ull_t Opt, ull_t To> struct _object_collapse_type_impl< arrays::matrix_view<T,Opt,To>> { typedef arrays::array_c<T,2,Opt,To> type;};
template<class T, ull_t Opt> struct _object_collapse_type_impl< arrays::vector_view<T,Opt>> { typedef arrays::array_c<T,1,Opt> type;};
// uncomment after merge qview branch
//template<class T, int R, ull_t Opt, ull_t To> struct _object_collapse_type_impl< arrays::array_qview<T,R,Opt,To>> { typedef arrays::array_c<T,R,Opt,To> type;};
//template<class T, ull_t Opt, ull_t To> struct _object_collapse_type_impl< arrays::matrix_qview<T,Opt,To>> { typedef arrays::array_c<T,2,Opt,To> type;};
//template<class T, ull_t Opt> struct _object_collapse_type_impl< arrays::vector_qview<T,Opt>> { typedef arrays::array_c<T,1,Opt> type;};
template<typename T> struct _object_collapse_type: _object_collapse_type_impl <typename std::decay<T>::type> {};
// Since I used array_c to store in the dict, I need to make it h5 read/writable.
// write is ok, because it is a view. Read : we can not use the view (no resize)
// so I first build the value (an array) and rebind the array_c to it.
template<class T, int R, ull_t Opt, ull_t To>
void h5_read(h5::group const &gr, std::string const &Name, arrays::array_c<T,R,Opt,To> & a) {
arrays::array<T,R,Opt,To> v; h5_read(gr,Name,v); a=v();// rebinds !
}
// --------------- the opaque object ---------------------------------------
// _object is a value : it makes deep copies in particular ...
class _object {
std::shared_ptr<void> p; // the object handled by the class
size_t type_code_; // id of the type, implementation dependent...
std::function<_object()> clone_; // clones the object : will be used to make copy of parameters
std::function<void(h5::group const &, std::string const &)> h5_w; // the function to write in h5
std::function<std::string()> serialize_; // for boost serialization ...
std::function<void(std::ostream&)> print_;
std::string name_;
public :
template<typename T> static constexpr size_t type_code () { return typeid(T).hash_code();}
template<typename T> static std::string make_type_name () { return get_triqs_hdf5_data_scheme(typename _object_collapse_type<T>::type());}
//template<typename T> static std::string make_type_name () { return demangle(typeid(T).name());}
_object() {_init(); };
// pass a const & or a && (in which case a move will be used, provided that T has a move constructor)
template<typename T>
_object ( T && obj, std::string const & name){ delegate_constructor( new typename std::remove_cv<typename std::remove_reference<T>::type>::type(std::forward<T>(obj)), name); }
private:
template<typename T> void delegate_constructor( T * obj, std::string const & name) {
p = std::shared_ptr<void> (obj);
type_code_ = type_code<T>();
name_ = name;
clone_ = [obj,name]() { return _object( *obj, name);} ;
//clone_ = [this,obj]() { return _object::factory( *obj);} ; //clone_ = [obj]() { return _object( *obj, "");} ;
//h5_w = [obj](h5::group const &F, std::string const &Name)->void { h5_write(F,Name, *obj);};
h5_w = h5::make_h5_write(obj);// either call h5_write or synthetize one into a string using boost serialization
//static_assert(h5::has_h5_write<T>::value, "oops");
serialize_ = [obj](){ return triqs::serialize(*obj);};
print_ = [obj](std::ostream & out ){out << *obj;};
// CHECK if std::bind would lead to less code bloat ??
_init();
}
public:
_object(_object const & x ) { *this = x; _init(); }
_object(_object && c) { *this= std::move(c); _init();}
friend void swap (_object & a, _object & b) {
#define SWAP(A) swap(a.A,b.A)
SWAP(p); std::SWAP(type_code_); SWAP(clone_); SWAP(h5_w); SWAP(serialize_); SWAP(print_); SWAP(name_);
#undef SWAP
}
_object & operator = (_object && c) { swap(*this, c); return *this; }
_object & operator = (_object const & x ) { if (x.p) *this = x.clone_(); else *this = _object(); return *this; }
_object & operator = (_object & x ) { if (x.p) *this = x.clone_(); else *this = _object(); return *this; }
// the last one is needed since otherwise the template for _object & is taken !
// alternatively disable the template for object ...
template <typename RHS> _object & operator=(RHS && rhs) {
typedef typename _object_collapse_type<RHS>::type storage_t;
*this = _object (storage_t(std::forward<RHS>(rhs)),this->name());
register_type<storage_t>::invoke();
//register_type<typename std::remove_reference<RHS>::type>::invoke();
return *this;
}
// special treatment for const char *: fall back to string
_object & operator=(const char * rhs) { *this = std::string(rhs); return *this;}
bool is_empty() const { return bool(p);}
std::string const & name() const { return name_;}
void set_name(std::string const & n) { name_ = n;}
friend bool have_same_type( _object const & a, _object const & b) { return a.type_code_ == b.type_code_;}
template<typename T> bool has_type() const { return type_code_ == type_code<T>();}
const void * get_void_ptr() const { return p.get();}
void * get_void_ptr() { return p.get();}
// implemented later, since it needs the extract function ...
#define CAST_OPERATOR(r, data, T) operator T () const;
BOOST_PP_SEQ_FOR_EACH(CAST_OPERATOR, nil , TRIQS_UTIL_OPAQUE_OBJECT_PREDEFINED_CAST);
#undef CAST_OPERATOR
friend std::ostream & operator << (std::ostream & out, _object const & p) { if (p.is_empty()) p.print_(out); else out<< "empty"; return out;}
friend void h5_write ( h5::group F, std::string const & Name, _object const & obj){ obj.h5_w(F,Name); };
friend void h5_read ( h5::group F, std::string const & Name, _object & obj);
friend std::string get_triqs_hdf5_data_scheme(_object const&) { return "";}
std::string type_name() const { return type_code_to_type_name[type_code_];}
// ----- Boost serialisation
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
std::string s = serialize_();
ar << boost::serialization::make_nvp("type_name", type_code_to_type_name[type_code_]);
ar << boost::serialization::make_nvp("seria_str", s);
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
std::string s, tnn;
ar >> boost::serialization::make_nvp("type_name", tnn);
ar >> boost::serialization::make_nvp("seria_str", s);
auto it = type_name_to_type_code.find(tnn);
if (it== type_name_to_type_code.end())
TRIQS_RUNTIME_ERROR << " Can not deserialize the type "<< tnn<< "\n Did you forget to register it ?";
*this = code_to_deserialization_fnts[it->second](s);
}
BOOST_SERIALIZATION_SPLIT_MEMBER();
// ----- Deserialization (boost, h5) table : type_code -> deserialization function
// init will register the most common types.
static std::map<size_t, std::function<_object(std::string const &)>> code_to_deserialization_fnts;
static std::map<size_t, std::function<_object(h5::group const &, std::string const &)>> code_to_h5_read_fnts;
static std::map<hid_t, size_t > h5_type_to_c_equivalence;
static std::map<std::pair<size_t,int>, size_t > code_element_rank_to_code_array;
static std::map<std::string, size_t > h5_scheme_to_code;
static std::map<size_t, std::string > type_code_to_type_name;
static std::map<std::string, size_t > type_name_to_type_code;
static bool initialized;
static void _init();
static bool is_initialised(size_t code) { return type_code_to_type_name.find(code) != type_code_to_type_name.end();}
template <typename T> struct register_type;
private: // native type
template <typename T>
static void register_native_type() {
if (register_type<T>::invoke()) return;
h5_type_to_c_equivalence[h5::h5_type_from_C(T()).getId()] = type_code<T>();
}
};// object class
// --------------------- type registering ---------------------------------
template <typename T> struct _object::register_type<const T> : _object::register_type<T>{};
template <typename T> struct _object::register_type {
static bool invoke() { // returns true if already registered
size_t code = type_code<T>();
if (is_initialised(code)) return true;
type_code_to_type_name[code] = make_type_name<T>();
type_name_to_type_code[make_type_name<T>()]= code;
code_to_deserialization_fnts[code] = [](std::string const &s) { return _object( triqs::deserialize<T>(s),"");};
code_to_h5_read_fnts[code] = [](h5::group const &f,std::string const &s) ->_object { T n; h5::make_h5_read(&n)(f,s); return _object(std::move(n),"");};
//code_to_h5_read_fnts[code] = [](h5::group const &f,std::string const &s) ->_object { T n; h5_read(f,s,n); return _object(std::move(n),true);};
auto h5_scheme = get_triqs_hdf5_data_scheme(T());
if (h5_scheme != "") h5_scheme_to_code[h5_scheme] = code;
//std::cerr << " registering " << type_code_to_type_name[code] << "h5 scheme "<< h5_scheme<< std::endl ;
return false;
}
};
// special case for array, we need to fill one more table
template<typename T, int R> struct _object::register_type<arrays::array_c<T,R>>{
static bool invoke() {
typedef arrays::array_c<T,R> A;
typedef arrays::array<T,R> Aa;
if (is_initialised(type_code<A>())) return true;
type_code_to_type_name[type_code<A>()] = make_type_name<A>();
type_name_to_type_code[make_type_name<A>()]= type_code<A>();
code_to_deserialization_fnts[type_code<A>()] = [](std::string const &s) { return _object( triqs::deserialize<A>(s),"");};
//code_to_h5_read_fnts[type_code<A>()] = [](h5::group const &f,std::string const &s) ->_object { A n; h5_read(f,s,n); return _object(std::move(n),true);};
code_to_h5_read_fnts[type_code<A>()] = [](h5::group const &f,std::string const &s) ->_object { Aa a; h5::make_h5_read(&a)(f,s); return _object(A(a),"");};
code_element_rank_to_code_array[std::make_pair(type_code<T>(), R)] = type_code<A>();
auto h5_scheme = get_triqs_hdf5_data_scheme(Aa());
if (h5_scheme != "") h5_scheme_to_code[h5_scheme] = type_code<A>();
return false;
}
};
// --------------------- arithmetic operations are deleted for _object ---------------------------------
#define DELETE_OP(op)\
template <typename LHS, typename RHS> \
typename std::enable_if< std::is_same<LHS,_object>::value || std::is_same<RHS,_object>::value>::type\
operator op( LHS const &, RHS const &) = delete;
DELETE_OP(+); DELETE_OP(-); DELETE_OP(*); DELETE_OP(/);
#undef DELETE_OP
// --------------------- extraction with strict type checking for python ---------------------------------
template<typename T> struct extract_strict {
typedef typename _object_collapse_type<T>::type T_stored;
static bool is_possible (_object const &obj) { return obj.has_type<T_stored>(); }
static T invoke(_object const &obj) {
if (! is_possible(obj))
TRIQS_RUNTIME_ERROR<<"extraction of "<< obj.name() << "impossible : type mismatch. Got a "<<obj.type_name()<< " while I am supposed to extract a "<<_object::make_type_name<T>();
return T(* static_cast<const T_stored*>(obj.get_void_ptr()));
}
};
// --------------------- extraction with more relaxed type checking for C++ ---------------------------------
template<typename T> T extract(_object const &obj);
template<typename T> T lex_cast_from_string(_object const &obj) {
std::string s = extract<std::string>(obj);
try { return boost::lexical_cast<T>(s); }
catch(boost::bad_lexical_cast &) { TRIQS_RUNTIME_ERROR << " extraction : can not read the string "<<s <<" into a "<< _object::make_type_name<T>(); }
}
template<typename T> struct lexical_cast_make_sense : std::is_arithmetic<T>{};
//template<typename T> constexpr bool lexical_cast_make_sense() { return std::is_arithmetic<T>::value; }
template<typename T> T extract1(_object const &obj, std::false_type) {
typedef typename _object_collapse_type<T>::type coll_t;
if (! obj.has_type<coll_t>())
TRIQS_RUNTIME_ERROR<<"extraction of "<< obj.name() << " impossible : type mismatch. Got "<<obj.type_name()<< ", while I am supposed to extract a "<<_object::make_type_name<T>();
return * static_cast<const coll_t*>(obj.get_void_ptr());
}
template<typename T> T extract1(_object const &obj, std::true_type) {
// if T is not a string, and object contains a string, attempt lexical_cast
if ((!std::is_same<T,std::string>::value) && (obj.has_type<std::string>())) { return lex_cast_from_string<T>(obj); }
return extract1<T>(obj, std::false_type());
}
template<typename T> T extract(_object const &obj) {
return extract1<T>(obj, std::integral_constant<bool,lexical_cast_make_sense<T>::value>());
}
template<> // specialize for double since we can make int -> double conversion
inline double extract(_object const & obj) {
if (obj.has_type<double>()) return * static_cast<const double*>(obj.get_void_ptr());
if (obj.has_type<std::string>()) {return lex_cast_from_string<double>(obj); }
#define TRANSFORM_TYPE(T) if (obj.has_type<T>()) return extract<T>(obj)
TRANSFORM_TYPE(int);
//TRANSFORM_TYPE(unsigned int);
TRANSFORM_TYPE(long);
//TRANSFORM_TYPE(unsigned long);
TRANSFORM_TYPE(short);
//TRANSFORM_TYPE(unsigned short);
TRANSFORM_TYPE(long long);
//TRANSFORM_TYPE(unsigned long long);
//TRANSFORM_TYPE(float);
#undef TRANSFORM_TYPE
TRIQS_RUNTIME_ERROR<<"extraction of "<< obj.name() << " impossible : type mismatch. Got "<<obj.type_name()<< ", while I am supposed to extract a double";
}
// template<> // special case to size_t
// inline size_t extract(_object const & obj) { return extract<long>(obj);}
// --------------- _object cast op implementation ---------------------------------------
#define CAST_OPERATOR(r, data, T) inline _object::operator T () const{ return extract<T>(*this);}
BOOST_PP_SEQ_FOR_EACH(CAST_OPERATOR, nil , TRIQS_UTIL_OPAQUE_OBJECT_PREDEFINED_CAST);
#undef CAST_OPERATOR
}}
#endif