28 #include "petsc_utils.h"
36 #include "caliper/cali.h"
53 logger =
f_open(
"electrics.log", param_globals::experiment != 4 ?
"w" :
"a");
68 if (param_globals::bidomain || param_globals::extracell_monodomain_stim) {
77 param_globals::dt, 0,
"elec::ref_dt",
"TS");
97 if(strlen(param_globals::phie_rec_ptf) > 0)
104 if (param_globals::prepacing_bcl > 0)
114 m->
regions.resize(param_globals::num_gregions);
117 log_msg(logger, 0, 0,
"Setting up %s tissue properties for %d regions ..", grid_name,
118 param_globals::num_gregions);
123 for (
size_t i=0; i<m->
regions.size(); i++, reg++) {
124 if(!strcmp(param_globals::gregion[i].name,
"")) {
125 snprintf(buf,
sizeof buf,
", gregion_%d",
int(i));
126 param_globals::gregion[i].name =
dupstr(buf);
129 reg->
regname = strdup(param_globals::gregion[i].name);
131 reg->
nsubregs = param_globals::gregion[i].num_IDs;
137 reg->
subregtags[j] = param_globals::gregion[i].ID[j];
139 log_msg(NULL,3,
ECHO,
"Warning: not all %u IDs provided for gregion[%u]!\n", reg->
nsubregs, i);
147 emat->
InVal[0] = param_globals::gregion[i].g_il;
148 emat->
InVal[1] = param_globals::gregion[i].g_it;
149 emat->
InVal[2] = param_globals::gregion[i].g_in;
151 emat->
ExVal[0] = param_globals::gregion[i].g_el;
152 emat->
ExVal[1] = param_globals::gregion[i].g_et;
153 emat->
ExVal[2] = param_globals::gregion[i].g_en;
155 emat->
BathVal[0] = param_globals::gregion[i].g_bath;
156 emat->
BathVal[1] = param_globals::gregion[i].g_bath;
157 emat->
BathVal[2] = param_globals::gregion[i].g_bath;
160 for (
int j=0; j<3; j++) {
161 emat->
InVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
162 emat->
ExVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
163 emat->
BathVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
170 const char* file = g ==
Electrics::intra_grid ? param_globals::gi_scale_vec : param_globals::ge_scale_vec;
176 void Electrics::setup_mappings()
186 log_msg(
logger, 0, 0,
"%s: Setting up intracellular algebraic-to-nodal scattering.", __func__);
190 log_msg(
logger, 0, 0,
"%s: Setting up intracellular PETSc to canonical permutation.", __func__);
196 log_msg(
logger, 0, 0,
"%s: Setting up extracellular algebraic-to-nodal scattering.", __func__);
198 log_msg(
logger, 0, 0,
"%s: Setting up extracellular PETSc to canonical permutation.", __func__);
200 log_msg(
logger, 0, 0,
"%s: Setting up intra-to-extra scattering.", __func__);
204 bool check_i2e =
false;
205 if(check_i2e && extra_exists) {
223 SF_real*
id = intra_testvec->ptr();
224 for(
size_t i=0; i<intra_alg_nod.
size(); i++) {
226 id[lpidx] = intra_ref_nbr[intra_alg_nod[i]];
228 intra_testvec->release_ptr(
id);
230 SF_real* ed = extra_testvec->ptr();
231 for(
size_t i=0; i<extra_alg_nod.
size(); i++) {
233 ed[lpidx] = extra_ref_nbr[extra_alg_nod[i]];
235 extra_testvec->release_ptr(ed);
237 i2e_testvec->set(-1.0);
238 i2e.
forward(*intra_testvec, *i2e_testvec);
241 for(
size_t i=0; i<extra_alg_nod.
size(); i++) {
242 auto id = i2e_testvec->get(i);
243 auto ed = extra_testvec->get(i);
244 if(
id > -1 &&
id != ed)
249 log_msg(0,5,0,
"Electrics mapping test failed!");
251 log_msg(0,5,0,
"Electrics mapping test succeeded!");
276 stimulate_extracellular();
278 if(param_globals::bidomain ==
BIDOMAIN)
286 stimulate_intracellular();
289 if(param_globals::dump_data &
DUMP_IC)
300 if(param_globals::bidomain ==
BIDOMAIN)
330 if(param_globals::bidomain && (param_globals::dump_data &
DUMP_IACT)) {
334 if(param_globals::dump_data &
DUMP_IC) {
337 if (time <= time_step) {
339 Ic[i] = (Vmv[i] - Vmv[i]) / (-time_step);
343 Ic[i] = (Ic[i] - Vmv[i]) / (-time_step);
358 double curtime =
timing(t2, t1);
389 log_msg( NULL, 0, 0,
"Balancing stimulus %d with %d %s-wise.",balance_from, balance_to,
390 is_current(stimuli[balance_from].phys.type) ?
"current" :
"voltage" );
392 stimulus & from = stimuli[balance_from];
393 stimulus & to = stimuli[balance_to];
415 void Electrics::balance_electrodes()
417 for(
int i=0; i<param_globals::num_stim; i++) {
418 if(param_globals::stim[i].crct.balance != -1) {
419 int from = param_globals::stim[i].crct.balance;
427 void Electrics::setup_stimuli()
432 stimuli.resize(param_globals::num_stim);
433 for(
int i=0; i<param_globals::num_stim; i++)
443 if (s.electrode.dump_vtx)
446 if(param_globals::stim[i].pulse.dumpTrace &&
get_rank() == 0) {
448 s.pulse.wave.write_trace(s.name+
".trc");
455 double val; s.
value(val);
459 for (
size_t i = 0; i < idx.
size(); i++) {
462 vec.
set(local_idx, val, add,
true);
465 void Electrics::stimulate_intracellular()
476 if(param_globals::operator_splitting) {
487 *ps.tmp_i1 = *ps.IIon;
488 *ps.tmp_i1 -= *ps.Irhs;
493 ps.mass_i->mult(*ps.tmp_i1, *ps.Irhs);
495 *ps.Irhs = *ps.tmp_i1;
503 if(illum_vec == NULL) {
504 log_msg(0,5,0,
"Cannot apply illumination stim: global vector not present!");
519 void Electrics::clamp_Vm() {
521 if(s.phys.type ==
Vm_clmp && s.is_active())
526 void Electrics::stimulate_extracellular()
528 if(param_globals::bidomain) {
533 if(dbcs_have_updated && time_not_final)
538 for(
const stimulus & s :
stimuli) {
539 if(s.is_active() && s.phys.type ==
I_ex)
557 for(
int pid=0; pid < mpi_size; pid++) {
558 if(mpi_rank == pid) {
560 buffsize = sndbuff.
size();
563 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, PETSC_COMM_WORLD);
565 MPI_Bcast(sndbuff.
data(), buffsize*
sizeof(
mesh_int_t), MPI_BYTE, pid, PETSC_COMM_WORLD);
567 mesh_int_t start = layout[mpi_rank], stop = layout[mpi_rank+1];
570 if(i >= start && i < stop)
594 for(
int pid=0; pid < mpi_size; pid++) {
595 if(mpi_rank == pid) {
597 buffsize = sndbuff.
size();
600 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, PETSC_COMM_WORLD);
602 MPI_Bcast(sndbuff.
data(), buffsize*
sizeof(
mesh_int_t), MPI_BYTE, pid, PETSC_COMM_WORLD);
674 void Electrics::setup_output()
682 restr_i, param_globals::num_io_nodes > 0);
684 if(param_globals::dataout_i)
687 if(param_globals::bidomain) {
689 restr_e, param_globals::num_io_nodes > 0);
691 if(param_globals::dataout_i)
693 if(param_globals::dataout_e)
697 if(param_globals::dump_data &
DUMP_IC) {
712 if(param_globals::num_trace) {
714 open_trace(
ion.
miif, param_globals::num_trace, param_globals::trace_node, NULL, &imesh);
721 void Electrics::dump_matrices()
723 std::string bsname = param_globals::dump_basename;
729 if ( param_globals::parab_solve==1 ) {
731 fn = bsname +
"_Ki_CN.bin";
734 fn = bsname +
"_Ki.bin";
737 fn = bsname +
"_Mi.bin";
740 if ( param_globals::bidomain ) {
741 fn = bsname +
"_Kie.bin";
744 fn = bsname +
"_Me.bin";
761 val = std::nan(
"NaN");
775 s_unit =
stimuli[sidx].pulse.wave.f_unit;
788 for(
size_t i = 0; i<stimuli.
size(); i++)
812 double* reg_kappa =
new double[miif.
N_IIF];
814 for(
int i=0; i<miif.
N_IIF; i++)
815 reg_kappa[i] = k * miif.
IIF[i]->cgeom().SVratio * ir[i].volFrac;
817 double *kd = kappa.
ptr();
819 for(
int i = 0; i < miif.
numNode; i++)
820 kd[i] = reg_kappa[(
int) miif.
IIFmask[i]];
836 for(
size_t i=0; i < m.
regions.size(); i++) {
842 void Electrics::setup_solvers()
848 if (param_globals::bidomain) {
853 if(param_globals::dump2MatLab)
871 double min_diff = 1e100;
873 for(
int i=0; i<param_globals::num_tsav; i++)
875 double diff = fabs(param_globals::tsav[i] - time);
876 if(min_diff > diff) {
885 return param_globals::tsav_ext[min_idx];
888 void Electrics::checkpointing()
897 snprintf(save_fnm,
sizeof save_fnm,
"%s.%s.roe", param_globals::write_statef, tsav_ext);
905 snprintf(save_fnm,
sizeof save_fnm,
"checkpoint.%.1f.roe", tm.time);
940 mass_e ->
init(extra_mesh, dpn, dpn, param_globals::mass_lumping ? 1 : max_row_entries);
962 int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
983 log_msg(logger,0,log_flag,
"Computed ellipitc stiffness matrix in %.3f seconds.", dur);
987 log_msg(logger,0,log_flag,
"Elliptic lhs matrix enforcing Dirichlet boundaries.");
998 log_msg(logger,0,log_flag,
"Elliptic lhs matrix Dirichlet enforcing done in %.3f seconds.", dur);
1001 log_msg(logger,1,
ECHO,
"Elliptic lhs matrix is singular!");
1010 setup_linear_solver(logger);
1013 log_msg(logger,0,log_flag,
"Initializing elliptic solver in %.5f seconds.", dur);
1020 int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
1029 if(param_globals::mass_lumping) {
1036 log_msg(logger,0,log_flag,
"Computed elliptic mass matrix in %.3f seconds.", dur);
1039 void elliptic_solver::setup_linear_solver(
FILE_SPEC logger)
1043 tol = param_globals::cg_tol_ellip;
1044 max_it = param_globals::cg_maxit_ellip;
1046 std::string default_opts;
1047 std::string solver_file;
1048 solver_file = param_globals::ellip_options_file;
1049 if (param_globals::flavor == std::string(
"ginkgo")) {
1050 default_opts = std::string(
1052 "type": "solver::Cg",
1055 "type": "Iteration",
1059 "type": "ResidualNorm",
1060 "reduction_factor": 1e-4
1064 "type": "solver::Multigrid",
1067 "type": "multigrid::Pgm",
1068 "deterministic": true
1073 "type": "Iteration",
1077 "coarsest_solver": {
1078 "type": "preconditioner::Schwarz",
1080 "type": "preconditioner::Ilu"
1084 "min_coarse_rows": 8,
1085 "default_initial_guess": "zero"
1088 } else if (param_globals::flavor == std::string(
"petsc")) {
1089 default_opts = std::string(
"-ksp_type cg -pc_type hypre -pc_hypre_type boomeramg -pc_hypre_boomeramg_max_iter 1 -pc_hypre_boomeramg_strong_threshold 0.0 -options_left");
1094 logger, solver_file.c_str(), default_opts.c_str());
1108 Ki.mult(Vmv, tmp_i);
1120 auto dur =
timing(t1, t0);
1146 auto dur =
timing(t1, t0);
1178 if(!(vm_ptr != NULL && iion_ptr != NULL)) {
1179 log_msg(0,5,0,
"%s error: global Vm and Iion vectors not properly set up! Ionics seem invalid! Aborting!",
1205 if(!param_globals::operator_splitting)
1216 mass_i ->
init(intra_mesh, dpn, dpn, param_globals::mass_lumping ? 1 : max_row_entries);
1225 double start, end, period;
1231 int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
1240 if( (param_globals::bidomain ==
MONODOMAIN && param_globals::bidm_eqv_mono) ||
1255 log_msg(logger,0,log_flag,
"Computed parabolic stiffness matrix in %.3f seconds.", dur);
1258 if(param_globals::mass_lumping)
1267 log_msg(logger,0,log_flag,
"Computed parabolic mass matrix in %.3f seconds.", dur);
1275 bool same_nonzero = param_globals::mass_lumping ==
false;
1305 setup_linear_solver(logger);
1308 log_msg(logger,0,log_flag,
"Initializing parabolic solver in %.5f seconds.", dur);
1311 period =
timing(end, start);
1315 void parabolic_solver::setup_linear_solver(
FILE_SPEC logger)
1318 tol = param_globals::cg_tol_parab;
1319 max_it = param_globals::cg_maxit_parab;
1321 std::string default_opts;
1322 std::string solver_file;
1323 solver_file = param_globals::parab_options_file;
1324 if (param_globals::flavor == std::string(
"ginkgo")) {
1325 default_opts = std::string(
1328 "type": "solver::Cg",
1331 "type": "Iteration",
1335 "type": "ResidualNorm",
1336 "reduction_factor": 1e-4
1340 "type": "preconditioner::Schwarz",
1342 "type": "preconditioner::Ilu"
1347 } else if (param_globals::flavor == std::string(
"petsc")) {
1348 default_opts = std::string(
"-pc_type bjacobi -sub_pc_type ilu -ksp_type cg");
1352 "parabolic PDE",
false, logger, solver_file.c_str(),
1353 default_opts.c_str());
1360 case CN: solve_CN(phie_i);
break;
1361 case O2dT: solve_O2dT(phie_i);
break;
1362 default: solve_EF(phie_i);
break;
1366 void parabolic_solver::solve_CN(
sf_vec & phie_i)
1371 if (param_globals::bidomain ==
BIDOMAIN) {
1378 *
tmp_i2 *= 1.0 - param_globals::theta;
1385 if(!param_globals::operator_splitting)
1399 auto dur =
timing(t1, t0);
1405 void parabolic_solver::solve_O2dT(
sf_vec & phie_i)
1410 if (param_globals::bidomain ==
BIDOMAIN) {
1437 void parabolic_solver::solve_EF(
sf_vec & phie_i)
1444 if (param_globals::bidomain ==
BIDOMAIN) {
1456 if(param_globals::operator_splitting ==
false)
1465 char* prvSimDir = strlen(param_globals::start_statef) ?
1468 const char* extn =
".dat";
1471 int addLATs = param_globals::compute_APD ? 2 : 0;
1473 bool have_sentinel = param_globals::t_sentinel > 0.0;
1474 bool need_to_add_sentinel = have_sentinel && (param_globals::sentinel_ID < 0);
1476 addLATs += need_to_add_sentinel ? 1 : 0;
1477 acts.resize(param_globals::num_LATs + addLATs);
1480 for (
int i = 0; i < param_globals::num_LATs; i++ )
1483 if (param_globals::lats[i].method <= 0 || (param_globals::lats[i].measurand ==
PHIE && !param_globals::bidomain)) {
1484 log_msg(NULL, 3, 0,
"Phie-based LAT measurement requires bidomain >=1 Ignoring lats[%d].", i);
1489 acts[j].threshold = param_globals::lats[i].threshold;
1490 acts[j].mode = param_globals::lats[i].mode;
1491 acts[j].all = param_globals::lats[i].all;
1492 acts[j].measurand = (
PotType)param_globals::lats[i].measurand;
1493 acts[j].ID = param_globals::lats[i].ID;
1494 acts[j].fout = NULL;
1496 if(param_globals::lats[i].all) {
1497 acts[j].fname = (
char*) malloc((strlen(param_globals::lats[i].ID)+strlen(extn)+1)*
sizeof(
char));
1498 snprintf(
acts[j].fname, strlen(param_globals::lats[i].ID)+strlen(extn)+1,
"%s%s", param_globals::lats[i].ID, extn);
1501 char prfx[] =
"init_acts_";
1502 int max_len = strlen(prfx) + strlen(param_globals::lats[i].ID) + strlen(extn) + 1;
1504 acts[j].fname = (
char*) malloc(max_len*
sizeof(
char));
1505 snprintf(
acts[j].fname, max_len,
"%s%s%s", prfx, param_globals::lats[i].ID, extn);
1509 if(prvSimDir != NULL) {
1510 int len_fname = strlen(prvSimDir)+strlen(
acts[j].fname)+2;
1511 acts[j].prv_fname = (
char*) malloc(len_fname*
sizeof(
char));
1512 snprintf(
acts[j].prv_fname, len_fname,
"%s/%s", prvSimDir,
acts[j].fname);
1518 if(param_globals::compute_APD) {
1520 acts[j].threshold = param_globals::actthresh;
1525 acts[j].fout = NULL;
1530 acts[j].threshold = param_globals::recovery_thresh;
1536 acts[j].fname =
dupstr(
"vm_repolarisation.dat");
1546 sntl.
ID = param_globals::sentinel_ID;
1548 if(need_to_add_sentinel) {
1551 acts[j].threshold = param_globals::actthresh;
1556 acts[j].fout = NULL;
1563 if(prvSimDir) free(prvSimDir);
1568 const Activation & act = acts[idx];
1571 log_msg(logger, 0, 0,
"LAT detector [%2d]", idx);
1572 log_msg(logger, 0, 0,
"-----------------\n");
1574 log_msg(logger, 0, 0,
"Measurand: %s", act.measurand ?
"Phie" :
"Vm");
1575 log_msg(logger, 0, 0,
"All: %s", act.all ?
"All" :
"Only first");
1576 log_msg(logger, 0, 0,
"Method: %s", act.method==
ACT_DT ?
"Derivative" :
"Threshold crossing");
1578 char buf[64], gt[2], sgn[2];
1579 snprintf(sgn,
sizeof sgn,
"%s", act.mode?
"-":
"+");
1580 snprintf(gt,
sizeof gt,
"%s", act.mode?
"<":
">");
1583 snprintf(buf,
sizeof buf,
"Maximum %sdf/dt %s %.2f mV", sgn, gt, act.threshold);
1585 snprintf(buf,
sizeof buf,
"Intersection %sdf/dt with %.2f", sgn, act.threshold);
1587 log_msg(logger, 0, 0,
"Mode: %s", buf);
1588 log_msg(logger, 0, 0,
"Threshold: %.2f mV\n", act.threshold);
1594 log_msg(0,0,5,
"There seems to be no EP is defined. LAT detector requires active EP! Aborting LAT setup!");
1603 for(
size_t i = 0; i <
acts.size(); ++i) {
1606 acts[i].phi->shallow_copy(!
acts[i].measurand ? vm : phie);
1607 acts[i].offset = offset;
1617 log_msg(NULL,2,0,
"Detection of -df/dt|max not implemented, +df/dt|max will be detected.");
1621 acts[i].ibuf = (
int *)malloc(
acts[i].phi->lsize()*
sizeof(int));
1622 acts[i].actbuf = (
double *)malloc(
acts[i].phi->lsize()*
sizeof(double));
1626 acts[i].tm->set(-1.);
1629 if(
acts[i].prv_fname != NULL) {
1631 size_t nread =
acts[i].tm->read_ascii(
acts[i].prv_fname);
1635 log_msg(NULL,2,
ECHO,
"Warning: Initialization of LAT[%2d] failed.", i);
1640 (*sc)(*
acts[i].tm,
false);
1647 if(
acts[i].prv_fname!=NULL) {
1651 log_msg(NULL,2,0,
"Copying over of previous activation file not implemented.\n");
f_close(in);
1654 log_msg(NULL,3,0,
"Warning: Initialization in %s - \n"
1655 "Failed to read activation file %s.\n", __func__,
acts[i].prv_fname);
1676 for (
int i=0; i<nlacts; i++)
1677 fprintf(fp->fd,
"%d\t%.6f\n", ibuf[i], act_tbuf[i]);
1684 for (
int j=1; j<numProc; j++) {
1687 MPI_Recv(&acts, 1, MPI_INT, j, 110, PETSC_COMM_WORLD, &status);
1693 MPI_Recv(buf_inds.
data(), acts, MPI_INT, j, 110, PETSC_COMM_WORLD, &status);
1694 MPI_Recv(buf_acts.
data(), acts, MPI_DOUBLE, j, 110, PETSC_COMM_WORLD, &status);
1696 for(
int ii=0; ii<acts; ii++)
1697 fprintf(fp->fd,
"%d\t%.6f\n", buf_inds[ii], buf_acts[ii]);
1705 MPI_Send(&nlacts, 1, MPI_INT, 0, 110, PETSC_COMM_WORLD);
1707 MPI_Send(ibuf, nlacts, MPI_INT, 0, 110, PETSC_COMM_WORLD);
1708 MPI_Send(act_tbuf, nlacts, MPI_DOUBLE, 0, 110, PETSC_COMM_WORLD);
1712 MPI_Bcast(&gacts, 1, MPI_INT, 0, PETSC_COMM_WORLD);
1724 switch (aptr->method) {
1726 lacts = check_cross_threshold(*aptr->phi, *aptr->phip, tm,
1727 aptr->ibuf, aptr->actbuf, aptr->threshold, aptr->mode);
1731 lacts = check_mx_derivative (*aptr->phi, *aptr->phip, tm,
1732 aptr->ibuf, aptr->actbuf, *aptr->dvp0, *aptr->dvp1,
1733 aptr->threshold, aptr->mode);
1741 a = aptr->tm->ptr();
1745 for(
int j=0; j<lacts; j++) {
1748 aptr->ibuf[j] = canon_nbr[nodal_idx] + aptr->offset;
1751 if(a[aptr->ibuf[j]] == -1)
1752 a[aptr->ibuf[j]] = aptr->actbuf[j];
1759 aptr->tm->release_ptr(a);
1761 MPI_Allreduce(MPI_IN_PLACE, &lacts, 1, MPI_INT, MPI_SUM, PETSC_COMM_WORLD);
1764 aptr->nacts = nacts;
1773 static int savequitFlag = 0;
1774 int numNodesActivated = -1;
1779 log_msg(0,0,
ECHO |
NONL,
"================================================================================================\n");
1780 log_msg(0,0,
ECHO |
NONL,
"%s() WARNING: simulation is configured to savequit() after %.2f ms of quiescence\n", __func__,
sntl.
t_window);
1781 log_msg(0,0,
ECHO |
NONL,
"================================================================================================\n");
1799 return numNodesActivated;
1805 int LAT_detector::check_cross_threshold(
sf_vec & vm,
sf_vec & vmp,
double tm,
1806 int *ibuf,
double *actbuf,
float threshold,
int mode)
1810 int lsize = vm.lsize();
1811 int nacts = 0, gnacts = 0;
1813 for (
int i=0; i<lsize; i++) {
1815 bool triggered =
false;
1817 triggered = p[i] <= threshold && c[i] > threshold; }
1819 triggered = p[i] >= threshold && c[i] < threshold;
1824 double tact = tm - param_globals::dt + (threshold-p[i])/(c[i]-p[i])*sgn*param_globals::dt;
1826 actbuf[nacts] = tact;
1837 int LAT_detector::check_mx_derivative(
sf_vec & vm,
sf_vec & vmp,
double tm,
1838 int *ibuf,
double *actbuf,
sf_vec & dvp0,
sf_vec & dvp1,
1839 float threshold,
int mode)
1841 int nacts = 0, gnacts = 0;
1842 double tact, dt2 = 2 * param_globals::dt;
1843 int lsize = vm.lsize();
1852 for (
int i=0; i<lsize; i++ ) {
1854 dvdt = dv/param_globals::dt;
1855 ddv0 = pd1[i]-pd0[i];
1858 if (dvdt>=threshold && ddv0>0 && ddv1<0) {
1859 tact = tm-dt2+(ddv0/(ddv0-ddv1))*param_globals::dt;
1861 actbuf[nacts] = tact;
1870 vmp .release_ptr(p);
1871 dvp0.release_ptr(pd0);
1872 dvp1.release_ptr(pd1);
1885 bool forward =
true;
1887 for (
size_t i = 0; i <
acts.size(); i++) {
1889 (*sc)(*
acts[i].tm, forward);
1890 acts[i].tm->write_ascii(
acts[i].fname,
false);
1895 void Electrics::prepace() {
1896 log_msg(NULL, 0, 0,
"Using activation times from file %s to distribute prepacing states\n",
1897 param_globals::prepacing_lats);
1898 log_msg(NULL, 0, 0,
"Assuming stimulus strength %f uA/uF with duration %f ms for prepacing\n",
1899 param_globals::prepacing_stimstr, param_globals::prepacing_stimdur);
1908 size_t numread = read_lats->read_ascii(param_globals::prepacing_lats);
1910 log_msg(NULL, 5, 0,
"Failed reading required LATs! Skipping prepacing!");
1919 bool forward =
false;
1920 (*sc)(*read_lats, forward);
1924 PetscReal* lp = read_lats->ptr();
1925 for(
int i=0; i<read_lats->lsize(); i++)
1926 if(lp[i] < 0.0) lp[i] = param_globals::tend + 10.0;
1928 read_lats->release_ptr(lp);
1933 SF_real LATmin = read_lats->min();
1936 log_msg(0,3,0,
"LAT data is not complete. Skipping prepacing.");
1940 SF_real offset = floor(LATmin / param_globals::prepacing_bcl) * param_globals::prepacing_bcl;
1941 SF_real last_tm = param_globals::prepacing_bcl * param_globals::prepacing_beats;
1944 *read_lats += -offset;
1946 *read_lats += last_tm;
1949 SF_real *save_tm = read_lats->ptr();
1952 for (
int ii = 0; ii < miif->
N_IIF; ii++) {
1953 if (!miif->
N_Nodes[ii])
continue;
1958 sorted_save[kk].v1 = save_tm[miif->
NodeLists[ii][kk]];
1959 sorted_save[kk].v2 = kk;
1961 std::sort(sorted_save.
begin(), sorted_save.
end());
1963 size_t lastidx = sorted_save.
size() - 1;
1967 for (
double t = 0; t < sorted_save[lastidx].v1; t += param_globals::dt) {
1968 if (fmod(t, param_globals::prepacing_bcl) < param_globals::prepacing_stimdur &&
1969 t < param_globals::prepacing_bcl * param_globals::prepacing_beats - 1)
1970 miif->
ldata[ii][limpet::Vm][paced] += param_globals::prepacing_stimstr * param_globals::dt;
1975 miif->
ldata[ii][limpet::Vm][paced] -= miif->
ldata[ii][limpet::Iion][paced] * param_globals::dt;
1976 vm[miif->
NodeLists[ii][paced]] = miif->
ldata[ii][limpet::Vm][paced];
1978 while (csav < miif->N_Nodes[ii] - 1 && t >= sorted_save[csav].v1)
1983 while (csav < miif->N_Nodes[ii] - 1)
1989 read_lats->release_ptr(save_tm);
1998 if (!rcv.pts.size())
2004 log_msg(0,0,5,
"There seems to be no EP is defined. Phie recovery requires active EP! Aborting!");
2009 sf_mat & Ki = *elec->parab_solver.rhs_parab;
2015 vm.get_ownership_range(start, end);
2023 rcv.phie_rec->get_ownership_range(r_start, r_end);
2025 SF_real *ph_r = rcv.phie_rec->ptr();
2030 float minDist = 2. / param_globals::imp_region[0].cellSurfVolRatio;
2032 Ki.mult(vm, *rcv.Im);
2033 int numpts = rcv.pts.size() / 3;
2036 for (
int j=0; j<numpts; j++) {
2037 fpt = rcv.pts.data() + j*3;
2039 *rcv.dphi = *rcv.Im;
2040 SF_real* dp = rcv.dphi->ptr();
2042 for (
size_t i = 0; i<alg_nod.
size(); i++)
2046 cpt = imesh.xyz.data()+loc_nodal_idx*3;
2048 double r =
dist(fpt, cpt) + minDist;
2049 dp[loc_petsc_idx] /= r;
2052 rcv.dphi->release_ptr(dp);
2054 SF_real phi = rcv.dphi->sum() / 4. /
M_PI / rcv.gBath;
2055 if ( (j>=r_start) && (j<r_end) )
2056 ph_r[j-r_start] = phi;
2059 rcv.phie_rec->release_ptr(ph_r);
2067 log_msg(0,0,5,
"There seems to be no EP is defined. Phie recovery requires active EP! Aborting!");
2073 phie_recovery_data & phie_rcv = elec->phie_rcv;
2076 elec->output_manager.close_files_and_cleanup();
2080 igb_output_manager phie_rec_out;
2081 phie_rec_out.register_output(phie_rcv.phie_rec,
phie_recv_msh, 1,
2082 param_globals::phie_recovery_file,
"mV");
2096 if(vm_igb.
x() != vm->gsize()) {
2097 log_msg(0,4,0,
"%s error: Vm dimension does not fit to %s file. Aborting recovery! \n",
2098 __func__, param_globals::vofile);
2110 FILE* fd =
static_cast<FILE*
>(vm_igb.
fileptr());
2117 assert(petsc_to_canonical != NULL);
2120 for(
int i=0; i<num_io; i++) {
2122 size_t nread = vm->read_binary<
float>(fd);
2124 if(nread !=
size_t(vm->gsize())) {
2125 log_msg(0,3,0,
"%s warning: read incomplete data slice! Aborting!", __func__);
2131 bool forward =
false;
2132 (*petsc_to_canonical)(*vm, forward);
2137 phie_rec_out.write_data();
2140 phie_rec_out.close_files_and_cleanup();
2149 log_msg(0,0,5,
"There seems to be no EP is defined. Phie recovery requires active EP! Aborting!");
2157 const std::string basename = param_globals::phie_rec_ptf;
2173 data.gBath =
static_cast<elecMaterial*
>(intra_regions[0].material)->BathVal[0];
2182 assert(param_globals::bidomain ==
BIDOMAIN);
2207 stimuli.resize(param_globals::num_stim);
2209 for(
int i=0; i<param_globals::num_stim; i++) {
2229 if(param_globals::dump2MatLab) {
2230 std::string bsname = param_globals::dump_basename;
2234 fn = bsname +
"_Kie.bin";
2245 restr_e, param_globals::num_io_nodes > 0);
2246 if(param_globals::dataout_e)
2251 restr_i, param_globals::num_io_nodes > 0);
2252 if(param_globals::dataout_i)
2271 log_msg(0,0,0,
"Solving Laplace problem ..");
2277 log_msg(0,0,0,
"Done in %.5f seconds.", dur);
2300 if(sidx != -1)
stimuli[sidx].value(val);
2301 else val = std::nan(
"NaN");
2309 if(sidx != -1) s_unit =
stimuli[sidx].pulse.wave.f_unit;
2321 for(stimulus & s : stimuli) {
2322 if(
is_current(s.phys.type) && s.phys.total_current) {
2324 if (s.phys.type ==
I_ex) {
2333 float scale = 1.e12/vol;
2335 s.pulse.strength *= scale;
2338 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^3\n",
2339 s.name.c_str(), s.idx, s.pulse.strength);
2341 else if (s.phys.type ==
I_tm) {
2347 if(alg_idx_map.
size() == 0) {
2350 alg_idx_map[n] = lidx;
2357 if(alg_idx_map.
count(n)) {
2361 surf = vol * miif->
IIF[r]->cgeom().SVratio * param_globals::imp_region[r].volFrac;
2367 surf =
get_global(surf, MPI_MAX, PETSC_COMM_WORLD);
2370 s.pulse.strength /= surf;
2372 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^2\n",
2373 s.name.c_str(), s.idx, s.pulse.strength);
opencarp::local_index_t mesh_int_t
opencarp::real_t SF_real
Global scalar type.
opencarp::global_index_t SF_int
Global algebraic index type.
#define CALI_CXX_MARK_FUNCTION
virtual void mult(const abstract_vector< T, S > &x, abstract_vector< T, S > &b) const =0
virtual void scale(S s)=0
virtual void get_diagonal(abstract_vector< T, S > &vec) const =0
virtual void mult_LR(const abstract_vector< T, S > &L, const abstract_vector< T, S > &R)=0
virtual void init(T iNRows, T iNCols, T ilrows, T ilcols, T loc_offset, T mxent)
virtual void duplicate(const abstract_matrix< T, S > &M)=0
virtual void add_scaled_matrix(const abstract_matrix< T, S > &A, const S s, const bool same_nnz)=0
virtual void write(const char *filename) const =0
virtual void release_ptr(S *&p)=0
virtual void deep_copy(const abstract_vector< T, S > &v)=0
virtual void shallow_copy(const abstract_vector< T, S > &v)=0
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
virtual T lsize() const =0
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false, const bool local=false)=0
const meshdata< mesh_int_t, mesh_real_t > * mesh
the connected mesh
T forward_map(T idx) const
Map one index from a to b.
overlapping_layout< T > pl
nodal parallel layout
vector< T > & get_numbering(SF_nbr nbr_type)
Get the vector defining a certain numbering.
Container for a PETSc VecScatter.
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
void backward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Backward scattering.
size_t size() const
The current size of the vector.
void resize(size_t n)
Resize a vector.
const T * end() const
Pointer to the vector's end.
const T * begin() const
Pointer to the vector's start.
T * data()
Pointer to the vector's start.
hm_int count(const K &key) const
Check if key exists.
std::vector< IonIfBase * > IIF
array of IIF's
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
node_count_t numNode
local number of nodes
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
node_count_t * N_Nodes
#nodes for each IMP
node_index_t ** NodeLists
local partitioned node lists for each IMP stored
IIF_Mask_t * IIFmask
region for each node
int timer_idx
the timer index received from the timer manager
FILE_SPEC logger
The logger of the physic, each physic should have one.
SF::vector< stimulus > stimuli
the electrical stimuli
LAT_detector lat
the activation time detector
grid_t
An electrics grid identifier to distinguish between intra and extra grids.
phie_recovery_data phie_rcv
struct holding helper data for phie recovery
generic_timing_stats IO_stats
void destroy()
Currently we only need to close the file logger.
gvec_data gvec
datastruct holding global IMP state variable output
elliptic_solver ellip_solver
Solver for the elliptic bidomain equation.
MaterialType mtype[2]
the material types of intra_grid and extra_grid grids.
std::string timer_unit(const int timer_id)
figure out units of a signal linked to a given timer
parabolic_solver parab_solver
Solver for the parabolic bidomain equation.
double timer_val(const int timer_id)
figure out current value of a signal linked to a given timer
void initialize()
Initialize the Electrics.
igb_output_manager output_manager
class handling the igb output
SF::index_mapping< mesh_int_t > petsc_to_nodal
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< Activation > acts
LAT_detector()
constructor, sets up basic datastructs from global_params
SF::vector< stimulus > stimuli
the electrical stimuli
elliptic_solver ellip_solver
Solver for the elliptic bidomain equation.
double timer_val(const int timer_id)
figure out current value of a signal linked to a given timer
std::string timer_unit(const int timer_id)
figure out units of a signal linked to a given timer
MaterialType mtype[2]
the material types of intra_grid and extra_grid grids.
igb_output_manager output_manager
class handling the igb output
manager for dirichlet boundary conditions
void enforce_dbc_rhs(sf_vec &rhs)
void recompute_dbcs()
recompute the dbc data.
bool dbc_update()
check if dbcs have updated
sf_mat * phie_mat
lhs matrix to solve elliptic
void rebuild_stiffness(MaterialType *mtype, SF::vector< stimulus > &stimuli, FILE_SPEC logger)
void rebuild_matrices(MaterialType *mtype, SF::vector< stimulus > &stimuli, FILE_SPEC logger)
sf_vec * phie_i
phi_e on intracellular grid
void solve(sf_mat &Ki, sf_vec &Vmv, sf_vec &tmp_i)
sf_sol * lin_solver
petsc or ginkgo lin_solver
sf_mat * mass_e
mass matrix for RHS elliptic calc
double tol
CG stopping tolerance.
bool phie_mat_has_nullspace
sf_vec * currtmp
temp vector for phiesrc
dbc_manager * dbc
dbcs require a dbc manager
int max_it
maximum number of iterations
void rebuild_mass(FILE_SPEC logger)
void write_data()
write registered data to disk
void register_output_sync(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)
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_vec * Ivol
global Vm vector
double tol
CG stopping tolerance.
sf_vec * Iact
global Vm vector
sf_vec * Diff_term
Diffusion current.
sf_mat * rhs_parab
rhs matrix to solve parabolic
sf_vec * kappa_i
scaling vector for intracellular mass matrix, M
void rebuild_matrices(MaterialType *mtype, limpet::MULTI_IF &miif, FILE_SPEC logger)
parabolic_t parab_tech
manner in which parabolic equations are solved
void solve(sf_vec &phie_i)
sf_vec * inv_mass_diag
inverse diagonal of mass matrix, for EXPLICIT solving
sf_mat * mass_i
lumped for parabolic problem
sf_vec * Ic
global Vm vector
sf_vec * tmp_i2
scratch vector for i-grid
int max_it
maximum number of iterations
sf_vec * tmp_i1
scratch vector for i-grid
sf_mat * lhs_parab
lhs matrix (CN) to solve parabolic
sf_vec * Vmv
global Vm vector
sf_vec * Irhs
weighted transmembrane currents
sf_vec * old_vm
older Vm needed for 2nd order dT
sf_sol * lin_solver
petsc or ginkgo lin_solver
sf_vec * IIon
ionic currents
SF::vector< mesh_int_t > vertices
bool total_current
whether we apply total current scaling
stim_t type
type of stimulus
int timer_id
timer for stimulus
waveform_t wform
wave form of stimulus
double strength
strength of stimulus
stim_protocol ptcl
applied stimulation protocol used
int idx
index in global input stimulus array
stim_electrode electrode
electrode geometry
stim_pulse pulse
stimulus wave form
void translate(int id)
convert legacy definitions to new format
void setup(int idx)
Setup from a param stimulus index.
stim_physics phys
physics of stimulus
bool value(double &v) const
Get the current value if the stimulus is active.
long d_time
current time instance index
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.
int add_singlestep_timer(double tg, double idur, const char *iname, const char *poolname=nullptr)
long d_end
final index in multiples of dt
std::vector< base_timer * > timers
vector containing individual timers
Tissue level electrics, main Electrics physics class.
void init_solver(SF::abstract_linear_solver< T, S > **sol)
void compute_surface_mesh(const meshdata< T, S > &mesh, const SF_nbr numbering, const hashmap::unordered_set< T > &tags, meshdata< T, S > &surfmesh)
Compute the surface of a given mesh.
void read_points(const std::string basename, const MPI_Comm comm, vector< S > &pts, vector< T > &ptsidx)
Read the points and insert them into a list of meshes.
void make_global(const vector< T > &vec, vector< T > &out, MPI_Comm comm)
make a parallel vector global
void unique_resize(vector< T > &_P)
void assemble_matrix(abstract_matrix< T, S > &mat, meshdata< mesh_int_t, mesh_real_t > &domain, matrix_integrator< mesh_int_t, mesh_real_t > &integrator)
Generalized matrix assembly.
int max_nodal_edgecount(const meshdata< T, S > &mesh)
Compute the maximum number of node-to-node edges for a mesh.
void local_petsc_to_nodal_mapping(const meshdata< T, S > &mesh, index_mapping< T > &petsc_to_nodal)
T local_nodal_to_local_petsc(const meshdata< T, S > &mesh, int rank, T local_nodal)
void assemble_lumped_matrix(abstract_matrix< T, S > &mat, meshdata< mesh_int_t, mesh_real_t > &domain, matrix_integrator< mesh_int_t, mesh_real_t > &integrator)
bool is_init(const abstract_vector< T, S > *v)
void layout_from_count(const T count, vector< T > &layout, MPI_Comm comm)
void init_vector(SF::abstract_vector< T, S > **vec)
void binary_sort(vector< T > &_V)
void init_matrix(SF::abstract_matrix< T, S > **mat)
@ NBR_PETSC
PETSc numbering of nodes.
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
@ NBR_SUBMESH
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
void dup_IMP_node_state(IonIfBase &IF, node_index_t from, node_index_t to, GlobalData_t **localdata)
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.
opencarp::local_index_t node_index_t
timer_manager * tm_manager
a manager for the various physics timers
bool using_legacy_stimuli
flag storing whether legacy stimuli are used
void get_kappa(sf_vec &kappa, IMPregion *ir, limpet::MULTI_IF &miif, double k)
compute the vector
void compute_IIF(limpet::IonIfBase &pIF, limpet::GlobalData_t **impdata, limpet::node_index_t n)
physic_t
Identifier for the different physics we want to set up.
int stimidx_from_timeridx(const SF::vector< stimulus > &stimuli, const int timer_id)
determine link between timer and stimulus
sf_vec * get_data(datavec_t d)
Retrieve a petsc data vector from the data registry.
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.
void set_cond_type(MaterialType &m, cond_t type)
void sample_wave_form(stim_pulse &sp, int idx)
sample a signal given in analytic form
void read_el_scale_vec(const char *file, mesh_t mt, SF::vector< double > &el_scale, int &el_scale_dpn)
SF_real get_volume_from_nodes(sf_mat &mass, SF::vector< mesh_int_t > &local_idx)
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 ...
cond_t
description of electrical tissue properties
void print_act_log(FILE_SPEC logger, const SF::vector< Activation > &acts, int idx)
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.
void region_mask(mesh_t meshspec, SF::vector< RegionSpecs > ®spec, SF::vector< int > ®ionIDs, bool mask_elem, const char *reglist, bool warn_on_default_tags)
classify elements/points as belonging to a region
bool is_dbc(stim_t type)
whether stimulus is a dirichlet type. implies boundary conditions on matrix
SF::meshdata< mesh_int_t, mesh_real_t > sf_mesh
void compute_restr_idx_async(sf_mesh &mesh, SF::vector< mesh_int_t > &inp_idx, SF::vector< mesh_int_t > &idx)
void apply_stim_to_vector(const stimulus &s, sf_vec &vec, bool add)
void recover_phie_std(sf_vec &vm, phie_recovery_data &rcv)
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
T get_global(T in, MPI_Op OP, MPI_Comm comm=PETSC_COMM_WORLD)
Do a global reduction on a variable.
V dist(const vec3< V > &p1, const vec3< V > &p2)
void init_stim_info(void)
uses potential for stimulation
int output_all_activations(FILE_SPEC fp, int *ibuf, double *act_tbuf, int nlacts)
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
void savequit()
save state and quit simulator
bool have_dbc_stims(const SF::vector< stimulus > &stimuli)
return wheter any stimuli require dirichlet boundary conditions
SF::scattering * register_permutation(const int mesh_id, const int perm_id, const int dpn)
Register a permutation between two orderings for a mesh.
bool is_current(stim_t type)
uses current as stimulation
void setup_dataout(const int dataout, std::string dataout_vtx, mesh_t grid, SF::vector< mesh_int_t > *&restr, bool async)
char * get_file_dir(const char *file)
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 constant_total_stimulus_current(SF::vector< stimulus > &stimuli, sf_mat &mass_i, sf_mat &mass_e, limpet::MULTI_IF *miif, FILE_SPEC logger)
Scales stimulus current to maintain constant total current across affected regions.
int postproc_recover_phie()
char * dupstr(const char *old_str)
void balance_electrode(elliptic_solver &ellip, SF::vector< stimulus > &stimuli, int balance_from, int balance_to)
void set_elec_tissue_properties(MaterialType *mtype, Electrics::grid_t g, FILE_SPEC logger)
Fill the RegionSpec of an electrics grid with the associated inputs from the param parameters.
void compute_restr_idx(sf_mesh &mesh, SF::vector< mesh_int_t > &inp_idx, SF::vector< mesh_int_t > &idx)
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
void setup_phie_recovery_data(phie_recovery_data &data)
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)
SF::abstract_matrix< SF_int, SF_real > sf_mat
V timing(V &t2, const V &t1)
void read_indices(SF::vector< T > &idx, const std::string filename, const hashmap::unordered_map< mesh_int_t, mesh_int_t > &dd_map, MPI_Comm comm)
Read indices from a file.
void update_cwd()
save the current working directory to curdir so that we can switch back to it if needed.
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
#define UM2_to_CM2
convert um^2 to cm^2
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
#define ALG_TO_NODAL
Scatter algebraic to nodal.
Electrical stimulation functions.
SF_int niter
number of iterations
SF_real time
solver runtime
SF_int reason
number of iterations
std::string name
the solver name
virtual void setup_solver(abstract_matrix< T, S > &mat, double tol, int max_it, short norm, std::string name, bool has_nullspace, void *logger, const char *solver_opts_file, const char *default_opts)=0
event detection data structures
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
int el_scale_dpn
0=disabled, 1=isotropic scalar, 3=anisotropic (sl, st, sn) per element
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
bool activated
flag sentinel activation
int ID
ID of LAT detector used as sentinel.
double t_start
start of observation window
double t_window
duration of observation window
double t_quiesc
measure current duration of quiescence
double ExVal[3]
extracellular conductivity eigenvalues
cond_t g
rule to build conductivity tensor
double InVal[3]
intracellular conductivity eigenvalues
double BathVal[3]
bath conductivity eigenvalues
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 init_logger(const char *filename)
void log_stats(double tm, bool cflg)
void update_iter(const int curiter)
double slvtime
total solver time
sf_vec * phie_rec
The phie recovery output vector buffer.
SF::vector< mesh_real_t > pts
The phie recovery locations.
physMat_t material_type
ID of physics material.