Skip to content

Commit f31a24e

Browse files
authored
Voronoi analysis using Voronota-LT (#444)
* Add voronotalt headers * Add voronoi analysis * Add test * Make voronotalt header a system header * Update documentation * DOI to voronota-lt * Detect and log PBC information * Support PBC
1 parent b7a218e commit f31a24e

20 files changed

+3400
-5
lines changed

docs/_docs/analysis.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,22 @@ is filed to disk, `sasa_histogram.dat`.
364364
`file` | Optionally stream area for each `nstep` to file (`.dat|.dat.gz`)
365365
`radius=1.4` | Probe radius (Å)
366366

367+
### Voronoi Tessellation (experimental)
368+
369+
Performs Voronoi tessellation to detect contacts and surface areas using
370+
the [Voronota-LT library](https://doi.org/10/mq8k).
371+
Only the total surface area is currently reported, but more options are planned for future
372+
releases.
373+
The algorithm is significantly faster than the above `sasa` analysis.
374+
375+
`voronoi` | Description
376+
-------------- | ---------------------------
377+
`nstep` | Interval between samples
378+
`nskip=0` | Number of initial steps excluded from the analysis
379+
`file` | Optionally stream surface area for each `nstep` to file (`.dat|.dat.gz`)
380+
`radius=1.4` | Probe radius (Å)
381+
382+
367383
## Charge Properties
368384

369385
### Molecular Multipoles

docs/schema.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,23 @@ properties:
12631263
nskip: {type: integer, default: 0, description: Number of steps to initially skip}
12641264
required: [dL, molecule, nstep]
12651265
additionalProperties: false
1266+
1267+
voronoi:
1268+
description: "Voronoi tessellation using Voronota"
1269+
type: object
1270+
properties:
1271+
nstep: {type: integer, description: "Interval between samples"}
1272+
nskip: {type: integer, default: 0, description: "Initial steps to skip"}
1273+
radius:
1274+
type: number
1275+
default: 1.4
1276+
description: "Probe radius (Å)"
1277+
file:
1278+
type: string
1279+
pattern: "(.*?)\\.(dat|dat.gz)$"
1280+
description: "Streaming filename (.dat|.dat.gz)"
1281+
required: [nstep]
1282+
additionalProperties: false
12661283

12671284
widom:
12681285
description: "Widom ghost particle insertion"

examples/sasa/sasa.out.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@
66
"nstep": 1,
77
"radius": 2.0,
88
"reference": "doi:10/dbjh",
9-
"relative time": 0.592,
109
"samples": 1,
1110
"slices_per_atom": 20,
1211
"⟨SASA²⟩-⟨SASA⟩²": 0.0,
1312
"⟨SASA⟩": 2177.145143881812
1413
}
14+
},
15+
{
16+
"voronota": {
17+
"nstep": 1,
18+
"radius": 2.0,
19+
"reference": "doi:10/mq8k",
20+
"samples": 1,
21+
"⟨SASA²⟩-⟨SASA⟩²": 0.0,
22+
"⟨SASA⟩": 2186.5484868984963
23+
}
1524
}
1625
],
1726
"compiler": "Apple LLVM 15.0.0 (clang-1500.0.40.1)",

examples/sasa/sasa.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ analysis:
2626
molecule: mymolecule
2727
radius: 2.0
2828
nstep: 1
29+
- voronoi:
30+
file: voronoi_sasa.dat
31+
radius: 2.0
32+
nstep: 1
2933

src/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
88
target_link_libraries(project_options INTERFACE ZLIB::ZLIB)
99
target_link_libraries(project_options INTERFACE ${CMAKE_THREAD_LIBS_INIT})
1010

11+
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/voronotalt)
12+
1113
# ========== pre-compiler system headers ==========
1214

