Commit ea28ebdd by Maarten L. Hekkelman

optimized caching of items in mmcif::Atom

parent 3ba46893
......@@ -36,6 +36,7 @@
#include <regex>
#include <set>
#include <sstream>
#include <shared_mutex>
#include "cif++/CifUtils.hpp"
......@@ -142,13 +143,13 @@ class Item
Item() {}
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
Item(const std::string &name, const T &value)
Item(const std::string_view name, const T &value)
: mName(name)
, mValue(std::to_string(value))
{
}
Item(const std::string &name, const std::string &value)
Item(const std::string_view name, const std::string_view value)
: mName(name)
, mValue(value)
{
......@@ -221,7 +222,7 @@ class Datablock
using iterator = CategoryList::iterator;
using const_iterator = CategoryList::const_iterator;
Datablock(const std::string &name);
Datablock(const std::string_view name);
~Datablock();
Datablock(const Datablock &) = delete;
......@@ -230,8 +231,6 @@ class Datablock
std::string getName() const { return mName; }
void setName(const std::string &n) { mName = n; }
std::string firstItem(const std::string &tag) const;
iterator begin() { return mCategories.begin(); }
iterator end() { return mCategories.end(); }
......@@ -256,7 +255,7 @@ class Datablock
void write(std::ostream &os);
// convenience function, add a line to the software category
void add_software(const std::string &name, const std::string &classification,
void add_software(const std::string_view name, const std::string &classification,
const std::string &versionNr, const std::string &versionDate);
friend bool operator==(const Datablock &lhs, const Datablock &rhs);
......@@ -264,7 +263,8 @@ class Datablock
friend std::ostream& operator<<(std::ostream &os, const Datablock &data);
private:
std::list<Category> mCategories;
CategoryList mCategories; // LRU
mutable std::shared_mutex mLock;
std::string mName;
Validator *mValidator;
Datablock *mNext;
......@@ -1816,12 +1816,12 @@ class Category
friend class Row;
friend class detail::ItemReference;
Category(Datablock &db, const std::string &name, Validator *Validator);
Category(Datablock &db, const std::string_view name, Validator *Validator);
Category(const Category &) = delete;
Category &operator=(const Category &) = delete;
~Category();
const std::string name() const { return mName; }
const std::string &name() const { return mName; }
using iterator = iterator_impl<Row>;
using const_iterator = iterator_impl<const Row>;
......
......@@ -51,26 +51,43 @@ const double
// float x, y, z;
// tie(x, y, z) = atom.loc();
template<typename F>
template <typename F>
struct PointF
{
typedef F FType;
FType mX, mY, mZ;
PointF() : mX(0), mY(0), mZ(0) {}
PointF(FType x, FType y, FType z) : mX(x), mY(y), mZ(z) {}
PointF()
: mX(0)
, mY(0)
, mZ(0)
{
}
PointF(FType x, FType y, FType z)
: mX(x)
, mY(y)
, mZ(z)
{
}
template<typename PF>
PointF(const PointF<PF>& pt)
template <typename PF>
PointF(const PointF<PF> &pt)
: mX(static_cast<F>(pt.mX))
, mY(static_cast<F>(pt.mY))
, mZ(static_cast<F>(pt.mZ)) {}
, mZ(static_cast<F>(pt.mZ))
{
}
#if HAVE_LIBCLIPPER
PointF(const clipper::Coord_orth& pt): mX(pt[0]), mY(pt[1]), mZ(pt[2]) {}
PointF(const clipper::Coord_orth &pt)
: mX(pt[0])
, mY(pt[1])
, mZ(pt[2])
{
}
PointF& operator=(const clipper::Coord_orth& rhs)
PointF &operator=(const clipper::Coord_orth &rhs)
{
mX = rhs[0];
mY = rhs[1];
......@@ -79,8 +96,8 @@ struct PointF
}
#endif
template<typename PF>
PointF& operator=(const PointF<PF>& rhs)
template <typename PF>
PointF &operator=(const PointF<PF> &rhs)
{
mX = static_cast<F>(rhs.mX);
mY = static_cast<F>(rhs.mY);
......@@ -88,19 +105,19 @@ struct PointF
return *this;
}
FType& getX() { return mX; }
FType &getX() { return mX; }
FType getX() const { return mX; }
void setX(FType x) { mX = x; }
FType& getY() { return mY; }
FType &getY() { return mY; }
FType getY() const { return mY; }
void setY(FType y) { mY = y; }
FType& getZ() { return mZ; }
FType &getZ() { return mZ; }
FType getZ() const { return mZ; }
void setZ(FType z) { mZ = z; }
PointF& operator+=(const PointF& rhs)
PointF &operator+=(const PointF &rhs)
{
mX += rhs.mX;
mY += rhs.mY;
......@@ -109,7 +126,7 @@ struct PointF
return *this;
}
PointF& operator+=(FType d)
PointF &operator+=(FType d)
{
mX += d;
mY += d;
......@@ -118,7 +135,7 @@ struct PointF
return *this;
}
PointF& operator-=(const PointF& rhs)
PointF &operator-=(const PointF &rhs)
{
mX -= rhs.mX;
mY -= rhs.mY;
......@@ -127,7 +144,7 @@ struct PointF
return *this;
}
PointF& operator-=(FType d)
PointF &operator-=(FType d)
{
mX -= d;
mY -= d;
......@@ -136,7 +153,7 @@ struct PointF
return *this;
}
PointF& operator*=(FType rhs)
PointF &operator*=(FType rhs)
{
mX *= rhs;
mY *= rhs;
......@@ -144,7 +161,7 @@ struct PointF
return *this;
}
PointF& operator/=(FType rhs)
PointF &operator/=(FType rhs)
{
mX /= rhs;
mY /= rhs;
......@@ -163,7 +180,7 @@ struct PointF
return length;
}
void rotate(const boost::math::quaternion<FType>& q)
void rotate(const boost::math::quaternion<FType> &q)
{
boost::math::quaternion<FType> p(0, mX, mY, mZ);
......@@ -181,17 +198,17 @@ struct PointF
}
#endif
operator std::tuple<const FType&, const FType&, const FType&>() const
operator std::tuple<const FType &, const FType &, const FType &>() const
{
return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
}
operator std::tuple<FType&,FType&,FType&>()
operator std::tuple<FType &, FType &, FType &>()
{
return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
}
bool operator==(const PointF& rhs) const
bool operator==(const PointF &rhs) const
{
return mX == rhs.mX and mY == rhs.mY and mZ == rhs.mZ;
}
......@@ -211,45 +228,45 @@ struct PointF
typedef PointF<float> Point;
typedef PointF<double> DPoint;
template<typename F>
inline std::ostream& operator<<(std::ostream& os, const PointF<F>& pt)
template <typename F>
inline std::ostream &operator<<(std::ostream &os, const PointF<F> &pt)
{
os << '(' << pt.mX << ',' << pt.mY << ',' << pt.mZ << ')';
return os;
}
template<typename F>
inline PointF<F> operator+(const PointF<F>& lhs, const PointF<F>& rhs)
template <typename F>
inline PointF<F> operator+(const PointF<F> &lhs, const PointF<F> &rhs)
{
return PointF<F>(lhs.mX + rhs.mX, lhs.mY + rhs.mY, lhs.mZ + rhs.mZ);
}
template<typename F>
inline PointF<F> operator-(const PointF<F>& lhs, const PointF<F>& rhs)
template <typename F>
inline PointF<F> operator-(const PointF<F> &lhs, const PointF<F> &rhs)
{
return PointF<F>(lhs.mX - rhs.mX, lhs.mY - rhs.mY, lhs.mZ - rhs.mZ);
}
template<typename F>
inline PointF<F> operator-(const PointF<F>& pt)
template <typename F>
inline PointF<F> operator-(const PointF<F> &pt)
{
return PointF<F>(-pt.mX, -pt.mY, -pt.mZ);
}
template<typename F>
inline PointF<F> operator*(const PointF<F>& pt, F f)
template <typename F>
inline PointF<F> operator*(const PointF<F> &pt, F f)
{
return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
}
template<typename F>
inline PointF<F> operator*(F f, const PointF<F>& pt)
template <typename F>
inline PointF<F> operator*(F f, const PointF<F> &pt)
{
return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
}
template<typename F>
inline PointF<F> operator/(const PointF<F>& pt, F f)
template <typename F>
inline PointF<F> operator/(const PointF<F> &pt, F f)
{
return PointF<F>(pt.mX / f, pt.mY / f, pt.mZ / f);
}
......@@ -257,17 +274,16 @@ inline PointF<F> operator/(const PointF<F>& pt, F f)
// --------------------------------------------------------------------
// several standard 3d operations
template<typename F>
inline double DistanceSquared(const PointF<F>& a, const PointF<F>& b)
template <typename F>
inline double DistanceSquared(const PointF<F> &a, const PointF<F> &b)
{
return
(a.mX - b.mX) * (a.mX - b.mX) +
return (a.mX - b.mX) * (a.mX - b.mX) +
(a.mY - b.mY) * (a.mY - b.mY) +
(a.mZ - b.mZ) * (a.mZ - b.mZ);
}
template<typename F>
inline double Distance(const PointF<F>& a, const PointF<F>& b)
template <typename F>
inline double Distance(const PointF<F> &a, const PointF<F> &b)
{
return sqrt(
(a.mX - b.mX) * (a.mX - b.mX) +
......@@ -275,22 +291,22 @@ inline double Distance(const PointF<F>& a, const PointF<F>& b)
(a.mZ - b.mZ) * (a.mZ - b.mZ));
}
template<typename F>
inline F DotProduct(const PointF<F>& a, const PointF<F>& b)
template <typename F>
inline F DotProduct(const PointF<F> &a, const PointF<F> &b)
{
return a.mX * b.mX + a.mY * b.mY + a.mZ * b.mZ;
}
template<typename F>
inline PointF<F> CrossProduct(const PointF<F>& a, const PointF<F>& b)
template <typename F>
inline PointF<F> CrossProduct(const PointF<F> &a, const PointF<F> &b)
{
return PointF<F>(a.mY * b.mZ - b.mY * a.mZ,
a.mZ * b.mX - b.mZ * a.mX,
a.mX * b.mY - b.mX * a.mY);
}
template<typename F>
double Angle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3)
template <typename F>
double Angle(const PointF<F> &p1, const PointF<F> &p2, const PointF<F> &p3)
{
PointF<F> v1 = p1 - p2;
PointF<F> v2 = p3 - p2;
......@@ -298,8 +314,8 @@ double Angle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3)
return std::acos(DotProduct(v1, v2) / (v1.length() * v2.length())) * 180 / kPI;
}
template<typename F>
double DihedralAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
template <typename F>
double DihedralAngle(const PointF<F> &p1, const PointF<F> &p2, const PointF<F> &p3, const PointF<F> &p4)
{
PointF<F> v12 = p1 - p2; // vector from p2 to p1
PointF<F> v43 = p4 - p3; // vector from p3 to p4
......@@ -325,8 +341,8 @@ double DihedralAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>&
return result;
}
template<typename F>
double CosinusAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
template <typename F>
double CosinusAngle(const PointF<F> &p1, const PointF<F> &p2, const PointF<F> &p3, const PointF<F> &p4)
{
PointF<F> v12 = p1 - p2;
PointF<F> v34 = p3 - p4;
......@@ -340,7 +356,7 @@ double CosinusAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p
return result;
}
template<typename F>
template <typename F>
auto DistancePointToLine(const PointF<F> &l1, const PointF<F> &l2, const PointF<F> &p)
{
auto line = l2 - l1;
......@@ -355,7 +371,7 @@ auto DistancePointToLine(const PointF<F> &l1, const PointF<F> &l2, const PointF<
// a random direction with a distance randomly chosen from a normal
// distribution with a stddev of offset.
template<typename F>
template <typename F>
PointF<F> Nudge(PointF<F> p, F offset);
// --------------------------------------------------------------------
......@@ -363,25 +379,28 @@ PointF<F> Nudge(PointF<F> p, F offset);
Quaternion Normalize(Quaternion q);
std::tuple<double,Point> QuaternionToAngleAxis(Quaternion q);
Point Centroid(std::vector<Point>& Points);
Point CenterPoints(std::vector<Point>& Points);
Quaternion AlignPoints(const std::vector<Point>& a, const std::vector<Point>& b);
double RMSd(const std::vector<Point>& a, const std::vector<Point>& b);
std::tuple<double, Point> QuaternionToAngleAxis(Quaternion q);
Point Centroid(const std::vector<Point> &Points);
Point CenterPoints(std::vector<Point> &Points);
Quaternion AlignPoints(const std::vector<Point> &a, const std::vector<Point> &b);
double RMSd(const std::vector<Point> &a, const std::vector<Point> &b);
// --------------------------------------------------------------------
// Helper class to generate evenly divided Points on a sphere
// we use a fibonacci sphere to calculate even distribution of the dots
template<int N>
template <int N>
class SphericalDots
{
public:
enum { P = 2 * N + 1 };
typedef typename std::array<Point,P> array_type;
enum
{
P = 2 * N + 1
};
typedef typename std::array<Point, P> array_type;
typedef typename array_type::const_iterator iterator;
static SphericalDots& instance()
static SphericalDots &instance()
{
static SphericalDots sInstance;
return sInstance;
......@@ -418,11 +437,10 @@ class SphericalDots
}
private:
array_type mPoints;
double mWeight;
};
typedef SphericalDots<50> SphericalDots_50;
}
} // namespace mmcif
......@@ -109,12 +109,12 @@ class Atom
float occupancy() const;
template <typename T>
T property(const std::string &name) const;
T property(const std::string_view name) const;
void property(const std::string &name, const std::string &value);
void property(const std::string_view name, const std::string &value);
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
void property(const std::string &name, const T &value)
void property(const std::string_view name, const T &value)
{
property(name, std::to_string(value));
}
......@@ -404,7 +404,7 @@ class File : public std::enable_shared_from_this<File>
File(const File &) = delete;
File &operator=(const File &) = delete;
cif::Datablock& createDatablock(const std::string &name);
cif::Datablock& createDatablock(const std::string_view name);
void load(const std::filesystem::path &path);
void save(const std::filesystem::path &path);
......
......@@ -33,6 +33,7 @@
#include <stack>
#include <tuple>
#include <unordered_map>
#include <shared_mutex>
#include <filesystem>
......@@ -351,7 +352,7 @@ namespace detail
// --------------------------------------------------------------------
// Datablock implementation
Datablock::Datablock(const std::string &name)
Datablock::Datablock(const std::string_view name)
: mName(name)
, mValidator(nullptr)
, mNext(nullptr)
......@@ -363,43 +364,57 @@ Datablock::~Datablock()
delete mNext;
}
std::string Datablock::firstItem(const std::string &tag) const
auto Datablock::emplace(std::string_view name) -> std::tuple<iterator, bool>
{
std::string result;
// LRU code
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(tag);
std::shared_lock lock(mLock);
for (auto &cat : mCategories)
bool isNew = true;
auto i = begin();
while (i != end())
{
if (iequals(cat.name(), catName))
if (iequals(name, i->name()))
{
for (auto row : cat)
isNew = false;
if (i != begin())
{
result = row[itemName].as<std::string>();
break;
auto n = std::next(i);
mCategories.splice(begin(), mCategories, i, n);
}
break;
}
++i;
}
return result;
}
if (isNew)
mCategories.emplace(begin(), *this, std::string(name), mValidator);
auto Datablock::emplace(std::string_view name) -> std::tuple<iterator, bool>
{
bool isNew = false;
iterator i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); });
return std::make_tuple(begin(), isNew);
if (i == end())
{
isNew = true;
i = mCategories.emplace(end(), *this, std::string(name), mValidator);
}
return std::make_tuple(i, isNew);
// bool isNew = false;
// iterator i = find_if(begin(), end(), [name](const Category &cat) -> bool
// { return iequals(cat.name(), name); });
// if (i == end())
// {
// isNew = true;
// i = mCategories.emplace(begin(), *this, std::string(name), mValidator);
// }
// else if (i != begin())
// {
// auto n = std::next(i);
// mCategories.splice(begin(), mCategories, i, n);
// i = mCategories.begin();
// }
// return std::make_tuple(i, isNew);
}
Category &Datablock::operator[](std::string_view name)
......@@ -411,14 +426,13 @@ Category &Datablock::operator[](std::string_view name)
Category *Datablock::get(std::string_view name)
{
auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); });
return i == end() ? nullptr : &*i;
return &operator[](name);
}
const Category *Datablock::get(std::string_view name) const
{
std::shared_lock lock(mLock);
auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
{ return iequals(cat.name(), name); });
......@@ -427,6 +441,8 @@ const Category *Datablock::get(std::string_view name) const
bool Datablock::isValid()
{
std::shared_lock lock(mLock);
if (mValidator == nullptr)
throw std::runtime_error("Validator not specified");
......@@ -438,20 +454,26 @@ bool Datablock::isValid()
void Datablock::validateLinks() const
{
std::shared_lock lock(mLock);
for (auto &cat : *this)
cat.validateLinks();
}
void Datablock::setValidator(Validator *v)
{
std::shared_lock lock(mLock);
mValidator = v;
for (auto &cat : *this)
cat.setValidator(v);
}
void Datablock::add_software(const std::string &name, const std::string &classification, const std::string &versionNr, const std::string &versionDate)
void Datablock::add_software(const std::string_view name, const std::string &classification, const std::string &versionNr, const std::string &versionDate)
{
std::shared_lock lock(mLock);
Category &cat = operator[]("software");
auto ordNr = cat.size() + 1;
// TODO: should we check this ordinal number???
......@@ -465,12 +487,16 @@ void Datablock::add_software(const std::string &name, const std::string &classif
void Datablock::getTagOrder(std::vector<std::string> &tags) const
{
std::shared_lock lock(mLock);
for (auto &cat : *this)
cat.getTagOrder(tags);
}
void Datablock::write(std::ostream &os)
{
std::shared_lock lock(mLock);
os << "data_" << mName << std::endl
<< "# " << std::endl;
......@@ -505,6 +531,8 @@ void Datablock::write(std::ostream &os)
void Datablock::write(std::ostream &os, const std::vector<std::string> &order)
{
std::shared_lock lock(mLock);
os << "data_" << mName << std::endl
<< "# " << std::endl;
......@@ -580,6 +608,9 @@ void Datablock::write(std::ostream &os, const std::vector<std::string> &order)
bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
{
std::shared_lock lockA(dbA.mLock);
std::shared_lock lockB(dbB.mLock);
std::vector<std::string> catA, catB;
for (auto &cat : dbA)
......@@ -1311,7 +1342,7 @@ RowSet &RowSet::orderBy(std::initializer_list<std::string> items)
// --------------------------------------------------------------------
Category::Category(Datablock &db, const std::string &name, Validator *Validator)
Category::Category(Datablock &db, const std::string_view name, Validator *Validator)
: mDb(db)
, mName(name)
, mValidator(Validator)
......
......@@ -976,7 +976,8 @@ void DictParser::parseSaveFrame()
if (isCategorySaveFrame)
{
std::string category = dict.firstItem("_category.id");
std::string category;
cif::tie(category) = dict["category"].front().get("id");
std::vector<std::string> keys;
for (auto k: dict["category_key"])
......@@ -991,7 +992,8 @@ void DictParser::parseSaveFrame()
else
{
// if the type code is missing, this must be a pointer, just skip it
std::string typeCode = dict.firstItem("_item_type.code");
std::string typeCode;
cif::tie(typeCode) = dict["item_type"].front().get("code");
const ValidateType* tv = nullptr;
if (not (typeCode.empty() or typeCode == "?"))
......@@ -1001,7 +1003,8 @@ void DictParser::parseSaveFrame()
for (auto e: dict["item_enumeration"])
ess.insert(e["value"].as<std::string>());
std::string defaultValue = dict.firstItem("_item_default.value");
std::string defaultValue;
cif::tie(defaultValue) = dict["item_default"].front().get("value");
bool defaultIsNull = false;
if (defaultValue.empty())
{
......
......@@ -102,14 +102,14 @@ Point CenterPoints(std::vector<Point>& Points)
return t;
}
Point Centroid(std::vector<Point>& Points)
Point Centroid(const std::vector<Point>& pts)
{
Point result;
for (Point& pt : Points)
for (auto &pt : pts)
result += pt;
result /= static_cast<float>(Points.size());
result /= static_cast<float>(pts.size());
return result;
}
......
......@@ -216,9 +216,9 @@ struct AtomImpl
, mLocation(i.mLocation)
, mRefcount(1)
, mRow(i.mRow)
, mCachedRefs(i.mCachedRefs)
, mCompound(i.mCompound)
, mRadius(i.mRadius)
, mCachedProperties(i.mCachedProperties)
, mSymmetryCopy(i.mSymmetryCopy)
, mClone(true)
// , mRTop(i.mRTop), mD(i.mD)
......@@ -270,9 +270,9 @@ struct AtomImpl
, mLocation(loc)
, mRefcount(1)
, mRow(impl.mRow)
, mCachedRefs(impl.mCachedRefs)
, mCompound(impl.mCompound)
, mRadius(impl.mRadius)
, mCachedProperties(impl.mCachedProperties)
, mSymmetryCopy(true)
, mSymmetryOperator(sym_op)
{
......@@ -338,9 +338,9 @@ struct AtomImpl
if (not mClone)
{
mRow["Cartn_x"] = p.getX();
mRow["Cartn_y"] = p.getY();
mRow["Cartn_z"] = p.getZ();
property("Cartn_x", std::to_string(p.getX()));
property("Cartn_y", std::to_string(p.getY()));
property("Cartn_z", std::to_string(p.getZ()));
}
// boost::format kPosFmt("%.3f");
......@@ -382,26 +382,31 @@ struct AtomImpl
return mRadius;
}
const std::string &property(const std::string &name) const
const std::string property(const std::string_view name) const
{
static std::string kEmptyString;
auto i = mCachedProperties.find(name);
if (i == mCachedProperties.end())
for (auto &&[tag, ref] : mCachedRefs)
{
auto v = mRow[name];
if (v.empty())
return kEmptyString;
return mCachedProperties[name] = v.as<std::string>();
if (tag == name)
return ref.as<std::string>();
}
else
return i->second;
mCachedRefs.emplace_back(name, mRow[name]);
return std::get<1>(mCachedRefs.back()).as<std::string>();
}
void property(const std::string &name, const std::string &value)
void property(const std::string_view name, const std::string &value)
{
mRow[name] = value;
for (auto &&[tag, ref] : mCachedRefs)
{
if (tag != name)
continue;
ref = value;
return;
}
mCachedRefs.emplace_back(name, mRow[name]);
std::get<1>(mCachedRefs.back()) = value;
}
int compare(const AtomImpl &b) const
......@@ -432,9 +437,11 @@ struct AtomImpl
Point mLocation;
int mRefcount;
cif::Row mRow;
mutable std::vector<std::tuple<std::string,cif::detail::ItemReference>> mCachedRefs;
mutable const Compound *mCompound = nullptr;
float mRadius = std::nanf("4");
mutable std::map<std::string, std::string> mCachedProperties;
bool mSymmetryCopy = false;
bool mClone = false;
......@@ -533,25 +540,25 @@ const cif::Row Atom::getRowAniso() const
}
template <>
std::string Atom::property<std::string>(const std::string &name) const
std::string Atom::property<std::string>(const std::string_view name) const
{
return impl()->property(name);
}
template <>
int Atom::property<int>(const std::string &name) const
int Atom::property<int>(const std::string_view name) const
{
auto v = impl()->property(name);
return v.empty() ? 0 : stoi(v);
}
template <>
float Atom::property<float>(const std::string &name) const
float Atom::property<float>(const std::string_view name) const
{
return stof(impl()->property(name));
}
void Atom::property(const std::string &name, const std::string &value)
void Atom::property(const std::string_view name, const std::string &value)
{
impl()->property(name, value);
}
......@@ -1736,7 +1743,7 @@ File::~File()
delete mImpl;
}
cif::Datablock& File::createDatablock(const std::string &name)
cif::Datablock& File::createDatablock(const std::string_view name)
{
auto db = new cif::Datablock(name);
......@@ -1807,8 +1814,8 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options)
}
if (mAtoms.empty())
throw std::runtime_error("No atoms loaded, refuse to continue");
std::cerr << "Warning: no atoms loaded" << std::endl;
else
loadData();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment