29 #include "petsc_utils.h"
41 log_msg(NULL, 5, 0,
"DREAM/Eikonal physics currently do not support MPI parallelization. Use openMP instead. Aborting!");
51 logger =
f_open(
"eikonal.log", param_globals::experiment != 4 ?
"w" :
"r");
75 param_globals::dt, 0,
"elec::ref_dt",
"TS");
98 if (param_globals::prepacing_bcl > 0)
109 m->
regions.resize(param_globals::num_gregions);
112 log_msg(
logger, 0, 0,
"Setting up %s tissue poperties for %d regions ..", grid_name,
113 param_globals::num_gregions);
118 for (
size_t i = 0; i < m->
regions.size(); i++, reg++) {
119 if (!strcmp(param_globals::gregion[i].
name,
"")) {
120 snprintf(buf,
sizeof buf,
", gregion_%d",
int(i));
121 param_globals::gregion[i].name =
dupstr(buf);
124 reg->
regname = strdup(param_globals::gregion[i].
name);
126 reg->
nsubregs = param_globals::gregion[i].num_IDs;
132 for (
int j = 0; j < reg->
nsubregs; j++)
133 reg->
subregtags[j] = param_globals::gregion[i].ID[j];
137 elecMaterial* emat =
new elecMaterial();
140 emat->InVal[0] = param_globals::gregion[i].g_il;
141 emat->InVal[1] = param_globals::gregion[i].g_it;
142 emat->InVal[2] = param_globals::gregion[i].g_in;
144 emat->ExVal[0] = param_globals::gregion[i].g_el;
145 emat->ExVal[1] = param_globals::gregion[i].g_et;
146 emat->ExVal[2] = param_globals::gregion[i].g_en;
148 emat->BathVal[0] = param_globals::gregion[i].g_bath;
149 emat->BathVal[1] = param_globals::gregion[i].g_bath;
150 emat->BathVal[2] = param_globals::gregion[i].g_bath;
153 for (
int j = 0; j < 3; j++) {
154 emat->InVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
155 emat->ExVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
156 emat->BathVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
165 const char* file = g ==
Eikonal::intra_grid ? param_globals::gi_scale_vec : param_globals::ge_scale_vec;
169 if (num_file_entries != mesh.g_numelem)
170 log_msg(0, 4, 0,
"%s warning: number of %s conductivity scaling entries does not match number of elements!",
176 escale->read_ascii(g ==
Eikonal::intra_grid ? param_globals::gi_scale_vec : param_globals::ge_scale_vec);
190 escale->release_ptr(p);
194 void Eikonal::setup_mappings()
203 log_msg(
logger, 0, 0,
"%s: Setting up intracellular algebraic-to-nodal scattering.", __func__);
207 log_msg(
logger, 0, 0,
"%s: Setting up intracellular PETSc to canonical permutation.", __func__);
218 case EIKONAL: solve_EIKONAL();
break;
219 case DREAM: solve_DREAM();
break;
220 default: solve_RE();
break;
251 double curtime =
timing(t2, t1);
282 void Eikonal::setup_stimuli()
286 bool dumpTrace =
true;
290 stimuli.resize(param_globals::num_stim);
291 for (
int i = 0; i < param_globals::num_stim; i++) {
295 if (param_globals::stim[i].crct.type != 0 && param_globals::stim[i].crct.type != 9) {
298 log_msg(NULL, 5, 0,
"%s error: stimulus of type %i is incompatible with the eikonal model! Use I_tm or Vm_clmp instead. Aborting!", __func__, s.
phys.
type);
307 log_msg(NULL, 2, 0,
"Only geometry, start time, npls, and bcl of stim[%i] are used", i);
316 void Eikonal::stimulate_intracellular()
326 if (param_globals::operator_splitting) {
336 *ps.tmp_i1 = *ps.IIon;
337 *ps.tmp_i1 -= *ps.Irhs;
342 ps.mass_i->mult(*ps.tmp_i1, *ps.Irhs);
344 *ps.Irhs = *ps.tmp_i1;
352 if (illum_vec == NULL) {
353 log_msg(0, 5, 0,
"Cannot apply illumination stim: global vector not present!");
368 void Eikonal::clamp_Vm()
376 void Eikonal::setup_output()
384 restr_i, param_globals::num_io_nodes > 0);
386 if (param_globals::dataout_i) {
395 if (strcmp(param_globals::dream.
output.idifffile,
"") != 0) {
402 if (strcmp(param_globals::dream.
output.idifffile,
"") != 0) {
410 if (param_globals::num_trace) {
412 open_trace(
ion.
miif, param_globals::num_trace, param_globals::trace_node, NULL, &imesh);
419 void Eikonal::dump_matrices()
421 std::string bsname = param_globals::dump_basename;
427 if (param_globals::parab_solve == 1) {
429 fn = bsname +
"_Ki_CN.bin";
432 fn = bsname +
"_Ki.bin";
435 fn = bsname +
"_Mi.bin";
449 val = std::nan(
"NaN");
463 s_unit =
stimuli[sidx].pulse.wave.f_unit;
468 void Eikonal::setup_solvers()
482 if (param_globals::dump2MatLab) dump_matrices();
486 void Eikonal::checkpointing()
495 snprintf(save_fnm,
sizeof save_fnm,
"%s.%s.roe", param_globals::write_statef, tsav_ext);
504 snprintf(save_fnm,
sizeof save_fnm,
"checkpoint.%.1f.roe", tm.time);
509 void Eikonal::prepace()
516 log_msg(NULL, 0, 0,
"Using activation times from file %s to distribute prepacing states\n",
517 param_globals::prepacing_lats);
518 log_msg(NULL, 1, 0,
"Assuming stimulus strength %f uA/uF with duration %f ms for prepacing\n",
530 size_t numread = read_lats->read_ascii(param_globals::prepacing_lats);
532 log_msg(NULL, 5, 0,
"Failed reading required LATs! Skipping prepacing!");
541 bool forward =
false;
542 (*sc)(*read_lats, forward);
546 SF_real* lp = read_lats->ptr();
547 for (
int i = 0; i < read_lats->lsize(); i++)
548 if (lp[i] < 0.0) lp[i] = param_globals::tend + 10.0;
550 read_lats->release_ptr(lp);
555 SF_real LATmin = read_lats->min();
558 log_msg(0, 3, 0,
"LAT data is not complete. Skipping prepacing.");
562 SF_real offset = floor(LATmin / param_globals::prepacing_bcl) * param_globals::prepacing_bcl;
563 SF_real last_tm = param_globals::prepacing_bcl * param_globals::prepacing_beats;
566 *read_lats += -offset;
568 *read_lats += last_tm;
571 SF_real* save_tm = read_lats->ptr();
574 for (
int ii = 0; ii < miif->
N_IIF; ii++) {
575 if (!miif->
N_Nodes[ii])
continue;
579 for (
int kk = 0; kk < miif->
N_Nodes[ii]; kk++) {
580 sorted_save[kk].v1 = save_tm[miif->
NodeLists[ii][kk]];
581 sorted_save[kk].v2 = kk;
583 std::sort(sorted_save.begin(), sorted_save.end());
585 size_t lastidx = sorted_save.size() - 1;
586 int paced = sorted_save[lastidx].v2;
589 for (
double t = 0; t < sorted_save[lastidx].v1; t += param_globals::dt) {
590 if (fmod(t, param_globals::prepacing_bcl) < stimdur &&
591 t < param_globals::prepacing_bcl * param_globals::prepacing_beats - 1)
592 miif->
ldata[ii][limpet::Vm][paced] += stimstr * param_globals::dt;
597 miif->
ldata[ii][limpet::Vm][paced] -= miif->
ldata[ii][limpet::Iion][paced] * param_globals::dt;
598 vm[miif->
NodeLists[ii][paced]] = miif->
ldata[ii][limpet::Vm][paced];
600 while (csav < miif->N_Nodes[ii] - 1 && t >= sorted_save[csav].v1)
605 while (csav < miif->N_Nodes[ii] - 1)
608 for (
int k = 0; k < miif->
N_Nodes[ii]; k++) vm[miif->
NodeLists[ii][k]] = miif->
ldata[ii][limpet::Vm][k];
611 read_lats->release_ptr(save_tm);
616 void Eikonal::solve_EIKONAL()
626 void Eikonal::solve_RE()
637 void Eikonal::solve_DREAM()
655 void Eikonal::solve_RD()
693 if (param_globals::output_level > 1)
log_msg(0, 0, 0,
"\n *** Initializing Eikonal Solver ***\n");
697 if (param_globals::dream.
output.debugNode >= 0) {
698 nodeData.
idX = param_globals::dream.output.debugNode;
700 snprintf(buf,
sizeof buf,
"node_%d.dat",
nodeData.
idX);
727 for (
int i = 0; i < ((mesh.
xyz.
size() + 1) / 3); i++) {
758 switch (mesh.
type[0]) {
772 log_msg(0, 5, 0,
"Error: Type of element is not compatible with this version of the eikonal model. Use tetrahedra or triangles");
777 if (strlen(param_globals::start_statef) > 0) load_state_file();
779 create_node_to_node_graph();
781 translate_stim_to_eikonal();
783 init_diffusion_current();
790 for (
int i = 0; i < length - 1; i++) {
791 for (
int j = 0; j < length - i - 1; j++) {
792 if (Array[j] > Array[j + 1]) {
794 Array[j] = Array[j + 1];
795 Array[j + 1] = Temp_a;
796 Temp_i = IndexArray[j];
797 IndexArray[j] = IndexArray[j + 1];
798 IndexArray[j + 1] = Temp_i;
806 bool act_is_in_safety_window, empty_list_and_illegal_stimulus, empty_stimulus, stimulus_is_in_safety_window;
808 act_is_in_safety_window = (
actMIN > param_globals::dream.tau_s) && (
actMIN - time > param_globals::dream.tau_s);
811 empty_list_and_illegal_stimulus = (
sum(
List) == 0) && (empty_stimulus || stimulus_is_in_safety_window);
813 return act_is_in_safety_window || empty_list_and_illegal_stimulus;
820 for (
int num_g = 0; num_g < param_globals::num_gregions; num_g++) {
821 for (
int num_id = 0; num_id <= param_globals::gregion[num_g].num_IDs; num_id++) {
823 for (
int i = 0; i < vertices.
size(); i++) {
824 CVnodes[vertices[i]] = param_globals::gregion[num_g].dream.vel_l;
825 cv_ar_t[vertices[i]] = param_globals::gregion[num_g].dream.vel_l / param_globals::gregion[num_g].dream.vel_t;
827 cv_ar_n[vertices[i]] = param_globals::gregion[num_g].dream.vel_l / param_globals::gregion[num_g].dream.vel_n;
834 for (
int num_i = 0; num_i < param_globals::num_imp_regions; num_i++) {
835 for (
int num_id = 0; num_id <= param_globals::imp_region[num_i].num_IDs; num_id++) {
837 for (
int i = 0; i < vertices.
size(); i++) {
838 p_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.rho;
839 k_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.kappa;
840 j_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.theta;
841 l_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.psi;
846 if (strlen(param_globals::cv_scale_vec)) {
848 const char* file = param_globals::cv_scale_vec;
852 if (num_file_entries != mesh.
g_numpts)
853 log_msg(0, 2, 0,
"%s warning: number of %s conductivity scaling entries does not match number of points!,%i,%i",
876 for (
int num_g = 0; num_g < param_globals::num_gregions; num_g++) {
877 for (
int num_id = 0; num_id < param_globals::gregion[num_g].num_IDs; num_id++) {
879 for (
int i = 0; i < vertices.
size(); i++) {
880 CVnodes[vertices[i]] *= nd_scale[vertices[i]];
888 void eikonal_solver::update_Ta_in_active_list()
890 bool First_in_List = 1;
892 for (
size_t j = 0; j <
List.size(); j++) {
893 if (
T_A[j] < 0)
continue;
894 if (
List[j] == 1 && First_in_List) {
909 for (
size_t j = 0; j <
List.size(); j++) {
925 if (T_A_indX ==
inf || (
T_R[indX] + delay) == -400 || abs(T_A_indX - (
TA_old[indX])) < 2) {
927 }
else if ((
T_R[indX]) > T_A_indX) {
930 float DI_indX = T_A_indX - (
T_R[indX] + delay);
939 return -
CVnodes[indX] * Factor_DI;
941 return CVnodes[indX] * Factor_DI;
953 SF_real q = compute_F(indX, p);
954 while (abs(p - q) >= param_globals::dream.fim.tol && iter_ch < param_globals::dream.fim.max_coh) {
956 q = compute_F(indX, p);
959 if (compute_H(indX, q) < 0)
return T_A[indX];
977 bool cond2add = add_node_neighbor_to_list(
T_R[indNode],
T_A[indNode], TimeSt);
978 if (cond2add && compute_H(indNode, TimeSt) > 0) {
979 if (
T_A[indNode] < TimeSt) {
982 T_A[indNode] = TimeSt;
986 if (param_globals::dream.
output.debugNode == indNode) {
991 for (
int ii =
n2n_dsp[indNode]; ii <
n2n_dsp[indNode + 1]; ii++) {
993 if (
List[indXNB] == 0) {
995 if (param_globals::dream.
output.debugNode == indXNB) {
1004 if (cond2add && compute_H(indNode, TimeSt) <= 0) {
1024 if (indNode == -1)
continue;
1027 bool cond2add = add_node_neighbor_to_list(
T_R[indNode],
T_A[indNode], TimeSt);
1028 if (cond2add && compute_H(indNode, TimeSt) > 0) {
1029 T_A[indNode] = TimeSt;
1033 if (param_globals::dream.
output.debugNode == indNode) {
1038 for (
int ii =
n2n_dsp[indNode]; ii <
n2n_dsp[indNode + 1]; ii++) {
1040 if (
List[indXNB] == 0) {
1042 if (param_globals::dream.
output.debugNode == indXNB) {
1058 if (cond2add && compute_H(indNode, TimeSt) <= 0) {
1083 T_A[indNode] = TimeSt;
1085 for (
size_t j =
n2n_dsp[indNode]; j <
n2n_dsp[indNode + 1]; j++) {
1096 if (
List[indX] == 0)
continue;
1099 SF_real q = compute_F(indX, p);
1102 if (abs(p - q) < param_globals::dream.fim.tol) {
1105 if (
List[indXNB] == 1)
continue;
1107 q = compute_F(indXNB, p);
1117 update_Ta_in_active_list();
1120 if (niter > param_globals::dream.fim.max_iter)
break;
1125 double* atc =
AT->
ptr();
1137 auto dur =
timing(t1, t0);
1157 float maxadvance =
user_globals::tm_manager->
time + param_globals::dream.tau_s + param_globals::dream.tau_inc + param_globals::dream.tau_max;
1168 if (
List[indX] == 0)
continue;
1170 q = compute_coherence(indX);
1175 if (q > maxadvance)
continue;
1177 if (abs(p - q) < param_globals::dream.fim.tol || (
num_changes[indX] > param_globals::dream.fim.max_iter) || (q ==
inf || p ==
inf)) {
1181 if (
List[indXNB] == 1) {
1188 qNB = compute_coherence(indXNB);
1194 bool ignore_L2_if_valid = node_is_valid && qNB > pNB &&
nReadded2List[indXNB] >= param_globals::dream.fim.max_addpt;
1196 if (node_is_valid && (
nReadded2List[indXNB] < param_globals::dream.fim.max_addpt) || ignore_L2_if_valid) {
1204 if (param_globals::dream.
output.debugNode == indXNB) {
1213 if (param_globals::dream.
output.debugNode == indX) {
1222 update_Ta_in_active_list();
1224 if (
actMIN > actMIN_old) {
1225 progress_time =
actMIN - actMIN_old;
1240 double* atc =
AT->
ptr();
1241 double* rpt =
RT->
ptr();
1255 auto dur =
timing(t1, t0);
1264 if (param_globals::dream.
output.debugNode >= 0) {
1312 D[0][0] += (1 / ani_ratio2),
D[1][1] += (1 / ani_ratio2),
D[2][2] += (1 / ani_ratio2);
1323 std::remove(Elem_cur.begin(), Elem_cur.end(), indX);
1325 condign1 =
T_A[Elem_cur[0]] ==
inf ||
T_A[Elem_cur[1]] ==
inf ||
T_A[Elem_cur[2]] ==
inf || (
T_A[Elem_cur[0]] < time) || (
T_A[Elem_cur[1]] < time) || (
T_A[Elem_cur[2]] < time);
1326 condign2 = (
T_A[Elem_cur[0]] <
T_R[Elem_cur[0]]) || (
T_A[Elem_cur[1]] <
T_R[Elem_cur[1]]) || (
T_A[Elem_cur[2]] <
T_R[Elem_cur[2]]);
1331 SF_real temp3 = solve_tet(indX, Elem_cur);
1333 if (temp3 < TA && temp3 >
T_R[indX] && compute_H(indX, temp3) > 0) {
1334 Winner_Element[0] = Elem_cur[0];
1335 Winner_Element[1] = Elem_cur[1];
1336 Winner_Element[2] = Elem_cur[2];
1341 std::vector<mesh_int_t> Triang = {-1, -1};
1343 for (
int cases = 0; cases < 3; cases++) {
1346 Triang[0] = Elem_cur[0];
1347 Triang[1] = Elem_cur[1];
1351 Triang[0] = Elem_cur[0];
1352 Triang[1] = Elem_cur[2];
1356 Triang[0] = Elem_cur[1];
1357 Triang[1] = Elem_cur[2];
1370 SF_real temp2 = solve_tri(indX, Triang);
1372 if (temp2 < TA && temp2 >
T_R[indX] && compute_H(indX, temp2) > 0) {
1373 Winner_Element[0] = Triang[0];
1374 Winner_Element[1] = Triang[1];
1375 Winner_Element[2] = -1;
1389 std::remove(Elem_cur.begin(), Elem_cur.end(), indX);
1398 SF_real temp2 = solve_tri(indX, Elem_cur);
1400 if (
TA > temp2 && temp2 >
T_R[indX] && compute_H(indX, temp2) > 0) {
1412 SF_real temp1 = solve_edges(indX, indXNB);
1414 if (temp1 < TA && temp1 >
T_R[indX] && compute_H(indX, temp1) > 0) {
1415 Winner_Element[0] = indXNB;
1416 Winner_Element[1] = -1;
1417 Winner_Element[2] = -1;
1424 Winner_Element[0] = -1;
1425 Winner_Element[1] = -1;
1426 Winner_Element[2] = -1;
1430 std::sort(Winner_Element.begin(), Winner_Element.end());
1431 if (compute_H(indX,
TA) == 0)
TA =
inf;
1437 SF_real eikonal_solver::solve_tet(
mesh_int_t& indx3, std::vector<mesh_int_t>& Elem_cur)
1444 int indy3 = Elem_cur[0], indz3 = Elem_cur[1], indw3 = Elem_cur[2];
1462 XY[0] =
Y[0] -
X[0];
1463 XZ[0] =
Z[0] -
X[0];
1464 XW[0] = W[0] -
X[0];
1465 XY[1] =
Y[1] -
X[1];
1466 XZ[1] =
Z[1] -
X[1];
1467 XW[1] = W[1] -
X[1];
1468 XY[2] =
Y[2] -
X[2];
1469 XZ[2] =
Z[2] -
X[2];
1470 XW[2] = W[2] -
X[2];
1472 c1 = mult_VtMV(XY,
iD, XY);
1473 c2 = mult_VtMV(XZ,
iD, XZ);
1474 c3 = mult_VtMV(XW,
iD, XW);
1475 c4 = 2 * (mult_VtMV(XY,
iD, XZ));
1476 c5 = 2 * mult_VtMV(XY,
iD, XW);
1477 c6 = 2 * mult_VtMV(XW,
iD, XZ);
1498 if (abs(
s1) > tol && abs(
s3) > tol && abs(
s4) > tol && (
s2 /
s3) > 0) {
1502 if ((
a > 0) && (
a < 1) && (
b > 0) && (
b < 1) && ((1 -
a -
b) > 0) && ((1 -
a -
b) < 1)) {
1503 TA_th =
std::min(TA_th, compute_AT_at_node_in_tet(
a,
b, indx3, indy3, indz3, indw3));
1509 if ((
a > 0) && (
a < 1) && (
b > 0) && (
b < 1) && ((1 -
a -
b) > 0) && ((1 -
a -
b) < 1)) {
1510 TA_th =
std::min(TA_th, compute_AT_at_node_in_tet(
a,
b, indx3, indy3, indz3, indw3));
1517 SF_real eikonal_solver::solve_tri(
mesh_int_t& indx, std::vector<mesh_int_t>& Elem_cur)
1524 int indy = Elem_cur[0], indz = Elem_cur[1];
1538 XY[0] =
Y[0] -
X[0];
1539 XZ[0] =
Z[0] -
X[0];
1540 XY[1] =
Y[1] -
X[1];
1541 XZ[1] =
Z[1] -
X[1];
1542 XY[2] =
Y[2] -
X[2];
1543 XZ[2] =
Z[2] -
X[2];
1545 C = mult_VtMV(XZ,
iD, XZ);
1546 A = mult_VtMV(XY,
iD, XY) - 2 * mult_VtMV(XZ,
iD, XY) +
C;
1547 B = 2 * mult_VtMV(XY,
iD, XZ) - 2 *
C;
1549 if (abs(
T_A[indy] -
T_A[indz]) < 1e-5) {
1551 if (
p1 > 0 &&
p1 < 1) {
1552 TA_tr =
std::min(compute_AT_at_node(
p1, indx, indy, indz), TA_tr);
1565 if (
p1 > 0 &&
p1 < 1) {
1566 TA_tr =
std::min(compute_AT_at_node(
p1, indx, indy, indz), TA_tr);
1572 if (
p2 > 0 &&
p2 < 1) {
1573 TA_tr =
std::min(compute_AT_at_node(
p2, indx, indy, indz), TA_tr);
1592 XY[0] =
Y[0] -
X[0];
1593 XY[1] =
Y[1] -
X[1];
1594 XY[2] =
Y[2] -
X[2];
1596 Num = mult_VtMV(XY,
iD, XY);
1607 bool isNewTABetweenRTAndOldTA = (
RT < newTA) && (newTA < oldTA);
1608 bool isNewTABiggerRTBiggerOldTA = (oldTA <
RT) && (
RT < newTA);
1610 return (isNewTABetweenRTAndOldTA) || (isNewTABiggerRTBiggerOldTA);
1617 if (
X[0] ==
Y[0] &&
X[1] ==
Y[1] &&
X[2] ==
Y[2]) {
1618 result =
iD[0][0] *
X[0] *
X[0] + 2 *
iD[0][1] *
X[0] *
X[1] +
iD[1][1] *
X[1] *
X[1] + 2 *
iD[0][2] *
X[0] *
X[2] +
iD[2][2] *
X[2] *
X[2] + 2 *
iD[1][2] *
X[1] *
X[2];
1620 result =
iD[0][0] *
X[0] *
Y[0] +
iD[0][1] * (
X[0] *
Y[1] +
X[1] *
Y[0]) +
iD[1][1] *
X[1] *
Y[1] +
iD[0][2] * (
X[0] *
Y[2] +
X[2] *
Y[0]) +
iD[2][2] *
X[2] *
Y[2] +
iD[1][2] * (
X[1] *
X[2] +
X[2] *
Y[1]);
1634 }
else if (pp == 1) {
1640 Num = sqrt(
A * pp * pp +
B * pp +
C);
1649 Num =
c1 * (pf2 * pf2) +
c2 * (qf2 * qf2) +
c3 * ((1 - pf2 - qf2) * (1 - pf2 - qf2)) +
c4 * (pf2 * qf2) +
c5 * (pf2 * (1 - pf2 - qf2)) +
c6 * (qf2 * (1 - pf2 - qf2));
1652 TA_ori =
T_A[indyf2] * pf2 +
T_A[indzf2] * qf2 +
T_A[indwf2] * (1 - pf2 - qf2);
1659 FILE* file_writestate;
1660 char buffer_writestate[1024];
1661 snprintf(buffer_writestate,
sizeof buffer_writestate,
"%s.%s.roe.dat", param_globals::write_statef, tsav_ext);
1662 file_writestate = fopen(buffer_writestate,
"w");
1663 for (
size_t jjj = 0; jjj <
List.size(); jjj++) {
1667 fclose(file_writestate);
1676 for (
int ind_nodes = 0; ind_nodes <
num_pts; ind_nodes++) {
1677 double prev_R =
T_R[ind_nodes];
1679 if (old_ptr[ind_nodes] >= param_globals::dream.repol_time_thresh && new_ptr[ind_nodes] < param_globals::dream.repol_time_thresh) {
1680 T_R[ind_nodes] = time;
1693 int max_threads = omp_get_max_threads();
1694 omp_set_num_threads(1);
1702 for (
int i = 0; i < miif->
N_IIF; i++) {
1703 if (!miif->
N_Nodes[i])
continue;
1712 int ind_gb = (miif->
NodeLists[i][current]);
1715 double Vm_old = miif->
ldata[i][limpet::Vm][current];
1716 double Iion_old = miif->
ldata[i][limpet::Iion][current];
1717 double prev_R =
T_R[ind_gb];
1719 bool cond_2upd =
T_R[ind_gb] <=
T_A[ind_gb] &&
T_A[ind_gb] !=
inf && Vm_old > param_globals::dream.repol_time_thresh;
1722 double elapsed_time = 0;
1723 double prev_Vm = miif->
ldata[i][limpet::Vm][current];
1728 miif->
ldata[i][limpet::Vm][current] -= miif->
ldata[i][limpet::Iion][current] * param_globals::dt;
1729 if (miif->
ldata[i][limpet::Vm][current] < param_globals::dream.repol_time_thresh) {
1730 T_R[ind_gb] = time + elapsed_time;
1733 }
while (elapsed_time < 600);
1736 miif->
ldata[i][limpet::Vm][current] = Vm_old;
1737 miif->
ldata[i][limpet::Iion][current] = Iion_old;
1740 }
while (current < miif->N_Nodes[i]);
1745 omp_set_num_threads(max_threads);
1749 auto dur =
timing(t1, t0);
1767 for (
size_t j = 0; j < alg_nod.
size(); j++) {
1771 if ((time >=
T_A[loc_nodal_idx]) && ((
T_A[loc_nodal_idx] + 5) >= time) && (
List[loc_nodal_idx] == 0)) {
1772 dT = time -
T_A[loc_nodal_idx];
1774 switch (
diff_cur[loc_nodal_idx].model) {
1776 c[loc_petsc_idx] =
diff_cur[loc_nodal_idx].alpha_1 * exp(-((dT -
diff_cur[loc_nodal_idx].beta_1) /
diff_cur[loc_nodal_idx].gamma_1) * ((dT -
diff_cur[loc_nodal_idx].beta_1) /
diff_cur[loc_nodal_idx].gamma_1))
1781 e_on = (dT >= 0) ? 1.0 : 0.0;
1782 e_off = (v[loc_petsc_idx] <
diff_cur[loc_nodal_idx].V_th) ? 1.0 : 0.0;
1783 c[loc_petsc_idx] =
diff_cur[loc_nodal_idx].A_F /
diff_cur[loc_nodal_idx].tau_F * exp(dT /
diff_cur[loc_nodal_idx].tau_F) * e_on * e_off;
1792 auto dur =
timing(t1, t0);
1796 void eikonal_solver::load_state_file()
1799 FILE* file_startstate;
1800 char buffer_startstate[strlen(param_globals::start_statef) + 10];
1802 snprintf(buffer_startstate,
sizeof buffer_startstate,
"%s.dat", param_globals::start_statef);
1803 file_startstate = fopen(buffer_startstate,
"r");
1805 if (file_startstate == NULL) {
1806 log_msg(NULL, 5, 0,
"Not able to open state file: %s", buffer_startstate);
1807 }
else if (param_globals::output_level) {
1808 log_msg(NULL, 0, 0,
"Open state file for eikonal model: %s", buffer_startstate);
1811 int ListVal, numChangesVal, numChanges2Val;
1812 float T_AVal, T_RVal, TA_oldVal, D_IVal, PCLVal;
1815 while (fscanf(file_startstate,
"%d %d %d %f %f %f %f", &ListVal, &numChangesVal, &numChanges2Val, &T_AVal, &T_RVal, &TA_oldVal, &D_IVal) == 7) {
1816 List[index] = ListVal;
1819 T_A[index] = T_AVal;
1820 T_R[index] = T_RVal;
1821 TA_old[index] = TA_oldVal;
1822 D_I[index] = D_IVal;
1825 if (param_globals::output_level)
log_msg(NULL, 0, 0,
"Number of nodes in active list: %i",
sum(
List));
1827 fclose(file_startstate);
1830 void eikonal_solver::init_diffusion_current()
1835 for (
int num_i = 0; num_i < param_globals::num_imp_regions; num_i++) {
1836 for (
int num_id = 0; num_id <= param_globals::imp_region[num_i].num_IDs; num_id++) {
1838 for (
int i = 0; i < vertices.
size(); i++) {
1840 switch (
diff_cur[vertices[i]].model) {
1842 diff_cur[vertices[i]].alpha_1 = param_globals::imp_region[num_i].dream.Idiff.alpha_i[0];
1843 diff_cur[vertices[i]].alpha_2 = param_globals::imp_region[num_i].dream.Idiff.alpha_i[1];
1844 diff_cur[vertices[i]].alpha_3 = param_globals::imp_region[num_i].dream.Idiff.alpha_i[2];
1845 diff_cur[vertices[i]].beta_1 = param_globals::imp_region[num_i].dream.Idiff.beta_i[0];
1846 diff_cur[vertices[i]].beta_2 = param_globals::imp_region[num_i].dream.Idiff.beta_i[1];
1847 diff_cur[vertices[i]].beta_3 = param_globals::imp_region[num_i].dream.Idiff.beta_i[2];
1848 diff_cur[vertices[i]].gamma_1 = param_globals::imp_region[num_i].dream.Idiff.gamma_i[0];
1849 diff_cur[vertices[i]].gamma_2 = param_globals::imp_region[num_i].dream.Idiff.gamma_i[1];
1850 diff_cur[vertices[i]].gamma_3 = param_globals::imp_region[num_i].dream.Idiff.gamma_i[2];
1853 diff_cur[vertices[i]].A_F = param_globals::imp_region[num_i].dream.Idiff.A_F;
1854 diff_cur[vertices[i]].tau_F = param_globals::imp_region[num_i].dream.Idiff.tau_F;
1855 diff_cur[vertices[i]].V_th = param_globals::imp_region[num_i].dream.Idiff.V_th;
1862 void eikonal_solver::translate_stim_to_eikonal()
1865 if (param_globals::output_level)
log_msg(NULL, 0, 0,
"Initializing stimuli for eikonal model ...");
1869 int number_stimulus = param_globals::num_stim;
1870 int total_stimuli = 0;
1875 for (stimulus s : *stimuliRef) {
1877 for (
int idx_pls = 0; idx_pls < s.
ptcl.
npls; idx_pls++) {
1885 bubble_sort(StartStimulus, IndexStimulus, total_stimuli);
1887 for (
int ind_stim = 0; ind_stim < total_stimuli; ind_stim++) {
1888 stimulus s = (*stimuliRef)[IndexStimulus[ind_stim]];
1890 for (
int ind_nodes = 0; ind_nodes < s.electrode.vertices.size(); ind_nodes++) {
1901 void eikonal_solver::create_node_to_node_graph()
1906 twoFib = mesh.she.size() > 0;
1908 for (
int point_idx = 0; point_idx <
num_pts; point_idx++) {
1910 std::vector<mesh_int_t> NNNodesxNodes(numNBElem *
numPtsxElem);
1912 for (
int eedsp = 0; eedsp < numNBElem; eedsp++) {
1916 fiber_node[3 * point_idx] = mesh.fib[3 * currElem];
1917 fiber_node[3 * point_idx + 1] = mesh.fib[3 * currElem + 1];
1918 fiber_node[3 * point_idx + 2] = mesh.fib[3 * currElem + 2];
1920 sheet_node[3 * point_idx] = mesh.she[3 * currElem];
1921 sheet_node[3 * point_idx + 1] = mesh.she[3 * currElem + 1];
1922 sheet_node[3 * point_idx + 2] = mesh.she[3 * currElem + 2];
1925 fiber_node[3 * point_idx] = mesh.fib[3 * currElem];
1926 fiber_node[3 * point_idx + 1] = mesh.fib[3 * currElem + 1];
1927 fiber_node[3 * point_idx + 2] = mesh.fib[3 * currElem + 2];
1930 for (
int nndsp = 0; nndsp <
numPtsxElem; nndsp++) {
1935 std::sort(NNNodesxNodes.begin(), NNNodesxNodes.end());
1936 auto it = std::unique(NNNodesxNodes.begin(), NNNodesxNodes.end());
1937 NNNodesxNodes.erase(it, NNNodesxNodes.end());
1938 it = std::remove(NNNodesxNodes.begin(), NNNodesxNodes.end(), point_idx);
1939 NNNodesxNodes.erase(it, NNNodesxNodes.end());
1940 int max = NNNodesxNodes.size();
1942 n2n_dsp[point_idx + 1] =
n2n_dsp[point_idx] + NNNodesxNodes.size();
1954 const char* h1 =
" ------ ---------- ---------- ------- ------- | List logic ----- -------- | Neighbor node ---- |";
1955 const char* h2 =
" cycle AT old AT RT DI | Status Entry Exit | ID AT |";
1958 log_msg(NULL, 3, 0,
"%s error: Could not open file %s in %s. Turning off logging.\n",
1968 if (!this->logger)
return;
1975 std::ostringstream oss_TA, oss_TA_, oss_TR, oss_DI, oss_nbnTA;
1976 oss_TA << this->
T_A;
1977 oss_TA_ << this->
T_A_;
1978 oss_TR << this->
T_R;
1979 oss_DI << this->
D_I;
1983 snprintf(cbuf,
sizeof cbuf,
"%7s %10s",
"-",
"-");
1985 snprintf(cbuf,
sizeof cbuf,
"%7d %10s", this->
idXNB, oss_nbnTA.str().c_str());
1988 snprintf(abuf,
sizeof abuf,
"%6d %10s %10s %7s %7s", this->
cycle, oss_TA.str().c_str(), oss_TA_.str().c_str(), oss_TR.str().c_str(), oss_DI.str().c_str());
1991 unsigned char flag = cflg ?
ECHO : 0;
1992 log_msg(this->logger, 0, flag |
FLUSH |
NONL,
"%9.3f %s | %s | %s |\n", time, abuf, bbuf, cbuf);
1996 this->T_A_ = this->
T_A;
1997 this->T_A = std::numeric_limits<double>::quiet_NaN();
1998 this->T_R = std::numeric_limits<double>::quiet_NaN();
1999 this->D_I = std::numeric_limits<double>::quiet_NaN();
2001 this->nbn_T_A = std::numeric_limits<double>::quiet_NaN();
2008 const char* stat_str;
2009 const char* reas_str;
double SF_real
Use the general double as real type.
Basic utility structs and functions, mostly IO related.
virtual void write(const char *filename) const =0
size_t read_ascii(FILE *fd)
virtual void release_ptr(S *&p)=0
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
virtual T lsize() const =0
void set_size(const short irows, const short icols)
set the matrix dimensions
overlapping_layout< T > pl
nodal parallel layout
size_t g_numpts
global number of points
vector< T > dsp
connectivity starting index of each element
vector< elem_t > type
element type
vector< S > xyz
node cooridnates
size_t l_numpts
local number of points
MPI_Comm comm
the parallel mesh is defined on a MPI world
Container for a PETSc VecScatter.
size_t size() const
The current size of the vector.
void resize(size_t n)
Resize a vector.
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Represents the ionic model and plug-in (IMP) data structure.
const IonType & get_type() const
Gets this IMP's model type.
virtual void copy_SVs_from(IonIfBase &other, bool alloc)=0
Copies the state variables of an IMP.
Target get_target() const
int get_num_node() const
Gets the number of nodes handled by this IMP.
void compute(int start, int end, GlobalData_t **data)
Perform ionic model computation for 1 time step.
virtual IonIfBase * make_ion_if(Target target, int num_node, const std::vector< std::reference_wrapper< IonType >> &plugins) const =0
Generate an IonIf object from this type.
std::vector< IonIfBase * > IIF
array of IIF's
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
std::vector< IonTypeList > plugtypes
plugins types for each region
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
GlobalData_t *** ldata
data local to each IMP
int N_IIF
how many different IIF's
int * N_Nodes
#nodes for each IMP
int ** NodeLists
local partitioned node lists for each IMP stored
int timer_idx
the timer index received from the timer manager
FILE_SPEC logger
The logger of the physic, each physic should have one.
const char * name
The name of the physic, each physic should have one.
std::string timer_unit(const int timer_id)
figure out units of a signal linked to a given timer
Eikonal()
Most of the initialization is done with initialize()
SF::vector< stimulus > stimuli
the electrical stimuli
parabolic_solver parab_solver
Solver for the parabolic bidomain equation.
MaterialType mtype[2]
the material types of intra_grid and extra_grid grids.
void destroy()
Currently we only need to close the file logger.
double timer_val(const int timer_id)
figure out current value of a signal linked to a given timer
sf_vec * phie_dummy
no elliptic solver needed, but we need a dummy for phie to use parabolic solver
eikonal_solver eik_solver
Solver for the eikonal equation.
LAT_detector lat
the activation time detector
gvec_data gvec
datastruct holding global IMP state variable output
generic_timing_stats IO_stats
grid_t
An electrics grid identifier to distinguish between intra and extra grids.
igb_output_manager output_manager_cycle
void initialize()
Initialize the Eikonal class.
igb_output_manager output_manager_time
class handling the igb output
int check_quiescence(double tm, double dt)
check for quiescence
void output_initial_activations()
output one nodal vector of initial activation time
void init(sf_vec &vm, sf_vec &phie, int offset, enum physic_t=elec_phys)
initializes all datastructs after electric solver setup
int check_acts(double tm)
check activations at sim time tm
SF::vector< SF_real > j_cvrest
void FIM()
Standard fast iterative method to solve eikonal equation with active list approach.
void update_repolarization_times_from_rd(sf_vec &Vmv, sf_vec &Vmv_old, double time)
Checks if Vm crosses the repolarization threshold during each time step of the parabolic solver and u...
SF::vector< SF_real > fiber_node
SF::vector< SF_real > cv_ar_t
SF::vector< SF_real > T_R
SF::vector< mesh_int_t > n2e_dsp
SF::vector< SF_real > T_A
mesh_int_t Index_currStim
SF::vector< SF_real > CVnodes
SF::vector< SF_real > k_cvrest
SF::vector< SF_real > z_coord
SF::vector< SF_real > p_cvrest
SF::vector< SF_real > TA_old
SF_real time2stop_eikonal
SF::vector< SF_real > cv_ar_n
SF::vector< SF_real > sheet_node
void set_initial_cv()
Set the initial conduction velocity (CV) parameters for the nodes in the mesh.
SF::vector< SF_real > x_coord
SF::vector< diffusion_current > diff_cur
void init()
Initialize vectors and variables in the eikonal_solver class.
SF::vector< mesh_int_t > e2n_con
bool determine_model_to_run(double &time)
Determine the next model to run in the alternation between RD and Eikonal.
SF::vector< mesh_int_t > stim_status
SF::vector< mesh_int_t > elem_start
SF::vector< mesh_int_t > StimulusPoints
void save_eikonal_state(const char *tsav_ext)
Save the current state of variables related to the Eikonal simulation to a file to initialize a futur...
void compute_bc()
Computes and updates boundary counditions in cycFIM.
void compute_diffusion_current(const double &time, sf_vec &vm)
Compute the diffusion current approximation I_diff in all activated nodes. Only converged nodes are c...
std::vector< mesh_int_t > n2n_connect
void set_stimuli(SF::vector< stimulus > &stimuli)
Simple setter for stimulus vector.
SF::vector< mesh_int_t > e2n_cnt
SF::vector< SF_real > y_coord
SF::vector< mesh_int_t > n2e_con
void clean_list()
Clean the list of nodes by resetting their status and tracking changes based on the time step of the ...
SF::vector< SF_real > D_I
SF::vector< mesh_int_t > nReadded2List
SF::vector< mesh_int_t > num_changes
std::vector< mesh_int_t > n2n_dsp
SF::vector< SF_real > l_cvrest
SF::vector< SF_real > StimulusTimes
SF::vector< mesh_int_t > n2e_cnt
void cycFIM()
Implementation of the cyclical fast iterative method used in step A of the DREAM model.
void update_repolarization_times(const Ionics &ion)
Estimates initial repolarization times (T_R) in Step D of DREAM.
eikonal_solver_stats stats
SF::vector< SF_real > CVnodes_mod
void write_data()
write registered data to disk
void close_files_and_cleanup()
close file descriptors
void register_output(sf_vec *inp_data, const mesh_t inp_meshid, const int dpn, const char *name, const char *units, const SF::vector< mesh_int_t > *idx=NULL, bool elem_data=false)
Register a data vector for output.
sf_mat * rhs_parab
rhs matrix to solve parabolic
void rebuild_matrices(MaterialType *mtype, limpet::MULTI_IF &miif, FILE_SPEC logger)
void solve(sf_vec &phie_i)
sf_mat * mass_i
lumped for parabolic problem
sf_mat * lhs_parab
lhs matrix (CN) to solve parabolic
sf_vec * Vmv
global Vm vector
sf_vec * old_vm
older Vm needed for 2nd order dT
int write_trace()
write traces to file
stim_t type
type of stimulus
int npls
number of stimulus pulses
double pcl
pacing cycle length
double start
start time of protocol
sig::time_trace wave
wave form of stimulus pulse
stim_protocol ptcl
applied stimulation protocol used
int idx
index in global input stimulus array
stim_pulse pulse
stimulus wave form
void translate(int id)
convert legacy definitions to new format
bool is_active() const
Return whether stim is active.
void setup(int idx)
Setup from a param stimulus index.
stim_physics phys
physics of stimulus
std::string name
label stimulus
double time_step
global reference time step
int add_eq_timer(double istart, double iend, int ntrig, double iintv, double idur, const char *iname, const char *poolname=nullptr)
Add a equidistant step timer to the array of timers.
Diffusion Reaction Eikonal Alternant Model (DREAM) based on the electrics physics class.
void invert_3x3(S *ele, S &det)
void transpose_connectivity(const vector< T > &a_cnt, const vector< T > &a_con, vector< T > &b_cnt, vector< T > &b_con)
Transpose CRS matrix graph A into B.
T sum(const vector< T > &vec)
Compute sum of a vector's entries.
void count(const vector< T > &data, vector< S > &cnt)
Count number of occurrences of indices.
void outer_prod(const Point &a, const Point &b, const double s, double *buff, const bool add=false)
T local_nodal_to_local_petsc(const meshdata< T, S > &mesh, int rank, T local_nodal)
size_t root_count_ascii_lines(std::string file, MPI_Comm comm)
count the lines in a ascii file
void init_vector(SF::abstract_vector< T, S > **vec)
void cnt_from_dsp(const std::vector< T > &dsp, std::vector< T > &cnt)
Compute counts from displacements.
void dsp_from_cnt(const std::vector< T > &cnt, std::vector< T > &dsp)
Compute displacements from counts.
void dup_IMP_node_state(IonIfBase &IF, int from, int to, GlobalData_t **localdata)
constexpr T min(T a, T b)
constexpr T max(T a, T b)
void dump_trace(MULTI_IF *MIIF, limpet::Real time)
void open_trace(MULTI_IF *MIIF, int n_traceNodes, int *traceNodes, int *label, opencarp::sf_mesh *imesh)
Set up ionic model traces at some global node numbers.
timer_manager * tm_manager
a manager for the various physics timers
bool using_legacy_stimuli
flag storing whether legacy stimuli are used
int stimidx_from_timeridx(const SF::vector< stimulus > &stimuli, const int timer_id)
determine link between timer and stimulus
SF::scattering * get_scattering(const int from, const int to, const SF::SF_nbr nbr, const int dpn)
Get a scattering from the global scatter registry.
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
SF::scattering * register_scattering(const int from, const int to, const SF::SF_nbr nbr, const int dpn)
Register a scattering between to grids, or between algebraic and nodal representation of data on the ...
SF::scattering * get_permutation(const int mesh_id, const int perm_id, const int dpn)
Get the PETSC to canonical permutation scattering for a given mesh and number of dpn.
SF::meshdata< mesh_int_t, mesh_real_t > sf_mesh
void apply_stim_to_vector(const stimulus &s, sf_vec &vec, bool add)
vec3< V > cross(const vec3< V > &a, const vec3< V > &b)
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
const char * get_mesh_type_name(mesh_t t)
get a char* to the name of a mesh type
void region_mask(mesh_t meshspec, SF::vector< RegionSpecs > ®spec, SF::vector< int > ®ionIDs, bool mask_elem, const char *reglist)
classify elements/points as belonging to a region
void init_stim_info(void)
uses potential for stimulation
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
SF::scattering * register_permutation(const int mesh_id, const int perm_id, const int dpn)
Register a permutation between two orderings for a mesh.
void setup_dataout(const int dataout, std::string dataout_vtx, mesh_t grid, SF::vector< mesh_int_t > *&restr, bool async)
void init_sv_gvec(gvec_data &GVs, limpet::MULTI_IF *miif, sf_vec &tmpl, igb_output_manager &output_manager)
void assemble_sv_gvec(gvec_data &gvecs, limpet::MULTI_IF *miif)
void indices_from_region_tag(SF::vector< mesh_int_t > &idx, const sf_mesh &mesh, const int tag)
Populate vertex data with the vertices of a given tag region.
char * dupstr(const char *old_str)
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
mesh_t
The enum identifying the different meshes we might want to load.
void get_time(double &tm)
bool mesh_is_registered(const mesh_t gt)
check wheter a SF mesh is set
SF::abstract_vector< SF_int, SF_real > sf_vec
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
const char * get_tsav_ext(double time)
void compute_IIF(limpet::IonIfBase &pIF, limpet::GlobalData_t **impdata, int n)
V timing(V &t2, const V &t1)
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
#define ALG_TO_NODAL
Scatter algebraic to nodal.
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
Electrical stimulation functions.
description of materal properties in a mesh
SF::vector< RegionSpecs > regions
array with region params
SF::vector< double > el_scale
optionally provided per-element params scale
region based variations of arbitrary material parameters
physMaterial * material
material parameter description
int nsubregs
#subregions forming this region
int * subregtags
FEM tags forming this region.
char * regname
name of region
double slvtime_A
total time in Step A
void log_stats(double time, bool cflg)
double minAT
minimum activation time in current solve
double maxAT
maximum activation time in current solve
void init_logger(const char *filename)
int activeList
number of nodes currently in list
void update_iter(const int curiter)
double slvtime_B
total time in Step B
double slvtime_D
total time in Step D
bool bc_status
boundary conditions were applied?
void update_cli(double time, bool cflg)
void log_stats(double tm, bool cflg)
void init_logger(const char *filename)
int calls
# calls for this interval, this is incremented externally
double tot_time
total time, this is incremented externally
void log_stats(double tm, bool cflg)
const char * reasonOut
reason for list entry
SF_real T_R
repolarization time
void init_logger(const char *filename)
void log_stats(double tm, bool cflg)
SF_real D_I
diastolic interval
mesh_int_t idXNB
neighboring node index responsible for list entry
const char * reasonIn
reason for list entry
SF_real T_A_
previous activation time
SF_real T_A
current activation time
SF_real nbn_T_A
activation time of neighboring node
mesh_int_t cycle
DREAM cycle.
void update_status(enum status s, enum reason r)