91 const Topology& top,
const std::vector<QMStateType>& carriers) {
93 const Eigen::Matrix3d& box = top.
getBox();
94 Eigen::Matrix3d off = box;
95 off.diagonal().setZero();
96 if (off.cwiseAbs().maxCoeff() > 1
e-9 * box.diagonal().maxCoeff()) {
97 throw std::runtime_error(
98 "networkfit: the box of the state file must be orthorhombic");
100 d.
box = box.diagonal();
102 const std::vector<Segment>& segs = top.
Segments();
105 throw std::runtime_error(
"networkfit: need at least two segments");
108 d.
energies.assign(carriers.size(), std::vector<double>(n));
109 d.
U_xX_nN.assign(carriers.size(), 0.0);
110 d.
U_nX_nN.assign(carriers.size(), 0.0);
111 d.
U_xN_xX.assign(carriers.size(), 0.0);
112 for (
Index i = 0; i < n; ++i) {
114 if (seg.
getId() != i) {
115 throw std::runtime_error(
116 "networkfit: segment ids must be consecutive and start at 0");
118 Eigen::Vector3d p = seg.
getPos();
119 for (
Index k = 0; k < 3; ++k) {
120 p[k] -= d.
box[k] * std::floor(p[k] / d.
box[k]);
121 if (p[k] >= d.
box[k]) {
126 for (std::size_t c = 0; c < carriers.size(); ++c) {
135 p.
i = pair->Seg1()->getId();
136 p.
j = pair->Seg2()->getId();
137 p.
dist = pair->R().norm();
139 p.
J2.push_back(pair->getJeff2(s));
141 d.
pairs.push_back(std::move(p));
143 if (d.
pairs.empty()) {
144 throw std::runtime_error(
"networkfit: the state file has no pairs");
273 const std::vector<Eigen::Vector3d>& points,
const Eigen::Vector3d& box,
274 const std::vector<Index>& ls,
double bin,
double rmax) {
276 const Index lmax = *std::max_element(ls.begin(), ls.end());
278 throw std::runtime_error(
"networkfit: l larger than the number of sites");
281 std::vector<Index> nearest(n * lmax);
282#pragma omp parallel for schedule(dynamic, 64)
283 for (
Index i = 0; i < n; ++i) {
284 std::vector<Index> nb = cells.
Nearest(i, lmax);
285 std::copy(nb.begin(), nb.end(), nearest.begin() + i * lmax);
287 Curve bins = Bins(bin, rmax);
289 std::vector<Curve> result(ls.size(), bins);
290 std::vector<Index> rank(n, -1);
291 std::vector<double>
hist(lmax);
292 for (
Index i = 0; i < n; ++i) {
293 for (
Index k = 0; k < lmax; ++k) {
294 rank[nearest[i * lmax + k]] = k;
296 cells.
Within(points[i], rmax,
297 [&](
Index j,
const Eigen::Vector3d&,
double dist) {
299 if (j <= i || b >= nbins) {
304 std::fill(
hist.begin(),
hist.end(), 0.0);
305 for (
Index k = 0; k < lmax; ++k) {
306 Index ri = rank[nearest[j * lmax + k]];
308 hist[std::max(ri, k)] += 1.0;
311 std::partial_sum(
hist.begin(),
hist.end(),
hist.begin());
312 for (std::size_t q = 0; q < ls.size(); ++q) {
313 Curve& c = result[q];
315 c.
value[b] +=
hist[ls[q] - 1] / double(ls[q]);
318 for (
Index k = 0; k < lmax; ++k) {
319 rank[nearest[i * lmax + k]] = -1;
322 for (
Curve& c : result) {
323 for (
Index b = 0; b < nbins; ++b) {
324 if (c.count[b] > 0.0) {
325 c.value[b] /= c.count[b];
333 const Curve& Oc,
double& w_b,
336 double bb = 0.0, bc = 0.0, cc = 0.0, bk = 0.0, ck = 0.0, n = 0.0;
337 for (std::size_t q = 0; q < kappa.
r.size(); ++q) {
338 double w = kappa.
count.empty() ? 1.0 : kappa.
count[q];
342 double b = Ob.
value[q];
343 double c = Oc.
value[q];
344 double k = kappa.
value[q];
353 throw std::runtime_error(
"networkfit: no pairs for the correlation fit");
358 auto cost = [&](
double x,
double y) {
360 for (std::size_t q = 0; q < kappa.
r.size(); ++q) {
361 double w = kappa.
count.empty() ? 1.0 : kappa.
count[q];
370 auto clamp01 = [](
double x) {
return std::min(1.0, std::max(0.0, x)); };
371 std::vector<std::array<double, 2>> cand;
372 cand.push_back({0.0, 0.0});
373 double det = bb * cc - bc * bc;
374 if (det > 1
e-14 * bb * cc) {
375 cand.push_back({(bk * cc - ck * bc) / det, (ck * bb - bk * bc) / det});
378 cand.push_back({clamp01(bk / bb), 0.0});
381 cand.push_back({0.0, clamp01(ck / cc)});
384 double a2 = bb - 2.0 * bc + cc;
386 double t = clamp01((bk - ck - bc + cc) / a2);
387 cand.push_back({t, 1.0 - t});
389 double best = std::numeric_limits<double>::infinity();
390 for (
const auto& x : cand) {
391 if (x[0] < 0.0 || x[1] < 0.0 || x[0] + x[1] > 1.0 + 1
e-12) {
394 double f = cost(x[0], x[1]);
401 return std::max(0.0, best);
461 const Eigen::Vector3d& box =
data_.box;
462 const Index probes = std::max(
Index(4000), n);
463 const std::uint64_t probe_seed = 4711;
466 std::vector<double> contact =
469 auto grid = [step](
double rmax) {
470 std::vector<double> r;
471 for (
double x = 0.0; x <= rmax; x += step) {
476 std::vector<double> rD = grid(1.5 * *std::max_element(nn.begin(), nn.end()));
477 std::vector<double> rH =
478 grid(1.5 * *std::max_element(contact.begin(), contact.end()));
479 std::vector<double> sorted = nn;
480 std::sort(sorted.begin(), sorted.end());
481 const double q01 = sorted[std::size_t(0.01 *
double(n - 1))];
482 const double q50 = sorted[std::size_t(0.5 *
double(n - 1))];
483 const double rg = std::min(HalfBox(box), 4.0 * q50);
485 fit.
H =
Cdf(contact, rH);
491 auto process = [&base](
const std::array<double, 3>& x) {
498 auto msd = [](
const Curve& a,
const Curve& b) {
500 for (std::size_t k = 0; k < a.
value.size(); ++k) {
501 s += (a.
value[k] - b.value[k]) * (a.
value[k] - b.value[k]);
503 return s / double(a.
value.size());
505 auto evaluate = [&](
const std::array<double, 3>& x,
PointFit* store) {
508 for (
Curve* c : {&
D, &
H, &g}) {
509 std::fill(c->value.begin(), c->value.end(), 0.0);
512 std::vector<std::array<Curve, 3>> curves(nr);
513 std::vector<char> saturated(nr, 0);
514#pragma omp parallel for schedule(dynamic, 1)
515 for (
Index k = 0; k < nr; ++k) {
516 std::vector<Eigen::Vector3d> pos;
519 std::vector<double> radii;
521 }
catch (
const std::runtime_error&) {
529 if (std::any_of(saturated.begin(), saturated.end(),
530 [](
char s) { return s != 0; })) {
533 for (
Index k = 0; k < nr; ++k) {
534 const Curve& d = curves[k][0];
535 const Curve& h = curves[k][1];
536 const Curve& gg = curves[k][2];
537 double f = 1.0 / double(nr);
538 for (std::size_t q = 0; q <
D.value.size(); ++q) {
539 D.value[q] += f * d.
value[q];
541 for (std::size_t q = 0; q <
H.value.size(); ++q) {
542 H.value[q] += f * h.
value[q];
544 for (std::size_t q = 0; q < g.value.size(); ++q) {
545 g.value[q] += f * gg.
value[q];
548 if (store !=
nullptr) {
553 return msd(
D, fit.
D) + msd(
H, fit.
H) + 0.25 * msd(g, fit.
g);
557 std::array<double, 3> best{0.0, 0.0, 0.0};
558 double fbest = std::numeric_limits<double>::infinity();
559 Index evaluations = 0;
560 for (
double a : {0.6, 0.8, 1.0}) {
561 for (
double b : {0.25, 0.5, 0.75, 1.0}) {
562 for (
double c : {0.05, 0.15}) {
564 std::array<double, 3> x{rh, b * std::max(q50 - rh, 0.1 * q50), c * q50};
565 double f = evaluate(x,
nullptr);
574 std::array<std::array<double, 3>, 4> s;
575 std::array<double, 4> fs;
578 for (
Index k = 0; k < 3; ++k) {
580 s[k + 1][k] += 0.1 * q50;
581 fs[k + 1] = evaluate(s[k + 1],
nullptr);
584 auto combine = [](
const std::array<double, 3>& a,
585 const std::array<double, 3>& b,
double t) {
587 return std::array<double, 3>{a[0] + t * (b[0] - a[0]),
588 a[1] + t * (b[1] - a[1]),
589 a[2] + t * (b[2] - a[2])};
591 while (evaluations <
settings_.max_evaluations) {
592 std::array<Index, 4> o{0, 1, 2, 3};
593 std::sort(o.begin(), o.end(),
594 [&fs](
Index a,
Index b) { return fs[a] < fs[b]; });
596 for (
Index k = 1; k < 4; ++k) {
597 for (
Index d = 0; d < 3; ++d) {
598 size = std::max(size, std::abs(s[o[k]][d] - s[o[0]][d]));
601 if (size < 1
e-3 * q50) {
604 std::array<double, 3> centroid{0.0, 0.0, 0.0};
605 for (
Index k = 0; k < 3; ++k) {
606 for (
Index d = 0; d < 3; ++d) {
607 centroid[d] += s[o[k]][d] / 3.0;
610 const Index w = o[3];
611 std::array<double, 3> xr = combine(centroid, s[w], -1.0);
612 double fr = evaluate(xr,
nullptr);
615 std::array<double, 3> xe = combine(centroid, s[w], -2.0);
616 double fe = evaluate(xe,
nullptr);
625 }
else if (fr < fs[o[2]]) {
629 bool outside = fr < fs[w];
630 std::array<double, 3> xc = combine(centroid, outside ? xr : s[w], 0.5);
631 double fc = evaluate(xc,
nullptr);
633 if (fc < std::min(fr, fs[w])) {
638 for (
Index k = 1; k < 4; ++k) {
639 s[o[k]] = combine(s[o[0]], s[o[k]], 0.5);
640 fs[o[k]] = evaluate(s[o[k]],
nullptr);
646 Index ib =
Index(std::min_element(fs.begin(), fs.end()) - fs.begin());
708 Index carrier, std::vector<double>& standardized)
const {
711 std::vector<double> x, y;
712 std::vector<Index> index;
713 for (std::size_t k = 0; k <
data_.pairs.size(); ++k) {
715 if (p.
J2[carrier] > 0.0) {
716 x.push_back(p.
dist * nm);
717 y.push_back(Log10J2eV(p.
J2[carrier]));
718 index.push_back(
Index(k));
724 standardized.assign(
data_.pairs.size(),
725 std::numeric_limits<double>::quiet_NaN());
727 throw std::runtime_error(
"networkfit: carrier " +
728 data_.carriers[carrier].ToString() +
729 " has fewer than three nonzero couplings");
731 std::array<double, 2> m = Line(x, y);
732 std::vector<double> e2(x.size());
733 for (std::size_t k = 0; k < x.size(); ++k) {
734 double e = y[k] - m[0] - m[1] * x[k];
737 std::array<double, 2> v = Line(x, e2);
738 double xmin = *std::min_element(x.begin(), x.end());
739 double xmax = *std::max_element(x.begin(), x.end());
740 if (!(v[0] + v[1] * xmin > 0.0) || !(v[0] + v[1] * xmax > 0.0)) {
745 if (Mean(e2) < 1
e-12) {
753 std::vector<double> u(x.size());
754 for (std::size_t k = 0; k < x.size(); ++k) {
755 double var = std::max(v[0] + v[1] * x[k], 1
e-300);
756 u[k] = (y[k] - m[0] - m[1] * x[k]) / std::sqrt(var);
757 standardized[index[k]] = u[k];
760 double var = Variance(u, mu);
761 double m3 = 0.0, m4 = 0.0;
763 m3 += std::pow(z - mu, 3);
764 m4 += std::pow(z - mu, 4);
766 m3 /= double(u.size());
767 m4 /= double(u.size());
768 fit.
skewness = var > 0.0 ? m3 / std::pow(var, 1.5) : 0.0;
772 std::sort(u.begin(), u.end());
773 for (
Index k = 0; k < K; ++k) {
774 double pos = (double(k) + 0.5) /
double(K) * double(u.size()) - 0.5;
775 pos = std::min(std::max(pos, 0.0),
double(u.size() - 1));
776 std::size_t lo = std::size_t(std::floor(pos));
777 std::size_t hi = std::min(lo + 1, u.size() - 1);
778 double t = pos - double(lo);
788 const std::size_t ncar =
data_.carriers.size();
808 double rc =
settings_.correlation_cutoff > 0.0
810 : HalfBox(
data_.box);
812 std::vector<char> trap_site(n, 0);
815 throw std::runtime_error(
"networkfit: invalid trap input");
818 if (i < 0 || i >= n) {
819 throw std::runtime_error(
"networkfit: trap site out of range");
825 n -
Index(std::count(trap_site.begin(), trap_site.end(), 1));
827 throw std::runtime_error(
"networkfit: no bulk sites left");
829 std::vector<std::vector<double>> bulk_energies(ncar);
830 for (std::size_t c = 0; c < ncar; ++c) {
831 for (
Index i = 0; i < n; ++i) {
833 bulk_energies[c].push_back(
data_.energies[c][i]);
837 std::vector<std::vector<double>> z(ncar);
839 for (std::size_t c = 0; c < ncar; ++c) {
841 e.mean = Mean(bulk_energies[c]);
842 e.sigma = std::sqrt(Variance(bulk_energies[c],
e.mean));
850 for (
Index i = 0; i < n; ++i) {
852 trap_site[i] ? 0.0 : (
data_.energies[c][i] -
e.mean) /
e.sigma);
856 std::vector<Index> lb, lc, ls;
858 if (l >= 1 && l <= n) {
863 if (l >= 1 && l <= n) {
867 if (any && (lb.empty() || lc.empty())) {
868 throw std::runtime_error(
869 "networkfit: the l_b and l_c grids have no values in [1, sites]");
871 std::vector<Curve> overlaps;
874 ls.insert(ls.end(), lc.begin(), lc.end());
875 std::sort(ls.begin(), ls.end());
876 ls.erase(std::unique(ls.begin(), ls.end()), ls.end());
880 return overlaps[std::lower_bound(ls.begin(), ls.end(), l) - ls.begin()];
884 for (
Index i = 0; i < n; ++i) {
885 s += z[0][i] * z[1][i];
894 std::vector<std::vector<double>> expected(ncar);
896 for (std::size_t c = 0; c < ncar; ++c) {
905 const double r2 = rho * rho;
906 if (c == 1 && std::abs(rho) > 0.999) {
913 expected[c] = expected[0];
919 std::vector<double> base(nb, 0.0);
923 auto model = [&](
Index b,
Index cc,
double wb,
double wc,
924 std::vector<double>& m) {
925 double wa = std::max(0.0, 1.0 - wb - wc);
926 double cw = (wa + wb * double(b) + wc * double(cc)) /
double(nbulk);
928 for (
Index q = 0; q < nb; ++q) {
929 m[q] = r2 * base[q] + (1.0 - r2) * (wb * overlap(b).value[q] +
930 wc * overlap(cc).value[q]);
932 return r2 * cbar0 + (1.0 - r2) * cw;
934 auto sample_residual = [&](
const std::vector<double>& m,
double cbar) {
935 double s = 0.0, w = 0.0;
936 for (
Index q = 0; q < nb; ++q) {
937 double d = (m[q] - cbar) / (1.0 - cbar) -
e.kappa.value[q];
938 s +=
e.kappa.count[q] * d * d;
939 w +=
e.kappa.count[q];
943 double best = std::numeric_limits<double>::infinity();
944 std::vector<double> m;
946 e.l_c_scan.r.assign(lc.begin(), lc.end());
947 e.l_c_scan.value.assign(lc.size(),
948 std::numeric_limits<double>::infinity());
950 for (std::size_t ic = 0; ic < lc.size(); ++ic) {
951 const Index cc = lc[ic];
955 double wb = 0.0, wc = 0.0, cbar = 0.0;
956 for (
Index iter = 0; iter < 8; ++iter) {
957 for (
Index q = 0; q < nb; ++q) {
958 double unbiased = cbar + (1.0 - cbar) *
e.kappa.value[q];
959 target.
value[q] = (unbiased - r2 * base[q]) / (1.0 - r2);
961 FitWeights(target, overlap(b), overlap(cc), wb, wc);
962 cbar = model(b, cc, wb, wc, m);
964 double f = sample_residual(m, cbar);
965 e.l_c_scan.value[ic] = std::min(
e.l_c_scan.value[ic], std::sqrt(f));
975 if (best == std::numeric_limits<double>::infinity()) {
976 throw std::runtime_error(
977 "networkfit: no l_c larger than l_b in the grids");
979 e.w_a = std::max(0.0, 1.0 -
e.w_b -
e.w_c);
980 double cbar = model(
e.l_b,
e.l_c,
e.w_b,
e.w_c, expected[c]);
985 e.l_b_at_edge = lb.size() > 1 &&
e.l_b == lb.back();
986 e.l_c_at_edge = lc.size() > 1 &&
e.l_c == lc.back();
987 e.rms = std::sqrt(sample_residual(expected[c], cbar));
988 e.kappa_model =
e.kappa;
989 for (
Index q = 0; q < nb; ++q) {
990 e.kappa_model.value[q] = (expected[c][q] - cbar) / (1.0 - cbar);
996 e.sigma_model =
e.fitted ?
e.sigma / std::sqrt(1.0 -
e.cbar) : 0.0;
1000 std::vector<std::vector<double>> standardized(ncar);
1001 for (std::size_t c = 0; c < ncar; ++c) {
1005 std::vector<std::array<double, 2>> both;
1006 for (std::size_t k = 0; k <
data_.pairs.size(); ++k) {
1007 if (std::isfinite(standardized[0][k]) &&
1008 std::isfinite(standardized[1][k])) {
1009 both.push_back({standardized[0][k], standardized[1][k]});
1012 if (both.size() > 2) {
1014 std::vector<std::vector<double>> score(2,
1015 std::vector<double>(both.size()));
1016 for (
Index a = 0; a < 2; ++a) {
1017 std::vector<std::size_t> o(both.size());
1018 std::iota(o.begin(), o.end(), 0);
1019 std::sort(o.begin(), o.end(), [&](std::size_t p, std::size_t q) {
1020 return both[p][a] < both[q][a];
1022 for (std::size_t r = 0; r < o.size(); ++r) {
1027 double sxy = 0.0, sxx = 0.0, syy = 0.0;
1028 for (std::size_t k = 0; k < both.size(); ++k) {
1029 sxy += score[0][k] * score[1][k];
1030 sxx += score[0][k] * score[0][k];
1031 syy += score[1][k] * score[1][k];
1034 std::min(1.0, std::max(-1.0, sxy / std::sqrt(sxx * syy)));
1038 for (std::size_t c = 0; c < ncar; ++c) {
1059 trap.
density = 1.0 / double(n);
1062 std::vector<double> o(ncar);
1063 for (std::size_t c = 0; c < ncar; ++c) {
1068 opt.
traps.push_back(trap);