1315
option(ENABLE_PCH "Enable Precompiled Headers" OFF)
@@ -60,7 +62,7 @@ set(objs actions.cpp analysis.cpp average.cpp atomdata.cpp auxiliary.cpp bonds.c
6062
chainmove.cpp clustermove.cpp core.cpp forcemove.cpp units.cpp energy.cpp externalpotential.cpp
6163
geometry.cpp group.cpp io.cpp molecule.cpp montecarlo.cpp move.cpp mpicontroller.cpp
6264
particle.cpp penalty.cpp potentials.cpp random.cpp reactioncoordinate.cpp regions.cpp rotate.cpp sasa.cpp
63-
scatter.cpp smart_montecarlo.cpp space.cpp speciation.cpp spherocylinder.cpp tensor.cpp)
65+
scatter.cpp smart_montecarlo.cpp space.cpp speciation.cpp spherocylinder.cpp tensor.cpp voronota.cpp)
6466

6567
set(hdrs actions.h analysis.h average.h atomdata.h auxiliary.h bonds.h celllist.h celllistimpl.h
6668
chainmove.h clustermove.h core.h forcemove.h energy.h externalpotential.h geometry.h group.h io.h

src/analysis.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
#include <cereal/archives/binary.hpp>
2626
#include <range/v3/numeric/accumulate.hpp>
2727
#include <range/v3/view/zip.hpp>
28-
2928
#include <iomanip>
3029
#include <iostream>
3130
#include <memory>
@@ -194,6 +193,8 @@ std::unique_ptr<Analysis> createAnalysis(const std::string& name, const json& j,
194193
return std::make_unique<VirtualVolumeMove>(j, spc, pot);
195194
} else if (name == "virtualtranslate") {
196195
return std::make_unique<VirtualTranslate>(j, spc, pot);
196+
} else if (name == "voronoi") {
197+
return std::make_unique<Voronota>(j, spc);
197198
} else if (name == "widom") {
198199
return std::make_unique<WidomInsertion>(j, spc, pot);
199200
} else if (name == "xtcfile") {
@@ -1879,14 +1880,14 @@ SASAAnalysis::SASAAnalysis(const double probe_radius, const int slices_per_atom,
18791880
break;
18801881
}
18811882
setPolicy(selected_policy);
1883+
cite = "doi:10/dbjh";
18821884
}
18831885

18841886
SASAAnalysis::SASAAnalysis(const json& j, const Space& spc)
18851887
: SASAAnalysis(j.value("radius", 1.4_angstrom), j.value("slices", 20), j.value("resolution", 50.0),
18861888
j.value("policy", Policies::INVALID), spc) {
18871889
from_json(j);
18881890
policy->from_json(j);
1889-
cite = "doi:10/dbjh";
18901891
}
18911892

18921893
void SASAAnalysis::_to_json(json& json_ouput) const {

src/analysis.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class ElectricPotential : public Analysis {
262262
unsigned int calculations_per_sample_event = 1;
263263
std::string file_prefix; //!< Output filename prefix for potential histogram and correlation
264264
struct Target {
265-
Point position; //!< Target position
265+
Point position; //!< Target position
266266
Average<double> mean_potential; //!< mean potential at position
267267
std::unique_ptr<SparseHistogram<double>> potential_histogram; //!< Histogram of observed potentials
268268
};
@@ -1086,6 +1086,36 @@ class SavePenaltyEnergy : public Analysis {
10861086
SavePenaltyEnergy(const json& j, const Space& spc, const Energy::Hamiltonian& pot);
10871087
};
10881088

1089+
/**
1090+
* @brief Analysis of Vorononoi tessellation using the Voronota-LT library
1091+
*
1092+
* @todo Currently only SASA information is reported in the output. Add more information!
1093+
*
1094+
* https://doi.org/10/mq8k
1095+
*/
1096+
class Voronota : public Analysis {
1097+
private:
1098+
struct Averages {
1099+
Average<double> area;
1100+
Average<double> area_squared;
1101+
}; //!< Placeholder class for average properties
1102+
Averages average_data; //!< Stores all averages for the selected molecule
1103+
1104+
std::unique_ptr<std::ostream> output_stream; //!< output stream
1105+
double probe_radius; //!< radius of the probe sphere
1106+
std::string filename; //!< output file name
1107+
bool use_pbc = false; //!< Is the cell periodic?
1108+
1109+
void _to_json(json& j) const override;
1110+
void _from_json(const json& input) override;
1111+
void _to_disk() override;
1112+
void _sample() override;
1113+
1114+
public:
1115+
Voronota(const json& j, const Space& spc);
1116+
Voronota(double probe_radius, const Space& spc);
1117+
};
1118+
10891119
} // namespace analysis
10901120

10911121
} // namespace Faunus

src/voronota.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include "analysis.h"
2+
#include "mpicontroller.h"
3+
#include <voronotalt/voronotalt.h>
4+
#include <numeric>
5+
6+
namespace Faunus::analysis {
7+
8+
Voronota::Voronota(double probe_radius, const Faunus::Space& spc)
9+
: Analysis(spc, "voronota")
10+
, probe_radius(probe_radius) {
11+
cite = "doi:10/mq8k";
12+
auto n_pbc = spc.geometry.asSimpleGeometry()->boundary_conditions.isPeriodic().count();
13+
switch (n_pbc) {
14+
case 0:
15+
faunus_logger->debug("{}: No PBC detected", name);
16+
use_pbc = false;
17+
break;
18+
case 3:
19+
faunus_logger->debug("{}: 3D PBC detected", name);
20+
use_pbc = true;
21+
break;
22+
default:
23+
faunus_logger->warn("{}: Non-uniform PBC is currently ignored - be careful!", name);
24+
use_pbc = false;
25+
}
26+
}
27+
28+
Voronota::Voronota(const Faunus::json& input, const Faunus::Space& spc)
29+
: Voronota(input.value("radius", 1.4_angstrom), spc) {
30+
from_json(input);
31+
}
32+
33+
void Voronota::_from_json(const Faunus::json& input) {
34+
if (filename = input.value("file", ""s); !filename.empty()) {
35+
output_stream = IO::openCompressedOutputStream(MPI::prefix + filename);
36+
*output_stream << "# step SASA\n";
37+
}
38+
}
39+
40+
void Voronota::_to_json(json& json_ouput) const {
41+
if (!average_data.area.empty()) {
42+
json_ouput = {{"⟨SASA⟩", average_data.area.avg()},
43+
{"⟨SASA²⟩-⟨SASA⟩²", average_data.area_squared.avg() - std::pow(average_data.area.avg(), 2)}};
44+
}
45+
json_ouput["radius"] = probe_radius;
46+
}
47+
48+
void Voronota::_to_disk() {
49+
if (output_stream) {
50+
output_stream->flush();
51+
}
52+
}
53+
54+
void Voronota::_sample() {
55+
using voronotalt::SimplePoint;
56+
using namespace ranges::cpp20::views;
57+
58+
// Convert single `Particle` to Voronota's `SimpleSphere`
59+
auto to_sphere = [&](const Particle& p) -> voronotalt::SimpleSphere {
60+
return {{p.pos.x(), p.pos.y(), p.pos.z()}, 0.5 * p.traits().sigma + probe_radius};
61+
};
62+
const auto spheres = spc.activeParticles() | transform(to_sphere) | ranges::to_vector;
63+
64+
voronotalt::RadicalTessellation::Result result;
65+
66+
if (use_pbc) {
67+
auto to_point = [](const Point& p) -> SimplePoint { return {p.x(), p.y(), p.z()}; };
68+
const auto corner = 0.5 * spc.geometry.getLength();
69+
const std::vector<SimplePoint> box_corners = {to_point(-corner), to_point(corner)};
70+
voronotalt::RadicalTessellation::construct_full_tessellation(spheres, box_corners, result);
71+
} else {
72+
voronotalt::RadicalTessellation::construct_full_tessellation(spheres, result);
73+
}
74+
75+
auto areas = result.cells_summaries | transform([](auto& s) { return s.sas_area; });
76+
const auto total_area = std::accumulate(areas.begin(), areas.end(), 0.0);
77+
average_data.area.add(total_area);
78+
average_data.area_squared.add(total_area * total_area);
79+
80+
if (output_stream) {
81+
*output_stream << this->getNumberOfSteps() << " " << total_area << "\n";
82+
}
83+
}
84+
85+
} // namespace Faunus::analysis

src/voronotalt/LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2023 Kliment Olechnovic
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

src/voronotalt/VERSION.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Voronota-LT version 0.9.2

0 commit comments

Comments
 (0)