GooseEPM v0.11.0
Loading...
Searching...
No Matches
System.h
Go to the documentation of this file.
1
7#ifndef GOOSEEPM_SYSTEM_H
8#define GOOSEEPM_SYSTEM_H
9
10#include <algorithm>
11#include <chrono>
12#include <prrng.h>
13#include <xtensor/xset_operation.hpp>
14#include <xtensor/xsort.hpp>
15
16#include "config.h"
17#include "version.h"
18
19namespace GooseEPM {
20
21namespace detail {
22
26inline std::string get_namespace()
27{
28 std::string ret = "GooseEPM";
29#ifdef GOOSEEPM_USE_XTENSOR_PYTHON
30 return ret + ".";
31#else
32 return ret + "::";
33#endif
34}
35
40template <class T>
41inline std::string shape_to_string(const T& shape)
42{
43 std::string ret = "[";
44 for (size_t i = 0; i < shape.size(); ++i) {
45 ret += std::to_string(shape[i]);
46 if (i < shape.size() - 1) {
47 ret += ", ";
48 }
49 }
50 ret += "]";
51 return ret;
52}
53
60inline void unravel_index(
61 ptrdiff_t idx,
62 const std::array<ptrdiff_t, 2>& strides,
63 std::array<ptrdiff_t, 2>& indices
64)
65{
66 indices[0] = idx / strides[0];
67 indices[1] = idx % strides[0];
68}
69
77template <class T>
78inline bool check_distances(const T& distance)
79{
80 using value_type = typename T::value_type;
81 static_assert(std::numeric_limits<value_type>::is_integer, "Distances must be integer");
82 static_assert(std::numeric_limits<value_type>::is_signed, "Distances must be signed");
83
84 if (distance.dimension() != 1) {
85 return false;
86 }
87
88 value_type lower = xt::amin(distance)();
89 value_type upper = xt::amax(distance)() + 1;
90 auto d = xt::arange<value_type>(lower, upper);
91
92 if (!xt::all(xt::in1d(distance, d))) {
93 return false;
94 }
95
96 if (static_cast<value_type>(distance.size()) != upper - lower) {
97 return false;
98 }
99
100 return true;
101}
102
126template <class T>
127inline T create_distance_lookup(const T& distance)
128{
129 using value_type = typename T::value_type;
130 GOOSEEPM_REQUIRE(detail::check_distances(distance), std::out_of_range);
131
132 value_type lower = xt::amin(distance)();
133 value_type upper = xt::amax(distance)() + 1;
134
135 value_type N = static_cast<value_type>(distance.size());
136 T ret = xt::empty<value_type>({2 * N - 1});
137
138 for (value_type i = 0; i < upper; ++i) {
139 ret(i) = xt::argmax(xt::equal(distance, i))();
140 }
141 for (value_type i = upper; i < N; ++i) {
142 ret(i) = xt::argmax(xt::equal(distance, i - N))();
143 }
144 for (value_type i = -1; i >= lower; --i) {
145 ret.periodic(i) = xt::argmax(xt::equal(distance, i))();
146 }
147 for (value_type i = lower; i > -N; --i) {
148 ret.periodic(i) = xt::argmax(xt::equal(distance, N + i))();
149 }
150
151 return ret;
152}
153
163template <size_t Dim, class T, class D>
164inline T reorganise_popagator(const T& propagator, const D& distances)
165{
166 static_assert(Dim > 0 && Dim < 3, "Only 1d/2d systems are supported");
167 GOOSEEPM_REQUIRE(propagator.dimension() == Dim, std::out_of_range);
168
169 T ret = xt::empty_like(propagator);
170
171 if constexpr (Dim == 1) {
172 auto d = xt::argsort(distances[0]);
173 for (size_t i = 0; i < propagator.size(); ++i) {
174 ret(i) = propagator(d(i));
175 }
176 }
177 else if constexpr (Dim == 2) {
178 auto dr = xt::argsort(distances[0]);
179 auto dc = xt::argsort(distances[1]);
180
181 for (size_t i = 0; i < propagator.shape(0); ++i) {
182 for (size_t j = 0; j < propagator.shape(1); ++j) {
183 ret(i, j) = propagator(dr(i), dc(j));
184 }
185 }
186 }
187
188 return ret;
189}
190
198template <size_t Dim, class T, class D>
199inline T popagator_fix_stress(const T& propagator, const D& distances)
200{
201 static_assert(Dim > 0 && Dim < 3, "Only 1d/2d systems are supported");
202 GOOSEEPM_REQUIRE(propagator.dimension() == Dim, std::out_of_range);
203
204 T ret = propagator;
205
206 if constexpr (Dim == 1) {
207 auto i = xt::argwhere(xt::equal(distances[0], 0));
208 GOOSEEPM_REQUIRE(i.size() == 1, std::out_of_range);
209 for (size_t k = 0; k < 20; ++k) { // loop to increase precision
210 ret -= xt::mean(ret);
211 ret(i[0][0]) = -1.0;
212 }
213 }
214 else if constexpr (Dim == 2) {
215 auto i = xt::argwhere(xt::equal(distances[0], 0));
216 auto j = xt::argwhere(xt::equal(distances[1], 0));
217 GOOSEEPM_REQUIRE(i.size() == 1, std::out_of_range);
218 GOOSEEPM_REQUIRE(j.size() == 1, std::out_of_range);
219 for (size_t k = 0; k < 20; ++k) { // loop to increase precision
220 ret -= xt::mean(ret);
221 ret(i[0][0], j[0][0]) = -1.0;
222 }
223 }
224
225 return reorganise_popagator<Dim>(ret, distances);
226}
227
235template <size_t Dim, class T, class D>
236inline T popagator_fix_strain(const T& propagator, const D& distances)
237{
238 static_assert(Dim > 0 && Dim < 3, "Only 1d/2d systems are supported");
239 GOOSEEPM_REQUIRE(propagator.dimension() == Dim, std::out_of_range);
240
241 T ret = propagator;
242
243 if constexpr (Dim == 1) {
244 auto i = xt::argwhere(xt::equal(distances[0], 0));
245 GOOSEEPM_REQUIRE(i.size() == 1, std::out_of_range);
246 for (size_t k = 0; k < 20; ++k) { // loop to increase precision
247 ret -= xt::mean(ret) + 1.0 / static_cast<double>(ret.size());
248 ret(i[0][0]) = -1.0;
249 }
250 }
251 else if constexpr (Dim == 2) {
252 auto i = xt::argwhere(xt::equal(distances[0], 0));
253 auto j = xt::argwhere(xt::equal(distances[1], 0));
254 GOOSEEPM_REQUIRE(i.size() == 1, std::out_of_range);
255 GOOSEEPM_REQUIRE(j.size() == 1, std::out_of_range);
256 for (size_t k = 0; k < 20; ++k) { // loop to increase precision
257 ret -= xt::mean(ret) + 1.0 / static_cast<double>(ret.size());
258 ret(i[0][0], j[0][0]) = -1.0;
259 }
260 }
261
262 return reorganise_popagator<Dim>(ret, distances);
263}
264
274class Default {
275public:
276 Default()
277 {
278 }
279};
280
290class Depinning {
291public:
292 Depinning()
293 {
294 }
295};
296
300class Finite {
301public:
302 Finite()
303 {
304 }
305};
306
332class FixStrain {
333public:
334 FixStrain()
335 {
336 }
337};
338
350class FixStress {
351private:
352 double m_sigbar;
353
354public:
359 FixStress(double sigbar) : m_sigbar(sigbar)
360 {
361 }
362
367 template <class T>
368 void set_average_stress(T& sig)
369 {
370 sig -= xt::mean(sig)() - m_sigbar;
371 }
372
377 void set_sigmabar(double sigbar)
378 {
379 m_sigbar = sigbar;
380 }
381
386 double sigmabar() const
387 {
388 return m_sigbar;
389 }
390};
391
424class Spring {
425private:
426 double m_u;
427 double m_k_div_n;
428 double m_kp1_div_k;
429
430public:
437 Spring(double k, double u, size_t size)
438 : m_u(u), m_k_div_n(k / static_cast<double>(size)), m_kp1_div_k((k + 1.0) / k)
439 {
440 }
441
447 template <class T>
448 void stress_match_frame(T& sig, double depsp_f)
449 {
450 sig -= depsp_f * m_k_div_n;
451 }
452
457 void frame_match_stress(double delta_sigma)
458 {
459 m_u += delta_sigma * m_kp1_div_k;
460 }
461
466 double u() const
467 {
468 return m_u;
469 }
470
475 void set_u(double u)
476 {
477 m_u = u;
478 }
479};
480
481} // namespace detail
482
558template <size_t Dimension, class StressFixer, class Temperature, class FailureRules>
560public:
561 static constexpr size_t Dim = Dimension;
562
563protected:
564 prrng::pcg32 m_gen;
566 std::array<ptrdiff_t, Dim> m_offset;
575 double m_t;
576 double m_alpha;
577 StressFixer* m_stress_fixer;
579 double m_inv_temp;
580 double m_tau;
583 double m_failure_x;
584
585protected:
597 uint64_t seed,
598 double failure_rate,
599 double alpha,
600 bool random_stress,
601 StressFixer* stress_fixer = nullptr
602 )
603 {
604 static_assert(Dim > 0 && Dim < 3, "Only 1d/2d systems are supported");
605
606 m_stress_fixer = stress_fixer;
607 m_temperature = 0.0;
608 m_inv_temp = std::numeric_limits<double>::infinity();
609
610 GOOSEEPM_REQUIRE(propagator.dimension() == Dim, std::out_of_range);
611 for (size_t i = 0; i < Dim; ++i) {
612 GOOSEEPM_REQUIRE(distances[i].size() == propagator.shape(i), std::out_of_range);
613 GOOSEEPM_REQUIRE(detail::check_distances(distances[i]), std::out_of_range);
614 m_offset[i] = xt::amin(distances[i])();
615 }
616 GOOSEEPM_REQUIRE(xt::has_shape(sigmay_mean, sigmay_std.shape()), std::out_of_range);
617
618 if constexpr (std::is_same<StressFixer, detail::FixStrain>::value) {
619 m_propagator = detail::popagator_fix_strain<Dim>(propagator, distances);
620 }
621 else {
622 m_propagator = detail::popagator_fix_stress<Dim>(propagator, distances);
623 }
624
625 m_gen.seed(seed);
626 m_alpha = alpha;
628 m_tau = 1.0 / m_failure_rate;
631 m_sig = xt::zeros_like(m_sigy_mu);
632 m_epsp = xt::zeros_like(m_sigy_mu);
633 m_nfails = xt::zeros<size_t>(m_epsp.shape());
634 m_sigy = xt::empty_like(m_sigy_mu);
635 m_unstable = xt::empty<bool>(m_epsp.shape());
636 m_tau_thermal = xt::empty_like(m_sigy_mu);
637
638 for (size_t i = 0; i < m_sigy.size(); ++i) {
639 this->drawYieldStress(i);
640 }
641
642 if (random_stress) {
643 this->initRandomStress(propagator, distances);
644 }
645 else {
646 this->stress_updated();
647 }
648
649 m_t = 0.0;
650 m_epsp.fill(0.0);
651 m_nfails.fill(0);
652 m_failure_idx = m_epsp.size();
653 m_failure_x = 0.0;
654 }
655
656private:
661 template <class S, class T>
662 void initRandomStress(const S& propagator, const T& distances)
663 {
664 if constexpr (std::is_same<StressFixer, detail::FixStrain>::value) {
665 m_propagator = detail::popagator_fix_stress<Dim>(propagator, distances);
666 }
667
668 this->initSigmaPropogator(0.1);
669
670 if constexpr (std::is_same<StressFixer, detail::FixStrain>::value) {
671 m_propagator = detail::popagator_fix_strain<Dim>(propagator, distances);
672 }
673
674 for (size_t i = 0; i < 20; ++i) {
675 size_t n = this->relaxAthermal();
676 if (n == 0) {
677 return;
678 }
679
680 if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
681 m_stress_fixer->set_u(0.0);
682 }
683 m_sig -= xt::mean(m_sig)();
684 this->stress_updated();
685 }
686 GOOSEEPM_WARNING("Initialisation failed to converge");
687 }
688
693 void stress_updated()
694 {
695 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
696 xt::noalias(m_unstable) = m_sig >= m_sigy;
697 if constexpr (std::is_same<Temperature, detail::Finite>::value) {
698 xt::noalias(m_tau_thermal) = xt::where(
700 m_tau,
701 xt::exp(xt::pow(m_sigy - m_sig, m_alpha) * m_inv_temp) * m_tau
702 );
703 }
704 }
705 else {
706 xt::noalias(m_unstable) = xt::abs(m_sig) >= m_sigy;
707 if constexpr (std::is_same<Temperature, detail::Finite>::value) {
708 xt::noalias(m_tau_thermal) = xt::where(
710 m_tau,
711 xt::exp(xt::pow(m_sigy - xt::abs(m_sig), m_alpha) * m_inv_temp) * m_tau
712 );
713 }
714 }
715 }
716
721 void stress_updated(size_t idx)
722 {
723 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
724 m_unstable.flat(idx) = m_sig.flat(idx) >= m_sigy.flat(idx);
725 }
726 else {
727 m_unstable.flat(idx) = std::abs(m_sig.flat(idx)) >= m_sigy.flat(idx);
728 }
729
730 if constexpr (std::is_same<Temperature, detail::Finite>::value) {
731 if (m_unstable.flat(idx)) {
732 m_tau_thermal.flat(idx) = m_tau;
733 }
734 else {
735 double E;
736 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
737 E = std::pow(m_sigy.flat(idx) - m_sig.flat(idx), m_alpha);
738 }
739 else {
740 E = std::pow(m_sigy.flat(idx) - std::abs(m_sig.flat(idx)), m_alpha);
741 }
742 m_tau_thermal.flat(idx) = std::exp(E * m_inv_temp) * m_tau;
743 }
744 }
745 }
746
751 void drawYieldStress(size_t idx)
752 {
753 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
754 m_sigy.flat(idx) = std::abs(m_gen.normal(m_sigy_mu.flat(idx), m_sigy_std.flat(idx)));
755 }
756 else {
757 m_sigy.flat(idx) = m_gen.normal(m_sigy_mu.flat(idx), m_sigy_std.flat(idx));
758 // avoid negative yield stresses. todo: permanent fix with truncated normal distribution
759 while (m_sigy.flat(idx) < 0) {
760 m_sigy.flat(idx) = m_gen.normal(m_sigy_mu.flat(idx), m_sigy_std.flat(idx));
761 }
762 }
763 }
764
765public:
770 std::string repr() const
771 {
772 std::string ret = detail::get_namespace() + "System";
773
774 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
775 ret += "Depinning";
776 }
777
778 if constexpr (std::is_same<Temperature, detail::Finite>::value) {
779 ret += "Thermal";
780 }
781
782 if constexpr (std::is_same<StressFixer, detail::FixStrain>::value) {
783 ret += "StrainControl";
784 }
785 else if constexpr (std::is_same<StressFixer, detail::FixStress>::value) {
786 ret += "StressControl";
787 }
788 else if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
789 ret += "SpringLoading";
790 }
791
792 return ret + std::to_string(Dim) + " " + detail::shape_to_string(m_sig.shape());
793 }
794
799 const auto& shape() const
800 {
801 return m_epsp.shape();
802 }
803
808 auto size() const
809 {
810 return m_epsp.size();
811 }
812
818 {
819 return m_propagator;
820 }
821
828 {
829 GOOSEEPM_REQUIRE(axis < Dim, std::out_of_range);
830 return m_offset[axis] + xt::arange<ptrdiff_t>(m_propagator.shape(axis));
831 }
832
837 auto alpha() const
838 {
839 return m_alpha;
840 }
841
846 auto failure_rate() const
847 {
848 return m_failure_rate;
849 }
850
855 auto failure_idx() const
856 {
857 return m_failure_idx;
858 }
859
864 auto failure_x() const
865 {
866 return m_failure_x;
867 }
868
873 void set_t(double t)
874 {
875 m_t = t;
876 }
877
882 double t() const
883 {
884 return m_t;
885 }
886
891 void set_state(uint64_t state)
892 {
893 m_gen.restore(state);
894 }
895
900 uint64_t state() const
901 {
902 return m_gen.state();
903 }
904
910 {
911 m_epsp = epsp;
912 }
913
919 {
920 return m_epsp;
921 }
922
931
937 {
938 return m_nfails;
939 }
940
946 {
947 m_sigy = sigmay;
948 this->stress_updated();
949 }
950
956 {
957 return m_sigy;
958 }
959
965 {
966 GOOSEEPM_ASSERT(xt::has_shape(value, m_sigy_mu.shape()), std::out_of_range);
967 m_sigy_mu = value;
968 }
969
975 {
976 return m_sigy_mu;
977 }
978
984 {
985 GOOSEEPM_ASSERT(xt::has_shape(value, m_sigy_std.shape()), std::out_of_range);
986 m_sigy_std = value;
987 }
988
994 {
995 return m_sigy_std;
996 }
997
1008 {
1009 m_sig = sigma;
1010 this->stress_updated();
1011 }
1012
1018 {
1019 return m_sig;
1020 }
1021
1035 {
1036 if constexpr (std::is_same<StressFixer, detail::FixStress>::value) {
1037 m_stress_fixer->set_sigmabar(sigmabar);
1038 m_stress_fixer->set_average_stress(m_sig);
1039 }
1040 else if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
1041 double dsigbar = sigmabar - xt::mean(m_sig)();
1042 m_sig += dsigbar;
1043 m_stress_fixer->frame_match_stress(dsigbar);
1044 }
1045 else {
1046 m_sig -= xt::mean(m_sig)() - sigmabar;
1047 }
1048 this->stress_updated();
1049 }
1050
1055 double sigmabar() const
1056 {
1057 if constexpr (std::is_same<StressFixer, detail::FixStress>::value) {
1058 return m_stress_fixer->sigmabar();
1059 }
1060 else {
1061 return xt::mean(m_sig)();
1062 }
1063 }
1064
1069 double epsframe() const
1070 {
1071 if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
1072 return m_stress_fixer->u();
1073 }
1074 throw std::invalid_argument("Only implemented for Spring");
1075 }
1076
1082 void set_epsframe(double u)
1083 {
1084 if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
1085 return m_stress_fixer->set_u(u);
1086 }
1087 throw std::invalid_argument("Only implemented for Spring");
1088 }
1089
1096 void initSigmaFast(double sigma_std, size_t delta_r)
1097 {
1098 m_sig.fill(0);
1099 ptrdiff_t d = static_cast<ptrdiff_t>(delta_r);
1100 auto dsig = m_gen.normal<decltype(m_sig)>(m_sig.shape(), 0, sigma_std);
1101
1102 if constexpr (Dim == 1) {
1103 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(m_sig.size()); ++i) {
1104 m_sig(i) += dsig(i);
1105 m_sig.periodic(i - d) -= 0.5 * dsig(i);
1106 m_sig.periodic(i + d) -= 0.5 * dsig(i);
1107 }
1108 }
1109 else if constexpr (Dim == 2) {
1110 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(m_sig.shape(0)); ++i) {
1111 for (ptrdiff_t j = 0; j < static_cast<ptrdiff_t>(m_sig.shape(1)); ++j) {
1112 m_sig(i, j) += dsig(i, j);
1113 m_sig.periodic(i - d, j) -= 0.5 * dsig(i, j);
1114 m_sig.periodic(i + d, j) -= 0.5 * dsig(i, j);
1115 m_sig.periodic(i, j - d) -= 0.5 * dsig(i, j);
1116 m_sig.periodic(i, j + d) -= 0.5 * dsig(i, j);
1117 m_sig.periodic(i + d, j + d) += 0.25 * dsig(i, j);
1118 m_sig.periodic(i - d, j + d) += 0.25 * dsig(i, j);
1119 m_sig.periodic(i + d, j - d) += 0.25 * dsig(i, j);
1120 m_sig.periodic(i - d, j - d) += 0.25 * dsig(i, j);
1121 }
1122 }
1123 m_sig *= 0.5;
1124 }
1125 this->stress_updated();
1126 }
1127
1140 void initSigmaPropogator(double sigma_std)
1141 {
1142 m_sig.fill(0);
1143 auto dsig = m_gen.normal<decltype(m_sig)>(m_sig.shape(), 0, sigma_std);
1144
1145 if constexpr (Dim == 1) {
1146 ptrdiff_t ni = static_cast<ptrdiff_t>(m_sig.size());
1147 ptrdiff_t nj = static_cast<ptrdiff_t>(m_propagator.size());
1148 ptrdiff_t dj = m_offset[0];
1149 for (ptrdiff_t i = 0; i < ni; ++i) {
1150 for (ptrdiff_t j = 0; j < nj; ++j) {
1151 m_sig.periodic(i + dj + j) -= dsig(i) * m_propagator(j);
1152 }
1153 }
1154 }
1155 else if constexpr (Dim == 2) {
1156 ptrdiff_t ni = static_cast<ptrdiff_t>(m_sig.shape(0));
1157 ptrdiff_t nj = static_cast<ptrdiff_t>(m_sig.shape(1));
1158 ptrdiff_t nk = static_cast<ptrdiff_t>(m_propagator.shape(0));
1159 ptrdiff_t nl = static_cast<ptrdiff_t>(m_propagator.shape(1));
1160 ptrdiff_t dk = m_offset[0];
1161 ptrdiff_t dl = m_offset[1];
1162
1163 for (ptrdiff_t i = 0; i < ni; ++i) {
1164 for (ptrdiff_t j = 0; j < nj; ++j) {
1165 for (ptrdiff_t k = 0; k < nk; ++k) {
1166 for (ptrdiff_t l = 0; l < nl; ++l) {
1167 m_sig.periodic(i + dk + k, j + dl + l) -=
1168 dsig(i, j) * m_propagator(k, l);
1169 }
1170 }
1171 }
1172 }
1173 }
1174
1175 m_sig /= xt::sqrt(xt::sum(xt::pow(m_propagator, 2.0)));
1176 this->stress_updated();
1177 }
1178
1189 {
1190 size_t nfailing = xt::sum(m_unstable)(); // todo: costly, cache?
1191
1192 if (nfailing == 0) {
1193 m_failure_idx = m_epsp.size();
1194 return;
1195 }
1196
1197 // == m_gen.exponential() / (m_failure_rate * static_cast<double>(nfailing));
1198 m_t -= std::log(1.0 - m_gen.random()) / (m_failure_rate * static_cast<double>(nfailing));
1199
1200 size_t i = 0;
1201 if (nfailing > 1) {
1202 i = m_gen.randint(nfailing);
1203 }
1204
1205 size_t idx;
1206 for (idx = 0; idx < m_sig.size(); ++idx) {
1207 if (m_unstable.flat(idx)) {
1208 if (i == 0) {
1209 break;
1210 }
1211 --i;
1212 }
1213 }
1214
1215 this->spatialParticleFailure(idx);
1216 }
1217
1223 {
1224 for (size_t i = 0; i < n; ++i) {
1226 }
1227 }
1228
1237 size_t makeAthermalFailureSteps_chrono(size_t n, double elapsed)
1238 {
1239 std::chrono::steady_clock::time_point tic = std::chrono::steady_clock::now();
1240 auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(tic - tic).count();
1241 dt = static_cast<decltype(dt)>(elapsed * 1e3);
1242
1243 for (size_t i = 0; i < n; ++i) {
1245
1246 std::chrono::steady_clock::time_point toc = std::chrono::steady_clock::now();
1247 if (std::chrono::duration_cast<std::chrono::milliseconds>(toc - tic).count() > dt) {
1248 return i + 1;
1249 }
1250 }
1251
1252 return n;
1253 }
1254
1263 void makeWeakestFailureStep(bool allow_stable = false)
1264 {
1265 size_t idx;
1266 double x;
1267
1268 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
1269 idx = xt::argmin(m_sigy - m_sig)(); // todo: costly, cache?
1270 x = m_sigy.flat(idx) - m_sig.flat(idx);
1271 }
1272 else {
1273 idx = xt::argmax(xt::abs(m_sig) - m_sigy)(); // todo: costly, cache?
1274 x = m_sigy.flat(idx) - std::abs(m_sig.flat(idx));
1275 }
1276
1277 if (x > 0 && !allow_stable) {
1278 m_failure_idx = m_epsp.size();
1279 return;
1280 }
1281
1282 m_t += 1.0;
1283 this->spatialParticleFailure(idx);
1284 }
1285
1291 void makeWeakestFailureSteps(size_t n, bool allow_stable = false)
1292 {
1293 for (size_t i = 0; i < n; ++i) {
1294 this->makeWeakestFailureStep(allow_stable);
1295 }
1296 }
1297
1307 size_t makeWeakestFailureSteps_chrono(size_t n, double elapsed, bool allow_stable = false)
1308 {
1309 std::chrono::steady_clock::time_point tic = std::chrono::steady_clock::now();
1310 auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(tic - tic).count();
1311 dt = static_cast<decltype(dt)>(elapsed * 1e3);
1312
1313 for (size_t i = 0; i < n; ++i) {
1314 this->makeWeakestFailureStep(allow_stable);
1315
1316 std::chrono::steady_clock::time_point toc = std::chrono::steady_clock::now();
1317 if (std::chrono::duration_cast<std::chrono::milliseconds>(toc - tic).count() > dt) {
1318 return i + 1;
1319 }
1320 }
1321
1322 return n;
1323 }
1324
1328 double temperature() const
1329 {
1330 if constexpr (!std::is_same<Temperature, detail::Finite>::value) {
1331 throw std::invalid_argument("Only implemented for thermal systems");
1332 }
1333 return m_temperature;
1334 }
1335
1340 {
1341 if constexpr (!std::is_same<Temperature, detail::Finite>::value) {
1342 throw std::invalid_argument("Only implemented for thermal systems");
1343 }
1345 m_inv_temp = 1.0 / m_temperature;
1346 this->stress_updated();
1347 }
1348
1354 {
1355 if constexpr (std::is_same<Temperature, detail::Finite>::value) {
1356 return m_tau_thermal;
1357 }
1358 else {
1359 return m_tau * xt::ones_like(m_sig);
1360 }
1361 }
1362
1376 {
1377 if constexpr (!std::is_same<Temperature, detail::Finite>::value) {
1378 throw std::invalid_argument("Only implemented for thermal systems");
1379 }
1380 // == m_gen.exponential<decltype(m_sig)>(m_sig.shape()) * m_tau_thermal (but faster)
1381 auto dt =
1382 xt::eval(-xt::log(1.0 - m_gen.random<decltype(m_sig)>(m_sig.shape())) * m_tau_thermal);
1383 size_t idx = xt::argmin(dt)();
1384 m_t += dt.flat(idx);
1385 this->spatialParticleFailure(idx);
1386 }
1387
1393 {
1394 for (size_t i = 0; i < n; ++i) {
1395 this->makeThermalFailureStep();
1396 }
1397 }
1398
1407 size_t makeThermalFailureSteps_chrono(size_t n, double elapsed)
1408 {
1409 std::chrono::steady_clock::time_point tic = std::chrono::steady_clock::now();
1410 auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(tic - tic).count();
1411 dt = static_cast<decltype(dt)>(elapsed * 1e3);
1412
1413 for (size_t i = 0; i < n; ++i) {
1414 this->makeThermalFailureStep();
1415
1416 std::chrono::steady_clock::time_point toc = std::chrono::steady_clock::now();
1417 if (std::chrono::duration_cast<std::chrono::milliseconds>(toc - tic).count() > dt) {
1418 return i + 1;
1419 }
1420 }
1421
1422 return n;
1423 }
1424
1430 {
1431 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
1432 return m_sigy - m_sig;
1433 }
1434 else {
1435 return m_sigy - xt::abs(m_sig);
1436 }
1437 }
1438
1444 {
1445 return m_unstable;
1446 }
1447
1457 void spatialParticleFailure(size_t idx)
1458 {
1459 double dsig;
1460
1461 m_failure_idx = idx;
1462 if constexpr (std::is_same<FailureRules, detail::Depinning>::value) {
1463 m_failure_x = m_sigy.flat(idx) - m_sig.flat(idx);
1464 dsig = 1.0;
1465 }
1466 else {
1467 m_failure_x = m_sigy.flat(idx) - std::abs(m_sig.flat(idx));
1468 dsig = m_sig.flat(idx) + m_gen.normal(0.0, 0.01);
1469 }
1470
1471 m_nfails.flat(idx) += 1;
1472 m_epsp.flat(idx) += dsig;
1473 this->drawYieldStress(idx);
1474
1475 if constexpr (Dim == 1) {
1476 ptrdiff_t di = static_cast<ptrdiff_t>(idx) + m_offset[0];
1477 ptrdiff_t n = static_cast<ptrdiff_t>(m_sig.size());
1478
1479 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(m_propagator.size()); ++i) {
1480 // index corrected for periodicity
1481 ptrdiff_t ii = (n + di + i) % n;
1482 // propagate
1483 m_sig.flat(ii) += m_propagator(i) * dsig;
1484 if constexpr (!std::is_same<StressFixer, detail::Spring>::value) {
1485 this->stress_updated(ii);
1486 }
1487 }
1488 }
1489 else if constexpr (Dim == 2) {
1490 auto index = xt::unravel_index(idx, m_sig.shape());
1491 ptrdiff_t di = static_cast<ptrdiff_t>(index[0]) + m_offset[0];
1492 ptrdiff_t dj = static_cast<ptrdiff_t>(index[1]) + m_offset[1];
1493 ptrdiff_t n = static_cast<ptrdiff_t>(m_sig.shape(0));
1494 ptrdiff_t m = static_cast<ptrdiff_t>(m_sig.shape(1));
1495
1496 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(m_propagator.shape(0)); ++i) {
1497 for (ptrdiff_t j = 0; j < static_cast<ptrdiff_t>(m_propagator.shape(1)); ++j) {
1498 // index corrected for periodicity
1499 ptrdiff_t ii = (n + di + i) % n;
1500 ptrdiff_t jj = (m + dj + j) % m;
1501 ptrdiff_t iidx = ii * m + jj;
1502 // propagate
1503 m_sig.flat(iidx) += m_propagator(i, j) * dsig;
1504 if constexpr (!std::is_same<StressFixer, detail::Spring>::value) {
1505 this->stress_updated(iidx);
1506 }
1507 }
1508 }
1509 }
1510
1511 if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
1512 m_stress_fixer->stress_match_frame(m_sig, dsig);
1513 this->stress_updated();
1514 }
1515 }
1516
1527 double shiftImposedShear(int direction = 1)
1528 {
1529 double dsig;
1530
1531 if (direction > 0) {
1532 dsig = xt::amin(m_sigy - m_sig)() + 2.0 * std::numeric_limits<double>::epsilon();
1533 }
1534 else {
1535 dsig = -xt::amin(m_sig + m_sigy)() + 2.0 * std::numeric_limits<double>::epsilon();
1536 }
1537
1538 m_sig += dsig;
1539 this->stress_updated();
1540
1541 if constexpr (std::is_same<StressFixer, detail::Spring>::value) {
1542 m_stress_fixer->frame_match_stress(dsig);
1543 }
1544
1545 return dsig;
1546 }
1547
1556 size_t relaxAthermal(size_t max_steps = 1e9, bool max_steps_is_error = true)
1557 {
1558 for (size_t i = 0; i < max_steps; ++i) {
1560 if (m_failure_idx == m_epsp.size()) {
1561 return i;
1562 }
1563 }
1564
1565 if (max_steps_is_error) {
1566 throw std::runtime_error("Failed to converge.");
1567 }
1568
1569 return max_steps;
1570 }
1571
1580 size_t relaxWeakest(size_t max_steps = 1e9, bool max_steps_is_error = true)
1581 {
1582 for (size_t i = 0; i < max_steps; ++i) {
1583 this->makeWeakestFailureStep();
1584 if (m_failure_idx == m_epsp.size()) {
1585 return i;
1586 }
1587 }
1588
1589 if (max_steps_is_error) {
1590 throw std::runtime_error("Failed to converge.");
1591 }
1592
1593 return max_steps;
1594 }
1595};
1596
1604template <size_t Dim>
1605class SystemStrainControl : public SystemBase<Dim, detail::FixStrain, void, void> {
1606public:
1619 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1622 uint64_t seed,
1623 double failure_rate = 1,
1624 double alpha = 1.5,
1625 bool random_stress = true
1626 )
1627 {
1628 this->initSystem(
1629 propagator, distances, sigmay_mean, sigmay_std, seed, failure_rate, alpha, random_stress
1630 );
1631 }
1632};
1633
1641template <size_t Dim>
1642class SystemStressControl : public SystemBase<Dim, detail::FixStress, void, void> {
1643private:
1644 detail::FixStress m_my_stress_fixer;
1645
1646public:
1652 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1655 uint64_t seed,
1656 double failure_rate = 1,
1657 double alpha = 1.5,
1658 bool random_stress = true
1659 )
1660 : m_my_stress_fixer(0.0)
1661 {
1662 this->initSystem(
1663 propagator,
1664 distances,
1666 sigmay_std,
1667 seed,
1669 alpha,
1670 random_stress,
1671 &m_my_stress_fixer
1672 );
1673 }
1674};
1675
1683template <size_t Dim>
1684class SystemSpringLoading : public SystemBase<Dim, detail::Spring, void, void> {
1685private:
1686 detail::Spring m_my_stress_fixer;
1687
1688public:
1695 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1698 uint64_t seed,
1699 double kframe,
1700 double failure_rate = 1,
1701 double alpha = 1.5,
1702 bool random_stress = true
1703 )
1704 : m_my_stress_fixer(kframe, 0.0, sigmay_mean.size())
1705 {
1706 this->initSystem(
1707 propagator,
1708 distances,
1710 sigmay_std,
1711 seed,
1713 alpha,
1714 random_stress,
1715 &m_my_stress_fixer
1716 );
1717 }
1718};
1719
1723template <size_t Dim>
1724class SystemThermalStressControl : public SystemBase<Dim, detail::FixStress, detail::Finite, void> {
1725private:
1726 detail::FixStress m_my_stress_fixer;
1727
1728public:
1734 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1737 uint64_t seed,
1738 double failure_rate = 1,
1739 double alpha = 1.5,
1740 bool random_stress = true
1741 )
1742 : m_my_stress_fixer(0.0)
1743 {
1744 this->initSystem(
1745 propagator,
1746 distances,
1748 sigmay_std,
1749 seed,
1751 alpha,
1752 random_stress,
1753 &m_my_stress_fixer
1754 );
1755 }
1756};
1757
1765template <size_t Dim>
1767 : public GooseEPM::
1768 SystemBase<Dim, GooseEPM::detail::FixStrain, void, GooseEPM::detail::Depinning> {
1769public:
1793 const array_type::tensor<double, Dim>& propagator,
1794 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1795 const array_type::tensor<double, Dim>& sigmay_std,
1796 uint64_t seed,
1797 double failure_rate = 1,
1798 double alpha = 1.5,
1799 bool random_stress = true
1800 )
1801 {
1802 this->initSystem(
1803 propagator,
1804 distances,
1805 xt::zeros_like(sigmay_std),
1806 sigmay_std,
1807 seed,
1808 failure_rate,
1809 alpha,
1810 random_stress
1811 );
1812 }
1813};
1814
1822template <size_t Dim>
1824 : public GooseEPM::
1825 SystemBase<Dim, GooseEPM::detail::FixStress, void, GooseEPM::detail::Depinning> {
1826private:
1827 GooseEPM::detail::FixStress m_my_stress_fixer;
1828
1829public:
1834 const array_type::tensor<double, Dim>& propagator,
1835 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1836 const array_type::tensor<double, Dim>& sigmay_std,
1837 uint64_t seed,
1838 double failure_rate = 1,
1839 double alpha = 1.5,
1840 bool random_stress = true
1841 )
1842 : m_my_stress_fixer(0.0)
1843 {
1844 this->initSystem(
1845 propagator,
1846 distances,
1847 xt::zeros_like(sigmay_std),
1848 sigmay_std,
1849 seed,
1850 failure_rate,
1851 alpha,
1852 random_stress,
1853 &m_my_stress_fixer
1854 );
1855 }
1856};
1857
1865template <size_t Dim>
1867 : public GooseEPM::
1868 SystemBase<Dim, GooseEPM::detail::Spring, void, GooseEPM::detail::Depinning> {
1869private:
1870 GooseEPM::detail::Spring m_my_stress_fixer;
1871
1872public:
1878 const array_type::tensor<double, Dim>& propagator,
1879 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1880 const array_type::tensor<double, Dim>& sigmay_std,
1881 uint64_t seed,
1882 double kframe,
1883 double failure_rate = 1,
1884 double alpha = 1.5,
1885 bool random_stress = true
1886 )
1887 : m_my_stress_fixer(kframe, 0.0, sigmay_std.size())
1888 {
1889 this->initSystem(
1890 propagator,
1891 distances,
1892 xt::zeros_like(sigmay_std),
1893 sigmay_std,
1894 seed,
1895 failure_rate,
1896 alpha,
1897 random_stress,
1898 &m_my_stress_fixer
1899 );
1900 }
1901};
1902
1906template <size_t Dim>
1908 Dim,
1909 GooseEPM::detail::FixStress,
1910 GooseEPM::detail::Finite,
1911 GooseEPM::detail::Depinning> {
1912private:
1913 GooseEPM::detail::FixStress m_my_stress_fixer;
1914
1915public:
1921 const std::vector<array_type::tensor<ptrdiff_t, 1>>& distances,
1923 uint64_t seed,
1924 double failure_rate = 1,
1925 double alpha = 1.5,
1926 bool random_stress = true
1927 )
1928 : m_my_stress_fixer(0.0)
1929 {
1930 this->initSystem(
1931 propagator,
1932 distances,
1933 xt::zeros_like(sigmay_std),
1934 sigmay_std,
1935 seed,
1937 alpha,
1938 random_stress,
1939 &m_my_stress_fixer
1940 );
1941 }
1942};
1943
1974inline std::tuple<
1975 array_type::tensor<size_t, 1>,
1976 array_type::tensor<size_t, 1>,
1977 array_type::tensor<size_t, 1>>
1979 const array_type::tensor<bool, 1>& condition,
1981 bool first = true,
1982 bool last = true
1983)
1984{
1985 GOOSEEPM_REQUIRE(xt::has_shape(condition, idx.shape()), std::out_of_range);
1986
1987 if (condition.size() == 0) {
1988 return std::make_tuple(
1989 xt::empty<size_t>({0}), xt::empty<size_t>({0}), xt::empty<size_t>({0})
1990 );
1991 }
1992
1993 if (xt::all(condition)) {
1994 if (!first || !last) {
1995 return std::make_tuple(
1996 xt::empty<size_t>({0}), xt::empty<size_t>({0}), xt::empty<size_t>({0})
1997 );
1998 }
1999 std::sort(idx.begin(), idx.end());
2000 size_t a = std::unique(idx.begin(), idx.end()) - idx.begin();
2001 array_type::tensor<size_t, 1> S = {condition.size()};
2004 return std::make_tuple(S, A, index);
2005 }
2006
2007 size_t n = condition.size();
2008 if (!last) {
2009 for (size_t i = condition.size() - 1; i > 0; --i) {
2010 if (!condition(i)) {
2011 n = i + 1;
2012 break;
2013 }
2014 }
2015 }
2016
2017 size_t nevent = static_cast<size_t>(condition(0));
2018 for (size_t i = 0; i < n - 1; ++i) {
2019 if (!condition(i) && condition(i + 1)) {
2020 ++nevent;
2021 }
2022 }
2023
2024 size_t i = 0;
2025 if (!first && condition(0)) {
2026 --nevent;
2027 for (; i < condition.size() - 1; ++i) {
2028 if (!condition(i)) {
2029 break;
2030 }
2031 }
2032 }
2033
2034 array_type::tensor<size_t, 1> S = xt::empty<size_t>({nevent});
2035 array_type::tensor<size_t, 1> A = xt::empty<size_t>({nevent});
2036 array_type::tensor<size_t, 1> index = xt::empty<size_t>({nevent});
2037 size_t event = 0;
2038 size_t j;
2039
2040 for (; i < n; ++i) {
2041 if (condition(i)) {
2042 for (j = i + 1; j < n; ++j) {
2043 if (!condition(j)) {
2044 break;
2045 }
2046 }
2047 if (j < n) {
2048 auto begin = &idx(i);
2049 auto end = &idx(j);
2050 std::sort(begin, end);
2051 S(event) = j - i;
2052 A(event) = std::unique(begin, end) - begin;
2053 index(event) = i;
2054 ++event;
2055 i = j;
2056 }
2057 else {
2058 break;
2059 }
2060 }
2061 }
2062
2063 if (last && j == n) {
2064 if (condition(j - 1)) {
2065 auto begin = &idx(i);
2066 auto end = &idx(j - 1) + 1;
2067 std::sort(begin, end);
2068 S(event) = j - i;
2069 A(event) = std::unique(begin, end) - begin;
2070 index(event) = i;
2071 }
2072 }
2073
2074 return std::make_tuple(S, A, index);
2075}
2076
2089{
2090 array_type::tensor<size_t, 1> ret = xt::empty_like(idx);
2091 xt::xtensor<bool, 1> failed = xt::zeros<bool>({xt::amax(idx)() + 1});
2092
2093 if (idx.size() == 0) {
2094 return ret;
2095 }
2096
2097 ret(0) = 1;
2098 failed(idx(0)) = true;
2099
2100 for (size_t i = 1; i < idx.size(); ++i) {
2101 if (!failed(idx(i))) {
2102 failed(idx(i)) = true;
2103 ret(i) = ret(i - 1) + 1;
2104 }
2105 else {
2106 ret(i) = ret(i - 1);
2107 }
2108 }
2109
2110 return ret;
2111}
2112
2135template <size_t Dimension>
2136class [[deprecated("Use GooseEYE instead")]] AvalancheSegmenter {
2137public:
2138 static constexpr size_t Dim = Dimension;
2139
2140private:
2141 std::array<size_t, Dim> m_shape;
2147 ptrdiff_t m_new_label = 1;
2148 size_t m_step = 0;
2149
2159 std::vector<ptrdiff_t> m_renum;
2160
2171 std::vector<ptrdiff_t> m_next;
2172
2173 std::vector<ptrdiff_t> m_connected;
2174
2175public:
2186 const std::array<size_t, Dim>& shape,
2189 )
2190 {
2191 GOOSEEPM_WARNING_PYTHON("AvalancheSegmenter is deprecated, use GooseEYE instead.");
2192 m_shape = shape;
2193 m_idx = idx;
2194 m_time = t;
2195 m_s = xt::zeros<size_t>(m_shape);
2196 m_label = xt::zeros<ptrdiff_t>(m_shape);
2197 m_renum.resize(m_s.size() + 1);
2198 m_next.resize(m_s.size() + 1);
2199 std::iota(m_renum.begin(), m_renum.end(), 0);
2200 this->clean_next();
2201
2202 GOOSEEPM_REQUIRE(xt::has_shape(m_idx, t.shape()), std::invalid_argument);
2203 GOOSEEPM_REQUIRE(std::is_sorted(m_time.begin(), m_time.end()), std::invalid_argument);
2204 GOOSEEPM_REQUIRE(xt::amax(m_idx)() < m_s.size(), std::invalid_argument);
2205
2206 if constexpr (Dim == 1) {
2207 // kernel = {1, 1, 1}
2208 m_dx = {-1, 1};
2209 }
2210 else if constexpr (Dim == 2) {
2211 // kernel = {{0, 1, 0}, {1, 1, 1}, {0, 1, 0}};
2212 m_dx = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
2213 }
2214 m_connected.resize(m_dx.shape(0));
2215 }
2216
2225 size_t nlabels()
2226 {
2227 return m_new_label;
2228 }
2229
2234 void prune()
2235 {
2236 ptrdiff_t n = static_cast<ptrdiff_t>(m_new_label);
2237 m_new_label = 1;
2238 m_renum[0] = 0;
2239 for (ptrdiff_t i = 1; i < n; ++i) {
2240 if (m_renum[i] == i) {
2241 m_renum[i] = m_new_label;
2242 ++m_new_label;
2243 }
2244 }
2245 this->private_renumber(m_renum);
2246 std::iota(m_renum.begin(), m_renum.begin() + n, 0);
2247 }
2248
2249private:
2253 void clean_next()
2254 {
2255 std::fill(m_next.begin(), m_next.end(), -1);
2256 }
2257
2262 template <class T>
2263 void private_renumber(const T& renum)
2264 {
2265 for (size_t i = 0; i < m_label.size(); ++i) {
2266 m_label.flat(i) = renum[m_label.flat(i)];
2267 }
2268 }
2269
2270public:
2282 {
2283 GOOSEEPM_ASSERT(new_label.size() >= this->nlabels(), std::out_of_range);
2285 *std::max_element(new_label.begin(), new_label.end()) < this->size() + 1,
2286 std::out_of_range
2287 );
2288
2289 this->private_renumber(new_label);
2290 m_new_label = xt::amax(m_label)() + 1;
2291 std::fill(m_renum.begin(), m_renum.begin() + m_new_label, 0);
2292
2293 for (size_t i = 0; i < new_label.size(); ++i) {
2294 m_renum[new_label[i]] = new_label[i];
2295 }
2296 }
2297
2303 {
2304 auto maxlab = *std::max_element(labels.begin(), labels.end());
2305
2306#ifdef GOOSEEPM_ENABLE_ASSERT
2307 GOOSEEPM_ASSERT(maxlab < m_new_label, std::out_of_range);
2308
2309 std::vector<bool> label_in_use(m_new_label, false);
2310 for (size_t i = 0; i < m_label.size(); ++i) {
2311 label_in_use[m_label.flat(i)] = true;
2312 }
2313
2314 std::vector<bool> label_in_order(m_new_label, false);
2315 for (size_t i = 0; i < labels.size(); ++i) {
2316 label_in_order[labels[i]] = true;
2317 }
2318
2319 for (ptrdiff_t i = 0; i < m_new_label; ++i) {
2320 if (label_in_use[i]) {
2321 GOOSEEPM_ASSERT(label_in_order[i], std::out_of_range);
2322 }
2323 }
2324#endif
2325
2326 decltype(m_renum) tmp(m_new_label);
2327
2328 for (size_t i = 0; i < labels.size(); ++i) {
2329 tmp[labels[i]] = i;
2330 m_renum[labels[i]] = i;
2331 }
2332
2333 this->private_renumber(tmp);
2334 m_new_label = xt::amax(m_label)() + 1;
2335 std::iota(m_renum.begin(), m_renum.begin() + m_new_label, 0);
2336 }
2337
2338private:
2347 void merge_detail(ptrdiff_t a, ptrdiff_t b)
2348 {
2349 // -> head[list(b)] = head[a]
2350 ptrdiff_t i = m_renum[b];
2351 ptrdiff_t target = m_renum[a];
2352 m_renum[b] = target;
2353 while (true) {
2354 i = m_next[i];
2355 if (i == -1) {
2356 break;
2357 }
2358 m_renum[i] = target;
2359 }
2360 // -> list(head[a]).append(list(b))
2361 while (m_next[a] != -1) {
2362 a = m_next[a];
2363 }
2364 m_next[a] = b;
2365 }
2366
2374 ptrdiff_t merge(ptrdiff_t* labels, size_t nlabels)
2375 {
2376 std::sort(labels, labels + nlabels);
2377 nlabels = std::unique(labels, labels + nlabels) - labels;
2378 ptrdiff_t target = labels[0];
2379 for (size_t i = 1; i < nlabels; ++i) {
2380 this->merge_detail(target, labels[i]);
2381 }
2382 return target;
2383 }
2384
2385public:
2390 void advance(size_t n = 1)
2391 {
2392 size_t nmerge = 0;
2393 size_t nconnected;
2394 ptrdiff_t iidx;
2395 size_t nsteps = std::min(m_step + n, m_idx.size());
2396 std::array<ptrdiff_t, Dim> index;
2397 std::array<ptrdiff_t, Dim> shape;
2398 std::array<ptrdiff_t, Dim> strides;
2399 for (size_t i = 0; i < Dim; ++i) {
2400 shape[i] = static_cast<ptrdiff_t>(m_shape[i]);
2401 strides[i] = static_cast<ptrdiff_t>(m_label.strides()[i]);
2402 }
2403
2404 for (; m_step < nsteps; ++m_step) {
2405 auto idx = static_cast<ptrdiff_t>(m_idx(m_step));
2406 m_s.flat(idx) += 1;
2407
2408 // if the block was labelled before there is nothing further to do
2409 if (m_s.flat(idx) > 1) {
2410 continue;
2411 }
2412
2413 nconnected = 0;
2414 for (size_t j = 0; j < m_dx.shape(0); ++j) {
2415 if constexpr (Dim == 1) {
2416 ptrdiff_t nn = shape[0];
2417 ptrdiff_t ii = (nn + idx + m_dx(j)) % nn; // index corrected for periodicity
2418 iidx = ii;
2419 }
2420 else if constexpr (Dim == 2) {
2421 detail::unravel_index(idx, strides, index);
2422 ptrdiff_t nn = shape[0];
2423 ptrdiff_t mm = shape[1];
2424 ptrdiff_t ii = (nn + index[0] + m_dx(j, 0)) % nn;
2425 ptrdiff_t jj = (mm + index[1] + m_dx(j, 1)) % mm;
2426 iidx = ii * mm + jj;
2427 }
2428 if (m_label.flat(iidx) != 0) {
2429 m_connected[nconnected] = m_renum[m_label.flat(iidx)];
2430 nconnected++;
2431 }
2432 }
2433 if (nconnected == 0) {
2434 m_label.flat(idx) = m_new_label;
2435 m_new_label += 1;
2436 }
2437 else if (nconnected == 1) {
2438 m_label.flat(idx) = m_connected[0];
2439 }
2440 else {
2441 // mark all labels in the list for merging
2442 // `m_label` is not yet updated to avoid looping over all blocks too frequently
2443 // the new label can be read by `m_renum[lab]` (as done above)
2444 m_label.flat(idx) = this->merge(&m_connected[0], nconnected);
2445 nmerge++;
2446
2447 // every so often: apply the renumbering to `m_label`
2448 // the linked labels in `m_next` can be released
2449 if (nmerge > 100) {
2450 this->private_renumber(m_renum);
2451 this->clean_next();
2452 nmerge = 0;
2453 }
2454 }
2455 }
2456
2457 // apply the renumbering to `m_label`
2458 // the linked labels in `m_next` can be released
2459 if (nmerge > 0) {
2460 this->private_renumber(m_renum);
2461 this->clean_next();
2462 }
2463 }
2464
2472 void advance_to(double t, bool floor = false)
2473 {
2474 if (t >= m_time.back()) {
2475 return this->advance(m_time.size() - m_step);
2476 }
2477 if (t <= m_time(m_step)) {
2478 return;
2479 }
2480
2481 for (size_t i = 0; i < m_time.size() - m_step; ++i) {
2482 if (m_time(m_step + i) > t) {
2483 if (floor) {
2484 return this->advance(i - 1);
2485 }
2486 else {
2487 if (m_time(m_step + i) - t < t - m_time(m_step + i - 1)) {
2488 return this->advance(i);
2489 }
2490 else {
2491 return this->advance(i - 1);
2492 }
2493 }
2494 }
2495 }
2496 }
2497
2502 std::string repr() const
2503 {
2504 return detail::get_namespace() + "AvalancheSegmenter" + std::to_string(Dim) + " " +
2505 detail::shape_to_string(m_shape);
2506 }
2507
2512 const auto& shape() const
2513 {
2514 return m_label.shape();
2515 }
2516
2521 auto size() const
2522 {
2523 return m_label.size();
2524 }
2525
2530 const auto& s() const
2531 {
2532 return m_s;
2533 }
2534
2535 // todo: allow resetting a cluster map ?
2536
2541 const auto& labels() const
2542 {
2543 return m_label;
2544 }
2545
2554 size_t nstep()
2555 {
2556 return m_step;
2557 }
2558
2563 auto t() const
2564 {
2565 if (m_step == 0) {
2566 return 0.0;
2567 }
2568 return m_time(m_step - 1);
2569 }
2570};
2571
2596private:
2600
2601public:
2602 Avalanche() = default;
2603
2608 std::string repr() const
2609 {
2610 return detail::get_namespace() + "Avalanche" + " " +
2611 detail::shape_to_string(std::array<size_t, 1>{m_idx.size()});
2612 }
2613
2619 {
2620 return m_idx;
2621 }
2622
2628 {
2629 return m_x;
2630 }
2631
2637 {
2638 return m_t;
2639 }
2640
2647 template <class System, class Func>
2648 void makeFailureSteps(System& system, size_t n, Func func)
2649 {
2650 m_idx = xt::empty<size_t>({n});
2651 m_x = xt::empty<double>({n});
2652 m_t = xt::empty<double>({n});
2653
2654 for (size_t i = 0; i < n; ++i) {
2655 func();
2656 m_idx(i) = system.failure_idx();
2657 m_x(i) = system.failure_x();
2658 m_t(i) = system.t();
2659 }
2660 }
2661
2670 template <class System, class Func>
2671 size_t makeFailureSteps_chrono(System& system, size_t n, double elapsed, Func func)
2672 {
2673 std::chrono::steady_clock::time_point tic = std::chrono::steady_clock::now();
2674 auto dt = std::chrono::duration_cast<std::chrono::milliseconds>(tic - tic).count();
2675 dt = static_cast<decltype(dt)>(elapsed * 1e3);
2676
2677 std::array<size_t, 1> shape = {n};
2678 m_idx.resize(shape);
2679 m_x.resize(shape);
2680 m_t.resize(shape);
2681
2682 for (size_t i = 0; i < n; ++i) {
2683 func();
2684 m_idx(i) = system.failure_idx();
2685 m_x(i) = system.failure_x();
2686 m_t(i) = system.t();
2687
2688 std::chrono::steady_clock::time_point toc = std::chrono::steady_clock::now();
2689 if (std::chrono::duration_cast<std::chrono::milliseconds>(toc - tic).count() > dt) {
2690 // todo: remove copy if xtensor.resize is item preserving
2691 // https://github.com/xtensor-stack/xtensor/issues/2745
2692 size_t nret = i + 1;
2693 shape[0] = nret;
2694 {
2695 std::vector<size_t> tmp(nret);
2696 std::copy(m_idx.begin(), m_idx.begin() + nret, tmp.begin());
2697 m_idx.resize(shape);
2698 std::copy(tmp.begin(), tmp.end(), m_idx.begin());
2699 }
2700 {
2701 std::vector<double> tmp(nret);
2702 std::copy(m_x.begin(), m_x.begin() + nret, tmp.begin());
2703 m_x.resize(shape);
2704 std::copy(tmp.begin(), tmp.end(), m_x.begin());
2705 }
2706 {
2707 std::vector<double> tmp(nret);
2708 std::copy(m_t.begin(), m_t.begin() + nret, tmp.begin());
2709 m_t.resize(shape);
2710 std::copy(tmp.begin(), tmp.end(), m_t.begin());
2711 }
2712 return nret;
2713 }
2714 }
2715
2716 return n;
2717 }
2718
2724 template <class System>
2725 void makeAthermalFailureSteps(System& system, size_t n)
2726 {
2727 this->makeFailureSteps(system, n, [&system]() { return system.makeAthermalFailureStep(); });
2728 }
2729
2736 template <class System>
2737 void makeWeakestFailureSteps(System& system, size_t n, bool allow_stable = false)
2738 {
2739 this->makeFailureSteps(system, n, [&system, allow_stable]() {
2740 return system.makeWeakestFailureStep(allow_stable);
2741 });
2742 }
2743
2749 template <class System>
2750 void makeThermalFailureSteps(System& system, size_t n)
2751 {
2752 this->makeFailureSteps(system, n, [&system]() { return system.makeThermalFailureStep(); });
2753 }
2754
2761 template <class System>
2762 size_t makeAthermalFailureSteps_chrono(System& system, size_t n, double elapsed)
2763 {
2764 return this->makeFailureSteps_chrono(system, n, elapsed, [&system]() {
2765 return system.makeAthermalFailureStep();
2766 });
2767 }
2768
2776 template <class System>
2778 System& system,
2779 size_t n,
2780 double elapsed,
2781 bool allow_stable = false
2782 )
2783 {
2784 return this->makeFailureSteps_chrono(system, n, elapsed, [&system, allow_stable]() {
2785 return system.makeWeakestFailureStep(allow_stable);
2786 });
2787 }
2788
2795 template <class System>
2796 size_t makeThermalFailureSteps_chrono(System& system, size_t n, double elapsed)
2797 {
2798 return this->makeFailureSteps_chrono(system, n, elapsed, [&system]() {
2799 return system.makeThermalFailureStep();
2800 });
2801 }
2802};
2803
2804} // namespace GooseEPM
2805
2806#endif
Segment event in spatially correlated avalanches.
Definition System.h:2136
size_t nstep()
Number of yielding events taken into account.
Definition System.h:2554
const auto & labels() const
Per block, the label (0 for background).
Definition System.h:2541
void advance_to(double t, bool floor=false)
Advance to the yielding events closest to a time t.
Definition System.h:2472
std::string repr() const
Basic class info.
Definition System.h:2502
auto size() const
Size of AvalancheSegmenter::s and AvalancheSegmenter::labels (== prod(shape)).
Definition System.h:2521
void change_labels(const array_type::tensor< size_t, 1 > &new_label)
Apply renumbering.
Definition System.h:2281
void reorder(const array_type::tensor< ptrdiff_t, 1 > &labels)
Reorder the labels.
Definition System.h:2302
size_t nlabels()
Current number of labels (including the background).
Definition System.h:2225
void advance(size_t n=1)
Advance n yielding events.
Definition System.h:2390
void prune()
Prune: renumber labels to lowest possible label, see also AvalancheSegmenter::nlabels.
Definition System.h:2234
const auto & shape() const
Shape of AvalancheSegmenter::s and AvalancheSegmenter::labels.
Definition System.h:2512
const auto & s() const
Per block, how many times it has yielded.
Definition System.h:2530
auto t() const
Time of each entry in m_idx.
Definition System.h:2563
AvalancheSegmenter(const std::array< size_t, Dim > &shape, const array_type::tensor< size_t, 1 > &idx, const array_type::tensor< double, 1 > &t)
Definition System.h:2185
Measure avalanches in a system.
Definition System.h:2595
size_t makeFailureSteps_chrono(System &system, size_t n, double elapsed, Func func)
Run the measurement.
Definition System.h:2671
const array_type::tensor< size_t, 1 > & idx() const
Index of failing block.
Definition System.h:2618
size_t makeAthermalFailureSteps_chrono(System &system, size_t n, double elapsed)
Run SystemBase::makeAthermalFailureStep and store the evolution.
Definition System.h:2762
const array_type::tensor< double, 1 > & t() const
Time of the failure.
Definition System.h:2636
const array_type::tensor< double, 1 > & x() const
Distance to yielding of the failing block before failing.
Definition System.h:2627
void makeAthermalFailureSteps(System &system, size_t n)
Run SystemBase::makeAthermalFailureStep and store the evolution.
Definition System.h:2725
void makeWeakestFailureSteps(System &system, size_t n, bool allow_stable=false)
Run SystemBase::makeWeakestFailureStep and store the evolution.
Definition System.h:2737
void makeFailureSteps(System &system, size_t n, Func func)
Run the measurement.
Definition System.h:2648
size_t makeThermalFailureSteps_chrono(System &system, size_t n, double elapsed)
Run SystemBase::makeThermalFailureStep and store the evolution.
Definition System.h:2796
size_t makeWeakestFailureSteps_chrono(System &system, size_t n, double elapsed, bool allow_stable=false)
Run SystemBase::makeWeakestFailureStep and store the evolution.
Definition System.h:2777
void makeThermalFailureSteps(System &system, size_t n)
Run SystemBase::makeThermalFailureStep and store the evolution.
Definition System.h:2750
std::string repr() const
Basic class info.
Definition System.h:2608
double shiftImposedShear(int direction=1)
Change the imposed shear strain such that one block fails in the direction of shear.
Definition System.h:1527
const auto & shape() const
Return the shape.
Definition System.h:799
void makeThermalFailureStep()
Make a thermal failure step.
Definition System.h:1375
array_type::tensor< bool, Dim > m_unstable
Per block: is the block unstable?
Definition System.h:573
void set_sigma(const array_type::tensor< double, Dim > &sigma)
Set the stress.
Definition System.h:1007
double m_tau
Inverse failure rate.
Definition System.h:580
std::string repr() const
Basic class info.
Definition System.h:770
void makeWeakestFailureStep(bool allow_stable=false)
Fail weakest block (unstable) and advance the time by one.
Definition System.h:1263
auto size() const
Return the size (== prod(shape)).
Definition System.h:808
void set_sigmabar(double sigmabar)
Adjust the stress such that the average stress is equal to the argument.
Definition System.h:1034
array_type::tensor< double, Dim > m_sigy_std
Standard deviation of yield stress.
Definition System.h:570
array_type::tensor< double, Dim > m_sig
Stress.
Definition System.h:567
const array_type::tensor< double, Dim > & sigmay() const
Get the yield stress.
Definition System.h:955
double m_t
Time.
Definition System.h:575
uint64_t state() const
Get the state of the random number generator.
Definition System.h:900
array_type::tensor< ptrdiff_t, 1 > distances(size_t axis=0) const
Distances along an axis of the propagator.
Definition System.h:827
const array_type::tensor< double, Dim > & sigmay_mean() const
Get the mean wherefrom the yield stress of each block is drawn.
Definition System.h:974
double m_temperature
Temperature.
Definition System.h:578
void initSigmaPropogator(double sigma_std)
Generate a stress field that is compatible.
Definition System.h:1140
array_type::tensor< double, Dim > m_sigy_mu
Mean yield stress.
Definition System.h:569
void makeThermalFailureSteps(size_t n)
Make n steps with SystemBase::makeThermalFailureStep.
Definition System.h:1392
StressFixer * m_stress_fixer
Method to fix the average stress.
Definition System.h:577
double epsframe() const
Get the current position of the loading frame.
Definition System.h:1069
double t() const
Get the time.
Definition System.h:882
array_type::tensor< double, Dim > m_tau_thermal
Thermal failure rate.
Definition System.h:574
size_t makeAthermalFailureSteps_chrono(size_t n, double elapsed)
Make n steps with SystemBase::makeAthermalFailureStep.
Definition System.h:1237
const array_type::tensor< bool, Dim > & unstable() const
If a block is unstable.
Definition System.h:1443
size_t makeThermalFailureSteps_chrono(size_t n, double elapsed)
Make n steps with SystemBase::makeThermalFailureStep.
Definition System.h:1407
double m_failure_x
For the last failure: distance to yielding before failure.
Definition System.h:583
void set_epsframe(double u)
Overwrite the current position of the loading frame.
Definition System.h:1082
const array_type::tensor< double, Dim > & propagator() const
Get the propagator (as used internally: reordered/normalised compared to the input).
Definition System.h:817
size_t relaxWeakest(size_t max_steps=1e9, bool max_steps_is_error=true)
Relax the system by calling SystemBase::makeWeakestFailureStep until there are no more unstable block...
Definition System.h:1580
auto failure_idx() const
For the last failure: index of the failing block.
Definition System.h:855
void set_sigmay_std(const array_type::tensor< double, Dim > &value)
Set the standard deviation wherefrom the yield stress of each block is drawn.
Definition System.h:983
array_type::tensor< size_t, Dim > m_nfails
Number of times a block failed.
Definition System.h:572
auto failure_rate() const
Failure rate (parameter).
Definition System.h:846
array_type::tensor< double, Dim > time_to_failure() const
Time to failure.
Definition System.h:1353
double m_alpha
Exponent characterising the shape of the potential.
Definition System.h:576
void set_sigmay(const array_type::tensor< double, Dim > &sigmay)
Set the yield stress.
Definition System.h:945
const array_type::tensor< double, Dim > & epsp() const
Get the plastic strain.
Definition System.h:918
void set_epsp(const array_type::tensor< double, Dim > &epsp)
Set the plastic strain.
Definition System.h:909
auto alpha() const
Exponent characterising the shape of the potential.
Definition System.h:837
void makeWeakestFailureSteps(size_t n, bool allow_stable=false)
Make n steps with SystemBase::makeWeakestFailureStep.
Definition System.h:1291
void spatialParticleFailure(size_t idx)
Fail a block.
Definition System.h:1457
double temperature() const
Get the applied temperature.
Definition System.h:1328
array_type::tensor< double, Dim > m_sigy
Yield stress.
Definition System.h:568
void set_sigmay_mean(const array_type::tensor< double, Dim > &value)
Set the mean wherefrom the yield stress of each block is drawn.
Definition System.h:964
void makeAthermalFailureStep()
Fail an unstable block, chosen randomly from all unstable blocks.
Definition System.h:1188
void set_t(double t)
Set the time.
Definition System.h:873
auto failure_x() const
For the last failure: distance to yielding before failure.
Definition System.h:864
prrng::pcg32 m_gen
Random number generator.
Definition System.h:564
const array_type::tensor< double, Dim > x() const
Distance to failure.
Definition System.h:1429
array_type::tensor< double, Dim > m_propagator
Propagator.
Definition System.h:565
double m_inv_temp
Inverse temperature.
Definition System.h:579
void makeAthermalFailureSteps(size_t n)
Make n steps with SystemBase::makeAthermalFailureStep.
Definition System.h:1222
void set_nfails(const array_type::tensor< size_t, Dim > &nfails)
Set the number of times each block failed.
Definition System.h:927
void initSigmaFast(double sigma_std, size_t delta_r)
Generate a stress field that is compatible, using a fast but approximative technique.
Definition System.h:1096
std::array< ptrdiff_t, Dim > m_offset
Minimal distance in each direction, used as offset.
Definition System.h:566
const array_type::tensor< size_t, Dim > & nfails() const
Get the number of times each block failed.
Definition System.h:936
void set_temperature(double temperature)
Set the applied temperature.
Definition System.h:1339
void set_state(uint64_t state)
Set the state of the random number generator.
Definition System.h:891
size_t m_failure_idx
For the last failure: index of the failing block.
Definition System.h:582
const array_type::tensor< double, Dim > & sigmay_std() const
Get the standard deviation wherefrom the yield stress of each block is drawn.
Definition System.h:993
size_t makeWeakestFailureSteps_chrono(size_t n, double elapsed, bool allow_stable=false)
Make n steps with SystemBase::makeWeakestFailureStep.
Definition System.h:1307
size_t relaxAthermal(size_t max_steps=1e9, bool max_steps_is_error=true)
Relax the system by calling SystemBase::makeAthermalFailureStep until there are no more unstable bloc...
Definition System.h:1556
static constexpr size_t Dim
Dimensionality of the system.
Definition System.h:561
void initSystem(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_mean, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate, double alpha, bool random_stress, StressFixer *stress_fixer=nullptr)
Definition System.h:592
double m_failure_rate
Failure rate (parameter).
Definition System.h:581
array_type::tensor< double, Dim > m_epsp
Plastic strain.
Definition System.h:571
double sigmabar() const
Get the average stress.
Definition System.h:1055
const array_type::tensor< double, Dim > & sigma() const
Get the stress.
Definition System.h:1017
System loaded using a spring.
Definition System.h:1868
SystemDepinningSpringLoading(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double kframe, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Important
Definition System.h:1877
System for which the average strain is fixed.
Definition System.h:1768
SystemDepinningStrainControl(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Important
Definition System.h:1792
System for which the average stress is fixed.
Definition System.h:1825
SystemDepinningStressControl(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Important
Definition System.h:1833
System for which the average stress is fixed.
Definition System.h:1911
SystemDepinningThermalStressControl(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Important
Definition System.h:1919
System loaded using a spring.
Definition System.h:1684
SystemSpringLoading(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_mean, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double kframe, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Definition System.h:1693
System for which the average strain is fixed.
Definition System.h:1605
SystemStrainControl(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_mean, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Definition System.h:1617
System for which the average stress is fixed.
Definition System.h:1642
SystemStressControl(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_mean, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Definition System.h:1650
System for which the average stress is fixed.
Definition System.h:1724
SystemThermalStressControl(const array_type::tensor< double, Dim > &propagator, const std::vector< array_type::tensor< ptrdiff_t, 1 > > &distances, const array_type::tensor< double, Dim > &sigmay_mean, const array_type::tensor< double, Dim > &sigmay_std, uint64_t seed, double failure_rate=1, double alpha=1.5, bool random_stress=true)
Definition System.h:1732
#define GOOSEEPM_ASSERT(expr, assertion)
All assertions are implementation as::
Definition config.h:53
#define GOOSEEPM_WARNING(message)
All warnings are implemented as::
Definition config.h:89
#define GOOSEEPM_REQUIRE(expr, assertion)
Assertion that cannot be switched off.
Definition config.h:63
#define GOOSEEPM_WARNING_PYTHON(message)
All warnings specific to the Python API are implemented as::
Definition config.h:105
xt::xtensor< T, N > tensor
Fixed (static) rank array.
Definition config.h:135
Elasto-plastic model.
Definition config.h:111
std::tuple< array_type::tensor< size_t, 1 >, array_type::tensor< size_t, 1 >, array_type::tensor< size_t, 1 > > segment_avalanche(const array_type::tensor< bool, 1 > &condition, array_type::tensor< size_t, 1 > idx, bool first=true, bool last=true)
Compute avalanche properties of a series of segments.
Definition System.h:1978
array_type::tensor< size_t, 1 > cumsum_n_unique(const array_type::tensor< size_t, 1 > &idx)
Cumulative sum of the number of unique items in the slice up to that index.
Definition System.h:2088