int NonLinImpRep::gapsolve() { // On entry, rv_ and jv_ contain the complex b for A*x = b. // On return rv_ and jv_ contain complex solution, x. // m_ is the factored matrix for the trees without gap junctions // Jacobi method (easy for parallel) // A = D + R // D*x_(k+1) = (b - R*x_(k)) // D is m_ (and includes the gap junction contribution to the diagonal) // R is the off diagonal matrix of the gaps. // one and only one stimulus #if NRNMPI if (nrnmpi_numprocs > 1 && nrnmpi_int_sum_reduce(iloc_ >= 0 ? 1 : 0) != 1) { if (nrnmpi_myid == 0) { hoc_execerror("there can be one and only one impedance stimulus", 0); } } #endif double *rx, *jx, *rx1, *jx1, *rb, *jb; if (neq_) { rx = new double[neq_]; jx = new double[neq_]; rx1 = new double[neq_]; jx1 = new double[neq_]; rb = new double[neq_]; jb = new double[neq_]; } // initialize for first iteration for (int i=0; i < neq_; ++i) { rx[i] = jx[i] = 0.0; rb[i] = rv_[i]; jb[i] = jv_[i]; } pargap_jacobi_setup(0); // iterate till change in x is small double tol = 1e-9; double delta; int success = 0; int iter; for (iter = 1; iter <= maxiter_; ++iter) { if (neq_) { cmplx_spSolve(m_, rb-1, rx1-1, jb-1, jx1-1); } // if any change in x > tol, then do another iteration. success = 1; delta = 0.0; for (int i=0; i < neq_; ++i) { double err = fabs(rx1[i] - rx[i]) + fabs(jx1[i] - jx[i]); if (err > tol) { success = 0; } if (delta < err) { delta = err; } } #if NRNMPI if (nrnmpi_numprocs > 1) { success = nrnmpi_int_sum_reduce(success) / nrnmpi_numprocs; } #endif if (success) { for (int i=0; i < neq_; ++i) { rv_[i] = rx1[i]; jv_[i] = jx1[i]; } break; } // setup for next iteration for (int i=0; i < neq_; ++i) { rx[i] = rx1[i]; jx[i] = jx1[i]; rb[i] = rv_[i]; jb[i] = jv_[i]; } pargap_jacobi_rhs(rb, rx); pargap_jacobi_rhs(jb, jx); } pargap_jacobi_setup(1); // tear down if (neq_) { delete [] rx; delete [] jx; delete [] rx1; delete [] jx1; delete [] rb; delete [] jb; } if (!success) { char buf[256]; sprintf(buf, "Impedance calculation did not converge in %d iterations. Max state change on last iteration was %g (Iterations stop at %g)\n", maxiter_, delta, tol); execerror(buf, 0); } return iter; }
void Cvode::init_eqn(){ double vtol; NrnThread* _nt; CvMembList* cml; Memb_list* ml; Memb_func* mf; int i, j, zneq, zneq_v, zneq_cap_v; //printf("Cvode::init_eqn\n"); if (nthsizes_) { delete [] nthsizes_; nthsizes_ = 0; } neq_ = 0; for (int id = 0; id < nctd_; ++id) { CvodeThreadData& z = ctd_[id]; z.cmlcap_ = nil; z.cmlext_ = nil; for (cml = z.cv_memb_list_; cml; cml = cml->next) { if (cml->index == CAP) { z.cmlcap_ = cml; } if (cml->index == EXTRACELL) { z.cmlext_ = cml; } } } if (use_daspk_) { daspk_init_eqn(); return; } FOR_THREADS(_nt) { // for lvardt, this body done only once and for ctd_[0] CvodeThreadData& z = ctd_[_nt->id]; // how many ode's are there? First ones are non-zero capacitance // nodes with non-zero capacitance zneq_cap_v = z.cmlcap_ ? z.cmlcap_->ml->nodecount : 0; zneq = zneq_cap_v; // now add the membrane mechanism ode's to the count for (cml = z.cv_memb_list_; cml; cml = cml->next) { Pfridot s = (Pfridot)memb_func[cml->index].ode_count; if (s) { zneq += cml->ml->nodecount * (*s)(cml->index); } } //printf("%d Cvode::init_eqn neq_v=%d zneq_=%d\n", nrnmpi_myid, neq_v, zneq_); if (z.pv_) { delete [] z.pv_; delete [] z.pvdot_; z.pv_ = 0; z.pvdot_ = 0; } if (zneq) { z.pv_ = new double*[zneq]; z.pvdot_ = new double*[zneq]; } z.nvoffset_ = neq_; z.nvsize_ = zneq; neq_ += zneq; if (nth_) { break; } //lvardt } #if PARANEURON if (use_partrans_) { global_neq_ = nrnmpi_int_sum_reduce(neq_, mpicomm_); //printf("%d global_neq_=%d neq=%d\n", nrnmpi_myid, global_neq_, neq_); } #endif atolvec_alloc(neq_); for (int id = 0; id < nctd_; ++id) { CvodeThreadData& z = ctd_[id]; double* atv = n_vector_data(atolnvec_, id); zneq_cap_v = z.cmlcap_ ? z.cmlcap_->ml->nodecount : 0; zneq = z.nvsize_; zneq_v = zneq_cap_v; for (i=0; i < zneq; ++i) { atv[i] = ncv_->atol(); } vtol = 1.; if (!vsym) { vsym = hoc_table_lookup("v", hoc_built_in_symlist); } if (vsym->extra) { double x; x = vsym->extra->tolerance; if (x != 0 && x < vtol) { vtol = x; } } for (i=0; i < zneq_cap_v; ++i) { atv[i] *= vtol; } // deal with voltage nodes // only cap nodes for cvode for (i=0; i < z.v_node_count_; ++i) { //sentinal values for determining no_cap NODERHS(z.v_node_[i]) = 1.; } for (i=0; i < zneq_cap_v; ++i) { ml = z.cmlcap_->ml; z.pv_[i] = &NODEV(ml->nodelist[i]); z.pvdot_[i] = &(NODERHS(ml->nodelist[i])); *z.pvdot_[i] = 0.; // only ones = 1 are no_cap } // the remainder are no_cap nodes if (z.no_cap_node_) { delete [] z.no_cap_node_; delete [] z.no_cap_child_; } z.no_cap_node_ = new Node*[z.v_node_count_ - zneq_cap_v]; z.no_cap_child_ = new Node*[z.v_node_count_ - zneq_cap_v]; z.no_cap_count_ = 0; j = 0; for (i=0; i < z.v_node_count_; ++i) { if (NODERHS(z.v_node_[i]) > .5) { z.no_cap_node_[z.no_cap_count_++] = z.v_node_[i]; } if (z.v_parent_[i] && NODERHS(z.v_parent_[i]) > .5) { z.no_cap_child_[j++] = z.v_node_[i]; } } z.no_cap_child_count_ = j; // use the sentinal values in NODERHS to construct a new no cap membrane list new_no_cap_memb(z, _nt); // map the membrane mechanism ode state and dstate pointers int ieq = zneq_v; for (cml = z.cv_memb_list_; cml; cml = cml->next) { int n; ml = cml->ml; mf = memb_func + cml->index; Pfridot sc = (Pfridot)mf->ode_count; if (sc && ( (n = (*sc)(cml->index)) > 0)) { Pfridot s = (Pfridot)mf->ode_map; if (mf->hoc_mech) { for (j=0; j < ml->nodecount; ++j) { (*s)(ieq, z.pv_ + ieq, z.pvdot_ + ieq, ml->prop[j], atv + ieq); ieq += n; } }else{ for (j=0; j < ml->nodecount; ++j) { (*s)(ieq, z.pv_ + ieq, z.pvdot_ + ieq, ml->data[j], ml->pdata[j], atv + ieq, cml->index); ieq += n; } } } } } structure_change_ = false; }