1: // 2: // Heccer : a compartmental solver that implements efficient Crank-Nicolson 3: // integration for neuronal models. 4: // 5: 6: ////////////////////////////////////////////////////////////////////////////// 7: //' 8: //' Heccer : testbed C implementation 9: //' 10: //' Copyright (C) 2006-2008 Hugo Cornelis 11: //' 12: //' functional ideas .. Hugo Cornelis, hugo.cornelis@gmail.com 13: //' 14: //' coding ............ Hugo Cornelis, hugo.cornelis@gmail.com 15: //' 16: ////////////////////////////////////////////////////////////////////////////// 17: 18: 19: #include <stdio.h> 20: #include <stdlib.h> 21: #include <string.h> 22: 23: #include "heccer/compartment.h" 24: #include "heccer/heccer.h" 25: 26: 27: /// ************************************************************************** 28: /// 29: /// SHORT: HeccerCompileCompartments() 30: /// 31: /// ARGS.: 32: /// 33: /// pheccer...: a heccer. 34: /// 35: /// RTN..: int 36: /// 37: /// success of operation. 38: /// 39: /// DESCR: Compile the intermediary of the compartments to byte code. 40: /// 41: /// ************************************************************************** 42: 43: int HeccerCompartmentCompile(struct Heccer *pheccer) 44: { 45: //- set default result : ok 46: 47: int iResult = TRUE; 48: 49: //v time step 50: 51: double dt; 52: 53: //- for backward euler integration 54: 55: if (pheccer->ho.iOptions & HECCER_OPTION_BACKWARD_EULER) 56: { 57: //- remember to do full time step 58: 59: dt = pheccer->dStep; 60: } 61: 62: //- else : crank-nicholson 63: 64: else 65: { 66: //- halve the time step 67: 68: dt = pheccer->dStep / 2.0; 69: } 70: 71: //v make minimum degree indices accessible 72: 73: int *piChildren = pheccer->indexers.md.piChildren; 74: 75: int **ppiChildren = pheccer->indexers.md.ppiChildren; 76: 77: int *piForward = pheccer->indexers.md.piForward; 78: 79: int *piBackward = pheccer->indexers.md.piBackward; 80: 81: //- create storage for diagterms 82: 83: pheccer->vm.iDiagonals = pheccer->inter.iCompartments; 84: 85: double *pdDiagonals = (double *)calloc(pheccer->vm.iDiagonals, sizeof(double)); 86: 87: pheccer->vm.pdDiagonals = pdDiagonals; 88: 89: //- loop over all compartments (in flow order) 90: 91: int i; 92: 93: for (i = 0 ; i < pheccer->inter.iCompartments ; i++) 94: { 95: //- get intermediary number for the current compartment 96: 97: int iIntermediary = piBackward[i]; 98: 99: //- get compartment parameters 100: 101: double dCm = pheccer->inter.pcomp[iIntermediary].dCm; 102: 103: double dRm = pheccer->inter.pcomp[iIntermediary].dRm; 104: 105: //- compute diagonal term : time constant 106: 107: pdDiagonals[i] = 1.0 + dt / (dCm * dRm); 108: 109: #ifdef DEBUG_OPERATIONS_DIAGONALS 110: 111: fprintf(stdout, "pdDiagonals[%i] : %e\t= 1.0 + %e / (%e * %e)\n", i, pdDiagonals[i], dt, dCm, dRm); 112: 113: #endif 114: 115: //- get compartment's parent number 116: 117: int iParent = pheccer->inter.pcomp[iIntermediary].iParent; 118: 119: //- get number of children of current compartment 120: 121: int iChildren = piChildren[iIntermediary]; 122: 123: //- loop over all children 124: 125: int iChild; 126: 127: for (iChild = 0 ; iChild < iChildren ; iChild++) 128: { 129: //- get index number for child of current compartment 130: 131: int iModelNumber = ppiChildren[iIntermediary][iChild]; 132: 133: //- compute reverse contribution (genesis raxial) 134: 135: //t check old hsolve code, this computation seems 136: //t contradictory to me, mixes up child and parent 137: //t properties. ... 138: //t ... ok, done, perhaps this is a bug in hsolve ? 139: //t have to check the computations carefully again. 140: 141: double dReverse = -dt / (pheccer->inter.pcomp[iIntermediary].dCm * pheccer->inter.pcomp[iModelNumber].dRa); 142: 143: pdDiagonals[i] -= dReverse; 144: 145: #ifdef DEBUG_OPERATIONS_DIAGONALS 146: 147: fprintf(stdout, "pdDiagonals[%i] : %e (- %e)\n", i, pdDiagonals[i], dReverse); 148: 149: #endif 150: 151: //- compute verse contribution, comes from child (genesis axial) 152: 153: double dVerse = -dt / (pheccer->inter.pcomp[iModelNumber].dCm * pheccer->inter.pcomp[iModelNumber].dRa); 154: 155: //- convert child compartment number to schedule number 156: 157: int iScheduleNumber = piForward[iModelNumber]; 158: 159: pdDiagonals[iScheduleNumber] -= dVerse; 160: 161: #ifdef DEBUG_OPERATIONS_DIAGONALS 162: 163: fprintf(stdout,"pdDiagonals[%i] : %e (- %e)\n", iScheduleNumber, pdDiagonals[i], dVerse); 164: 165: #endif 166: } 167: } 168: 169: #define SETCOP1(array,current,operator) ((array) ? (array)[(current)++] = (operator) : (current)++) 170: 171: #define SETCOP2(array,current,operator,operand) ((array) ? ({(array)[(current)++] = (operator); (array)[(current)++] = (operand); }) : ({ (current) += 2; })) 172: 173: #define SETAXRES(array,current,value) ((array) ? ((array)[(current)++] = (value)) : (current)++) 174: 175: //- first count, next compile the following block 176: 177: int iCountCompile; 178: 179: //- first counting by setting array to NULL 180: 181: int *piCops = NULL; 182: 183: double *pdAxres = NULL; 184: 185: for (iCountCompile = 0 ; iCountCompile < 2 ; iCountCompile++) 186: { 187: //- counters always start at zero 188: 189: int iCops = 0; 190: 191: int iAxres = 0; 192: 193: //v for leaves in the mid of the array, immediate skip to diagonal 194: 195: int iSkipped = 0; 196: 197: //- loop over all compartments : start forward elimination 198: 199: int i; 200: 201: for (i = 0 ; i < pheccer->inter.iCompartments ; i++) 202: { 203: //- get intermediary number for the current compartment 204: 205: int iIntermediary = piBackward[i]; 206: 207: //- get compartment capacity 208: 209: double dCm = pheccer->inter.pcomp[iIntermediary].dCm; 210: 211: //- get compartment's parent number 212: 213: int iParent = pheccer->inter.pcomp[iIntermediary].iParent; 214: 215: //- get number of children of current compartment 216: 217: int iChildren = piChildren[iIntermediary]; 218: 219: //- if skipped previous compartment 220: 221: if (iSkipped) 222: { 223: //- can only occur once in a row, so next sets diagonal 224: 225: //t easy consistency check 226: 227: iSkipped = 0; 228: } 229: 230: //- previous compartment has set diagonal 231: 232: else 233: { 234: //- if current compartment has children 235: 236: if (iChildren) 237: { 238: //- skip first two compartments, other always set the diagonal 239: 240: if (i > 1) 241: { 242: SETCOP1(piCops, iCops, HECCER_COP_SET_DIAGONAL); 243: } 244: } 245: 246: //- if no children for the current compartment 247: 248: else 249: { 250: //- starting from the second compartment 251: 252: if (i > 1) 253: { 254: SETCOP1(piCops, iCops, HECCER_COP_NEXT_ROW); 255: 256: //- remember skipped to the diagonal for the next compartment 257: 258: iSkipped = 1; 259: } 260: } 261: } 262: 263: //- loop over all children 264: 265: int j; 266: 267: for (j = 0; j < iChildren; j++) 268: { 269: //- get index number for child of current compartment 270: 271: int iModelNumber = ppiChildren[iIntermediary][j]; 272: 273: //- convert to schedule number 274: 275: int iScheduleNumber = piForward[iModelNumber]; 276: 277: SETCOP2(piCops, iCops, HECCER_COP_FORWARD_ELIMINATION, 2 * iScheduleNumber); 278: 279: //- compute reverse contribution (genesis raxial) 280: 281: //t check old hsolve code, this computation seems 282: //t contradictory to me, mixes up child and parent 283: //t properties. ... 284: //t ... ok, done, perhaps this is a bug in hsolve ? 285: //t have to check the computations carefully again. 286: 287: double dReverse = -dt / (pheccer->inter.pcomp[iIntermediary].dCm * pheccer->inter.pcomp[iModelNumber].dRa); 288: 289: SETAXRES(pdAxres, iAxres, dReverse); 290: 291: //- compute verse contribution, comes from child (genesis axial) 292: 293: double dVerse = -dt / (pheccer->inter.pcomp[iModelNumber].dCm * pheccer->inter.pcomp[iModelNumber].dRa); 294: 295: SETAXRES(pdAxres, iAxres, dVerse); 296: } 297: } 298: 299: //- finishing forward elimination 300: 301: SETCOP1(piCops, iCops, HECCER_COP_FINISH); 302: 303: //- start backward substitution 304: 305: for (i = pheccer->inter.iCompartments - 2 ; i >= 0 ; i--) 306: { 307: //- get intermediary number for the current compartment 308: 309: int iIntermediary = piBackward[i]; 310: 311: //- get compartment capacity 312: 313: double dCm = pheccer->inter.pcomp[iIntermediary].dCm; 314: 315: //- get compartment's parent number 316: 317: int iParent = pheccer->inter.pcomp[iIntermediary].iParent; 318: 319: //- get number of children of current compartment 320: 321: int iChildren = piChildren[iIntermediary]; 322: 323: //- convert to schedule number 324: 325: int iScheduleNumber = piForward[iParent]; 326: 327: SETCOP2(piCops, iCops, HECCER_COP_BACKWARD_SUBSTITUTION, 2 * iScheduleNumber); 328: 329: SETCOP1(piCops, iCops, HECCER_COP_FINISH_ROW); 330: 331: //- set axial resistance value for child 332: 333: double dRa = pheccer->inter.pcomp[iIntermediary].dRa; 334: 335: SETAXRES(pdAxres, iAxres, -dt / (dCm * dRa)); 336: } 337: 338: //- finish backward substitution 339: 340: SETCOP1(piCops, iCops, HECCER_COP_FINISH); 341: 342: //- if we were counting during the previous loop 343: 344: if (!pheccer->vm.piCops) 345: { 346: //- prepare for compilation : allocate ->piCops and ->pdAxres, set counters 347: 348: pheccer->vm.piCops 349: = (int *)calloc(iCops, sizeof(int)); 350: 351: piCops = pheccer->vm.piCops; 352: 353: pheccer->vm.iCops = iCops; 354: 355: pheccer->vm.pdAxres 356: = (double *)calloc(iAxres, sizeof(double)); 357: 358: pdAxres = pheccer->vm.pdAxres; 359: 360: pheccer->vm.iAxres = iAxres; 361: } 362: } 363: 364: pheccer->vm.iResults = 2 * pheccer->inter.iCompartments; 365: 366: pheccer->vm.pdResults = (double *)calloc(pheccer->vm.iResults, sizeof(double)); 367: 368: pheccer->vm.iVms = pheccer->inter.iCompartments; 369: 370: pheccer->vm.pdVms = (double *)calloc(pheccer->vm.iVms, sizeof(double)); 371: 372: //! the diagonal terms are also needed for solving the current 373: //! equations, but I guess it is ok to deal with that in the 374: //! mechanism compiler. Check needed. 375: 376: //- return result 377: 378: return(iResult); 379: } 380: 381: 382: /// ************************************************************************** 383: /// 384: /// SHORT: HeccerCompartmentDump() 385: /// 386: /// ARGS.: 387: /// 388: /// pcomp......: a compartment. 389: /// pfile......: stdio file. 390: /// iSelection.: selection to dump. 391: /// 392: /// RTN..: int 393: /// 394: /// success of operation. 395: /// 396: /// DESCR: Dump compartment parameters to the given stream. 397: /// 398: /// ************************************************************************** 399: 400: int 401: HeccerCompartmentDump 402: (struct Compartment *pcomp, FILE *pfile, int iSelection) 403: { 404: //- set default result 405: 406: int iResult = TRUE; 407: 408: //- administrative overhead 409: 410: //! this makes testing quite hard, needs careful thought 411: 412: #ifdef lkjlkslkjasdf 413: 414: HECCER_SOURCE_NEUROSPACES 415: 416: fprintf(pfile, "Compartment (mc.iSerial, mc.iType) : (%i, %i)\n", pcomp->mc.iSerial, pcomp->mc.iType); 417: 418: #else 419: 420: fprintf(pfile, "Compartment (mc.iType) : (%i)\n", pcomp->mc.iType); 421: 422: #endif 423: 424: //- index of parent compartment, -1 for none 425: 426: if (iSelection & HECCER_DUMP_INTERMEDIARY_STRUCTURE) 427: { 428: fprintf(pfile, "Compartment (iParent) : (%i)\n", pcomp->iParent); 429: } 430: 431: /* //m number of mechanisms */ 432: 433: /* if (iSelection & HECCER_DUMP_INTERMEDIARY_STRUCTURE) */ 434: /* { */ 435: /* fprintf(pfile, "Compartment (iMechanisms) : (%i)\n", pcomp->iMechanisms); */ 436: /* } */ 437: 438: //m descriptive values, alphabetical order 439: 440: if (iSelection & HECCER_DUMP_INTERMEDIARY_COMPARTMENTS_PARAMETERS) 441: { 442: fprintf(pfile, "Compartment (dCm) : (%g)\n", pcomp->dCm); 443: 444: fprintf(pfile, "Compartment (dEm) : (%g)\n", pcomp->dEm); 445: 446: fprintf(pfile, "Compartment (dInitVm) : (%g)\n", pcomp->dInitVm); 447: 448: fprintf(pfile, "Compartment (dInject) : (%g)\n", pcomp->dInject); 449: 450: fprintf(pfile, "Compartment (dRa) : (%g)\n", pcomp->dRa); 451: 452: fprintf(pfile, "Compartment (dRm) : (%g)\n", pcomp->dRm); 453: } 454: 455: //- return result 456: 457: return(iResult); 458: } 459: 460: 461: /// ************************************************************************** 462: /// 463: /// SHORT: HeccerCompartmentInitiate() 464: /// 465: /// ARGS.: 466: /// 467: /// pheccer...: a heccer. 468: /// 469: /// RTN..: int 470: /// 471: /// success of operation. 472: /// 473: /// DESCR: Fill the compartment arrays with initial values. 474: /// 475: /// ************************************************************************** 476: 477: int HeccerCompartmentInitiate(struct Heccer *pheccer) 478: { 479: //- set default result 480: 481: int iResult = TRUE; 482: 483: //- loop over all compartments 484: 485: int i; 486: 487: for (i = 0 ; i < pheccer->inter.iCompartments ; i++) 488: { 489: //- get schedule number 490: 491: int iSchedule = pheccer->indexers.md.piForward[i]; 492: 493: //- fill in initial membrane potential 494: 495: //! if I use initial vm of zero, I get NaN for vm ? 496: 497: pheccer->vm.pdVms[iSchedule] = pheccer->inter.pcomp[i].dInitVm; 498: 499: //t fill in precomputed values for channels 500: 501: } 502: 503: //- return result 504: 505: return(iResult); 506: } 507: 508: 509: /// ************************************************************************** 510: /// 511: /// SHORT: HeccerCompartmentSolveCN() 512: /// 513: /// ARGS.: 514: /// 515: /// pheccer...: a heccer. 516: /// 517: /// RTN..: int 518: /// 519: /// success of operation. 520: /// 521: /// DESCR: Perform the compartment operators once. 522: /// 523: /// ************************************************************************** 524: 525: int HeccerCompartmentSolveCN(struct Heccer *pheccer) 526: { 527: int *piCops = &pheccer->vm.piCops[0]; 528: int iCop; 529: 530: double *pdAxres = &pheccer->vm.pdAxres[0]; 531: double *pdResults = &pheccer->vm.pdResults[0]; 532: double *pdResult = &pdResults[2]; 533: double *pdVms = &pheccer->vm.pdVms[pheccer->inter.iCompartments]; 534: double dDiagonal = pdResult[1]; 535: double dResult = pdResult[0]; 536: 537: //- start forward elimination at row 1 538: 539: int iDone = FALSE; 540: 541: while (!iDone) 542: { 543: iCop = piCops[0]; 544: piCops++; 545: 546: if (iCop == HECCER_COP_FORWARD_ELIMINATION) 547: { 548: //! pdResults[piCops[0] + 1] contains other diagonal term 549: 550: double d = pdAxres[0] / pdResults[piCops[0] + 1]; 551: dDiagonal -= pdAxres[1] * d; 552: pdAxres += 2; 553: dResult -= pdResults[piCops[0]] * d; 554: piCops++; 555: } 556: else if (iCop == HECCER_COP_SET_DIAGONAL) 557: { 558: //- store right side 559: 560: pdResult[0] = dResult; 561: 562: //- store left side 563: 564: pdResult[1] = dDiagonal; 565: 566: //- next row 567: 568: pdResult += 2; 569: dResult = pdResult[0]; 570: dDiagonal = pdResult[1]; 571: } 572: else if (iCop == HECCER_COP_NEXT_ROW) 573: { 574: //- store right side 575: 576: pdResult[0] = dResult; 577: 578: //- store left side 579: 580: pdResult[1] = dDiagonal; 581: 582: //- skip zero coefficients 583: 584: pdResult += 2; 585: 586: //- next row 587: 588: pdResult += 2; 589: dResult = pdResult[0]; 590: dDiagonal = pdResult[1]; 591: } 592: else 593: { 594: iDone = TRUE; 595: } 596: } 597: 598: //- last row done separately 599: 600: //t should try to incorporate this in the next loop 601: //t I have the feeling the loop is coded in the wrong way. 602: 603: /* SOLVE_ROW(dResult, dResult / dDiagonal); */ 604: 605: dResult = dResult / dDiagonal; 606: 607: //- explicit forwards step for last row 608: 609: /* COMPUTE_VM(pdVms, dResult); */ 610: 611: pdVms--; 612: pdVms[0] = dResult + dResult - pdVms[0]; 613: 614: /* REGISTER_ROW_RIGHT(pdResult[0], dResult); */ 615: 616: pdResult[0] = dResult; 617: pdResult -= 2; 618: 619: //- start new right side 620: 621: /* INITIATE_ROW(dResult, pdResult[0]); */ 622: 623: dResult = pdResult[0]; 624: 625: //- backwards elimination 626: 627: iDone = FALSE; 628: 629: while (!iDone) 630: { 631: //- load the operation 632: 633: iCop = piCops[0]; 634: piCops++; 635: 636: if (iCop == HECCER_COP_BACKWARD_SUBSTITUTION) 637: { 638: //- fill in the off diagonal contribution 639: 640: /* OFF_DIAGONAL_CONTRIBUTION(dResult, pdAxres[0], pdResults, piCops[0]); */ 641: 642: dResult -= pdAxres[0] * pdResults[piCops[0]]; 643: pdAxres++; 644: piCops++; 645: } 646: else if (iCop == HECCER_COP_FINISH_ROW) 647: { 648: //- solve the matrix row 649: 650: //! pdResult[1] contains the diagonal 651: 652: /* SOLVE_ROW(dResult, dResult / pdResult[1]); */ 653: 654: dResult = dResult / pdResult[1]; 655: 656: //- explicit forward step to get the membrane potential 657: 658: /* COMPUTE_VM(pdVms, dResult); */ 659: 660: pdVms--; 661: pdVms[0] = dResult + dResult - pdVms[0]; 662: 663: /* REGISTER_ROW_RIGHT(pdResult[0], dResult); */ 664: 665: pdResult[0] = dResult; 666: pdResult -= 2; 667: 668: //- start new right side 669: 670: /* INITIATE_ROW(dResult, pdResult[0]); */ 671: 672: dResult = pdResult[0]; 673: } 674: else 675: { 676: iDone = TRUE; 677: } 678: } 679: 680: //- return success 681: 682: return(TRUE); 683: } 684: 685: 686: