summaryrefslogtreecommitdiff
path: root/fibre_elongation_model.cc
blob: 33d1e2e72a76ee5678ed845baa75cd2854f9b6da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
/*
  © Copyright 2008 Randal A. Koene <randalk@netmorph.org>
  
  With design assistance from J. van Pelt & A. van Ooyen, and support
  from the Netherlands Organization for Scientific Research (NWO)
  Program Computational Life Sciences grant CLS2003 (635.100.005) and
  from the EC Marie Curie Research and Training Network (RTN)
  NEURoVERS-it 019247.

  This file is part of NETMORPH.

  NETMORPH is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  NETMORPH is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with NETMORPH.  If not, see <http://www.gnu.org/licenses/>.
*/
// fibre_elongation_model.cc
// Randal A. Koene, 20051221

#include "fibre_elongation_model.hh"
#include "fibre_structure.hh"
#include "dendritic_growth_model.hh"
#include "axon_direction_model.hh"
#include "neuron.hh"
#include "Txt_Object.hh"
#include "diagnostic.hh"

#define DAYSECONDS 86400.0
#define DAYSECONDSSQR (86400.0*86400.0)
#define DAYSECONDSCUB (86400.0*86400.0*86400.0)

#ifdef TESTING_ELONGATION_TOTAL
double usedarborelongationfraction = 0.0;
#endif
// Elongation model selection indicators at the general level
arbor_elongation_model general_arbor_elongation_model = van_Pelt_aem;
String general_arbor_elongation_model_root;

terminal_segment_elongation_model general_terminal_segment_elongation_model = simple_tsem;
String general_terminal_segment_elongation_model_root;

//arbor_elongation_model_base * general_arbor_elongation_model_schema = NULL;
//terminal_segment_elongation_model_base * general_terminal_segment_elongation_model_schema = NULL;

// Schema object pointer arrays
// [***NOTE] See TL#200603120618.2.

// See http://rak.minduploading.org:8080/caspan/Members/randalk/model-specification-implementation/.
elongation_rate_initialization_model default_elongation_rate_initialization_model = length_distribution_eri; // identifier, changed when a different universal model is set
String universal_elongation_rate_initialization_model_root; // Used only to report if chaining at the universal set level.

// Region and natural subset specific schemas (initialized in neuron.cc:general_neuron_parameters_interface::parse_CLP(Command_Line_Parameters & clp, network & net))
region_arbor_elongation_model_base_ptr * arbor_elongation_model_region_subset_schemas = NULL;

// Region and natural subset specific schemas (initialized in neuron.cc:general_neuron_parameters_interface::parse_CLP(Command_Line_Parameters & clp, network & net))
region_terminal_segment_elongation_model_base_ptr * terminal_segment_elongation_model_region_subset_schemas = NULL;

// Region and natural subset specific schemas (initialized in neuron.cc:general_neuron_parameters_interface::parse_CLP(Command_Line_Parameters & clp, network & net))
region_elongation_rate_initialization_model_base_ptr * elongation_rate_initialization_model_region_subset_schemas = NULL;

arbor_elongation_model_base::arbor_elongation_model_base(arbor_elongation_model_base * aemcontrib, double & aemweight, arbor_elongation_model_base & schema): contributing(NULL), contributingweight(1.0), prev_t(eq->T()), base_parameters(schema.base_parameters) {
  if (aemcontrib) { // Clone chained models
    contributingweight = aemweight;
    contributing = aemcontrib->clone();
  }
  pdf = schema.pdf->clone();
}

arbor_elongation_model_base::arbor_elongation_model_base(String & thislabel, String & label, Command_Line_Parameters & clp): contributing(NULL), contributingweight(1.0), prev_t(eq->T()), pdf(NULL), base_parameters(0.0) {
  // This constructor is intended for use by schemas, which can set up model
  // chains, as well as look for general (thislabel=="") or subset parameter
  // settings that differ from the compile-time defaults.
  // (See TL#200603120618.5.)
  // [***NOTE] This is not a CLP_Modifiable, because I do not wish to have to
  // store the label in order to be able to parse_CLP at any time.
  pdf = pdfselection(thislabel+"aem.",clp,X_elongate);
  if (!pdf) pdf = new normal_pdf(X_elongate,0.0,0.3,1.0); // a normal PDF is the default for AEMs
  else if (pdf->amplitude()>1.0) warning("Warning: Arbor Elongation Model has perturbation with amplitude greater than 1.0, retraction may occur!\n");
  if (label.empty()) return;
  int n;
  if ((n=clp.Specifies_Parameter(label+"aem_weight"))>=0) contributingweight = atof(clp.ParValue(n));
  arbor_elongation_model_selection(label,clp,contributing,contributing);
}

void arbor_elongation_model_base::reset(double t) {
  prev_t = t;
  base_parameters.dL = 0.0;
  if (contributing) contributing->reset(t);
}

int arbor_elongation_model_base::total_terminal_segments(fibre_structure * arbor, growth_cone_competition_type scope) {
  // This function counts all the terminal segments in all the arbors that are linked in the list that contains "arbor".
  int res = 0;
  switch (scope) {
  case gcc_within_tree:
    res = arbor->count_terminal_segments();
    break;;
  case gcc_within_all_AorD:
    PLL_LOOP_FORWARD(fibre_structure,arbor->head(),1) res += e->count_terminal_segments();
    break;;
  default: // gcc_whole_neuron
    res = arbor->N()->total_input_terminal_segments() + arbor->N()->total_output_terminal_segments();
  };
  return res;
}

double arbor_elongation_model_base::predict(double weight, fibre_structure * arbor) {
  // This chain is called by elongation(), and therefore only once for
  // each t>prev_t.
  // [***INCOMPLETE] The manner of combination of chained arbor elongation
  // models is not yet based on a specific rationale (see TL#200603131455.1).
  double dL = weight*predict_elongation(arbor); // resources for arbor elongation
  if (contributing) dL += contributing->predict(contributingweight,arbor);
  return dL;
}

double arbor_elongation_model_base::elongation(fibre_structure * arbor) {
  // (TL#200603021224.1 documents the start of considerations about this
  // implementation.)
  if (eq->T()<=prev_t) return base_parameters.dL;
  // 1. predict elongation with this arbor elongation model
  base_parameters.dL = predict_elongation(arbor);
  // 2. combine with predictions by chained models
  if (contributing) {
    base_parameters.dL += contributing->predict(contributingweight,arbor);
  }
  // 3. probabilistic deviation
  // [***NOTE] Below is the case when NOT LEGACY_ELONGATION, perturbation
  // is normally added here!
#ifndef LEGACY_ELONGATION
#ifdef TESTING_AEM_RANDOM_DISTRIBUTION
  double r = pdf->random_selection();
  aemrandomdist[((unsigned int) (r*10.0))+10]++;
  base_parameters.dL *= (1.0+r);
#else
  base_parameters.dL *= (1.0+pdf->random_selection());
#endif
#endif
  // return the new cached value
  return base_parameters.dL; // resources for arbor elongation
}

String arbor_elongation_model_base::report_parameters() {
  // This calls report_parameters_specific() in any derived class and
  // propagates parameter reporting tocontributing models.
  String res(report_parameters_specific());
  res += " p_AEM=" + pdf->report_parameters();
  if (contributing) {
    res += String(contributingweight,"\n  [contrib.w=%.2f] ");
    res += contributing->report_parameters();
  }
  return res;
}

//van_Pelt_arbor_elongation_model::van_Pelt_arbor_elongation_model(): _nu0(15.5/DAYSECONDS), _F(0.74) {
//}

van_Pelt_arbor_elongation_model::van_Pelt_arbor_elongation_model(arbor_elongation_model_base * aemcontrib, double & aemweight, van_Pelt_arbor_elongation_model & schema): arbor_elongation_model_base(aemcontrib,aemweight,schema), parameters(schema.parameters) {
  // This constructor is specifically meant to be used by clone() calls.
  // See TL#200603120707.2.
  //schema.copy_parameters(parameters);
  //_nu0 = schema.get_nu0();
  //_F = schema.get_F();
  // Chaining of models is taken care of in the base constructor.
}

van_Pelt_arbor_elongation_model::van_Pelt_arbor_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): arbor_elongation_model_base(thislabel,label,clp), parameters(15.5/DAYSECONDS,0.74,gcc_whole_neuron) {
  // [***NOTE] This is not a CLP_Modifiable, because I do not wish to have to
  // store the label in order to be able to parse_CLP at any time.
  // Detect schema specific parameter values
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"growth_nu0"))>=0) parameters._nu0 = atof(clp.ParValue(n));
  if ((n=clp.Specifies_Parameter(thislabel+"growth_F"))>=0) parameters._F = atof(clp.ParValue(n));
  if ((n=clp.Specifies_Parameter(thislabel+"F_competes_with"))>=0) {
    if (downcase(clp.ParValue(n))==String("whole_neuron")) parameters._competition_with_all_trees = gcc_whole_neuron;
    else if (downcase(clp.ParValue(n))==String("all_dendrites")) parameters._competition_with_all_trees = gcc_within_all_AorD;
    else if (downcase(clp.ParValue(n))==String("all_axons")) parameters._competition_with_all_trees = gcc_within_all_AorD;
    else if (downcase(clp.ParValue(n))==String("same_arbor")) parameters._competition_with_all_trees = gcc_within_tree;
    else warning("Unknown F competition value:"+clp.ParValue(n)+'\n');
  }
  // Chaining of models is taken care of in the base constructor.
}

arbor_elongation_model_base * van_Pelt_arbor_elongation_model::clone() {
  // See tension_direction_model::clone() for a more complex example of a
  // cloning function.
  return new van_Pelt_arbor_elongation_model(contributing,contributingweight,*this);
}

double van_Pelt_arbor_elongation_model::predict_elongation(fibre_structure * arbor) {
  // prev_t should be set in here, since prev_t is cached separately in
  // each chained arbor elongation model, and this function is called both
  // by elongation() and by predict().
  // This model function is called directly or via predict() only once for
  // each update time. (See update time test above.)
  //#define TEST_VAN_PELT_ARBOR_ELONGATION_MODEL
#ifdef TEST_VAN_PELT_ARBOR_ELONGATION_MODEL
  int nofF = total_terminal_segments(arbor,parameters._competition_with_all_trees);
  int nofarbor = arbor->count_terminal_segments();
  double nt_F = exp(-parameters._F*log((double) nofF));
  nt_F *= (eq->T()-prev_t)*parameters._nu0; // van Pelt segment elongation scaled by N^(-F)
  // [***NOTE] fixed step alternative: nt_F *= _dt_nu0;
  prev_t = eq->T();
  double res = nt_F*((double) nofarbor);
  //if ((eq->T()<200.0) || (eq->T()>1.8143e+06)) cout << "At t=" << eq->T() << ": n(t)*dt*nu0*N^(-F) = (" << nofarbor << ")*(" << (eq->T()-prev_t) << ")*(" << parameters._nu0 << ")*(" << nofF << ")^(-" << parameters._F << ") = " << res << '\n';
  cout << " N=" << nofF << " n=" << nofarbor << " E[dL]=" << res;
  return res;
#else
  double nt_F = exp(-parameters._F*log((double) total_terminal_segments(arbor,parameters._competition_with_all_trees)));
  nt_F *= (eq->T()-prev_t)*parameters._nu0; // van Pelt segment elongation scaled by N^(-F)
  // [***NOTE] fixed step alternative: nt_F *= _dt_nu0;
  prev_t = eq->T();
  return nt_F*((double) arbor->count_terminal_segments());
#endif
}

String van_Pelt_arbor_elongation_model::report_parameters_specific() {
  String res(" van Pelt, nu0=");
  res += String(parameters._nu0*DAYSECONDS,"%.2f/day F=");
  res += String(parameters._F,"%.2f");
  res += " F competes ";
  switch (parameters._competition_with_all_trees) {
  case gcc_whole_neuron: res += "with whole neuron"; break;;
  case gcc_within_all_AorD: res += "with all axons or with all dendrites"; break;;
  default: res += "within the axon/dendrite arbor"; // gcc_within_tree
  };
  return res;
}

Polynomial_O1_arbor_elongation_model::Polynomial_O1_arbor_elongation_model(arbor_elongation_model_base * aemcontrib, double & aemweight, Polynomial_O1_arbor_elongation_model & schema): arbor_elongation_model_base(aemcontrib,aemweight,schema), parameters(schema.parameters) {
  // Chaining of models is taken care of in the base constructor.
}

Polynomial_O1_arbor_elongation_model::Polynomial_O1_arbor_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): arbor_elongation_model_base(thislabel,label,clp), parameters(5.631771/DAYSECONDS,1.0) {
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"growth_nu0"))>=0) parameters._nu0 = atof(clp.ParValue(n));
  if ((n=clp.Specifies_Parameter(thislabel+"growth_F"))>=0) parameters._F = atof(clp.ParValue(n));
  // Chaining of models is taken care of in the base constructor.
}

arbor_elongation_model_base * Polynomial_O1_arbor_elongation_model::clone() {
  return new Polynomial_O1_arbor_elongation_model(contributing,contributingweight,*this);
}

double Polynomial_O1_arbor_elongation_model::predict_elongation(fibre_structure * arbor) {
  // This model function is called directly or via predict() only once for
  // each update time. (See update time test above.)
  double dL = (eq->T()-prev_t)*parameters._nu0*exp((1.0 - parameters._F)*log((double) arbor->count_terminal_segments())); // resources for arbor elongation
  // [***NOTE] fixed step alternative: _dt_nu0*exp((1.0 - _F)*log((double) arbor->count_terminal_segments()));
  prev_t = eq->T();
  return dL;
}

String Polynomial_O1_arbor_elongation_model::report_parameters_specific() {
  String res(" Polynomial_01, nu0=");
  res += String(parameters._nu0*DAYSECONDS,"%.2f/day F=");
  res += String(parameters._F,"%.2f");
  return res;
}

Polynomial_O3_arbor_elongation_model::Polynomial_O3_arbor_elongation_model(arbor_elongation_model_base * aemcontrib, double & aemweight, Polynomial_O3_arbor_elongation_model & schema): arbor_elongation_model_base(aemcontrib,aemweight,schema), parameters(schema.parameters) {
  // Chaining of models is taken care of in the base constructor.
}

Polynomial_O3_arbor_elongation_model::Polynomial_O3_arbor_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): arbor_elongation_model_base(thislabel,label,clp), parameters(0.0/DAYSECONDSSQR,0.006564/DAYSECONDSCUB,3.839628/DAYSECONDS,1.0) {
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"growth_nu0"))>=0) parameters._nu0 = atof(clp.ParValue(n));
  if ((n=clp.Specifies_Parameter(thislabel+"growth_nu1"))>=0) parameters._nu1 = atof(clp.ParValue(n));
  if ((n=clp.Specifies_Parameter(thislabel+"growth_nu2"))>=0) parameters._nu2 = atof(clp.ParValue(n));
  if ((n=clp.Specifies_Parameter(thislabel+"growth_F"))>=0) parameters._F = atof(clp.ParValue(n));
  // Chaining of models is taken care of in the base constructor.
}

arbor_elongation_model_base * Polynomial_O3_arbor_elongation_model::clone() {
  return new Polynomial_O3_arbor_elongation_model(contributing,contributingweight,*this);
}

double Polynomial_O3_arbor_elongation_model::predict_elongation(fibre_structure * arbor) {
  // L(t2) - L(t1), where each is approximated by a cubic polynomial function
  // This model function is called directly or via predict() only once for
  // each update time. (See update time test above.)
  double t2 = eq->T();
  double t1sq = prev_t*prev_t, t2sq = t2*t2;
  double dL = (parameters._nu0*(t2-prev_t)) + (parameters._nu1*(t2sq-t1sq)) + (parameters._nu2*((t2sq*t2)-(t1sq*prev_t)));
  prev_t = t2;
  return dL*exp((1.0 - parameters._F)*log((double) arbor->count_terminal_segments()));
}

String Polynomial_O3_arbor_elongation_model::report_parameters_specific() {
  String res(" Polynomial_03, nu0=");
  res += String(parameters._nu0*DAYSECONDSCUB,"%.2f/day^3 nu1=");
  res += String(parameters._nu1*DAYSECONDSCUB,"%.2f/day^3 nu2=");
  res += String(parameters._nu2*DAYSECONDSCUB,"%.2f/day^3 F=");
  res += String(parameters._F,"%.2f");
  return res;
}

terminal_segment_elongation_model_base::terminal_segment_elongation_model_base(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, terminal_segment_elongation_model_base & schema): contributing(NULL), contributingweight(1.0), prev_t(eq->T()), l_i_cache_t(eq->T()), base_parameters(schema.base_parameters) {
  // [***NOTE:] In the current implementation, any TSEM that requires ts during initialization (e.g. nonnorm_BESTL) must be at the head of a chain.
  if (tsemcontrib) { // Clone chained models
    contributingweight = tsemweight;
    contributing = tsemcontrib->clone(NULL);
  }
  pdf = schema.pdf->clone();
  // [***INCOMPLETE] The REDUCED_MEMORY_PTSEM option is in need of a place for initialization, where (a) the arbor can be reached in order to set tsem_pdf, where (b) the applicable prefix is known (e.g. "all_axons."), and (c) needs to take care of deleting the allocated PDF object as well. Also, I need to add more compiler directives around references to pdf when compiling with REDUCED_MEMORY_PTSEM.
}

terminal_segment_elongation_model_base::terminal_segment_elongation_model_base(String & thislabel, String & label, Command_Line_Parameters & clp): contributing(NULL), contributingweight(1.0), prev_t(eq->T()), l_i_cache_t(eq->T()), base_parameters(0.0) {
  // This constructor is intended for use by schemas, which can set up model
  // chains, as well as look for general (thislabel=="") or subset parameter
  // settings that differ from the compile-time defaults.
  // (See TL#200603120618.5.)
  // [***NOTE] This is not a CLP_Modifiable, because I do not wish to have to
  // store the label in order to be able to parse_CLP at any time.
  pdf = pdfselection(thislabel+"tsem.",clp,X_elongate);
  if (!pdf) pdf = new normal_pdf(X_elongate,0.0,0.3,1.0); // a normal PDF is the default for TSEMs
  else {
    if (pdf->amplitude()>1.0) warning("Warning: Terminal Segment Elongation Model has perturbation with amplitude greater than 1.0, retraction may occur!\n");
    else if ((pdf->amplitude()==0.0) && (pdf->mean_X()>0.0)) warning("Warning: TSEM perturbation is added to 1.0 before multiplication with the computed elongation speed. When using a delta PDF this means that values greater than zero can cause ramping up to 'infinite' elongation speed and resulting invalid computations.\n");
  }
  if (label.empty()) return;
  int n;
  if ((n=clp.Specifies_Parameter(label+"tsem_weight"))>=0) contributingweight = atof(clp.ParValue(n));
  terminal_segment_elongation_model_selection(label,clp,contributing,contributing);
}

void terminal_segment_elongation_model_base::reset(double t) {
  // [***NOTE] This only appears to be used in a "glimpse ahead" in network.cc.
  prev_t = t;
  l_i_cache_t = t;
  base_parameters.l_i_cache = 0.0;
  if (contributing) contributing->reset(t);
}

void terminal_segment_elongation_model_base::handle_branching(terminal_segment & ts1, terminal_segment & ts2) {
  ts2.set_elongation_model(clone(&ts2));
  ts1.set_elongation_model(this);
}

void terminal_segment_elongation_model_base::handle_turning(terminal_segment & ts) {
  ts.set_elongation_model(this);
}

double terminal_segment_elongation_model_base::predict(double weight) {
  // This chain is called by perturbed_expected_elongation(), and therefore
  // only once for each t>l_i_cache_t.
  // This function is called by:
  //   perturbed_expected_elongation()
  // This function calls:
  //   predict_elongate()
  //   predict()
  // [***INCOMPLETE] The manner of combination of chained arbor elongation
  // models is not yet based on a specific rationale (see TL#200603140534.1).
  double l_i = weight*predict_elongate();
  if (contributing) l_i += contributing->predict(contributingweight);
  prev_t=eq->T(); // store for used by calculations in chained model
  return l_i;
}

#ifdef REDUCED_MEMORY_PTSEM
probability_distribution_function * tsem_pdf_cache = NULL;
#endif

double terminal_segment_elongation_model_base::perturbed_expected_elongation() {
  // This function is called by:
  //   elongate()
  // This function calls:
  //   predict_elongate()
  //   predict()
  // See the comment about the base_parameters.l_i_cache in the header file.
  // [***INCOMPLETE] The manner of combination of chained arbor elongation
  // models is not yet based on a specific rationale (see TL#200603140534.1).
  if (eq->T()<=l_i_cache_t) return base_parameters.l_i_cache;
  // 1. predict expected elongation
  // Using different temporary variable in case contributing models need
  // the original cached value.
  double l_i = predict_elongate();
  // 2. combine with predictions by chained models
  if (contributing) {
    l_i += contributing->predict(contributingweight);
  }
  // 3. probabilistic deviation (i.e. perturbed expected elongation)
#ifdef LEGACY_ELONGATION
  base_parameters.l_i_cache = l_i;
#else
#ifdef REDUCED_MEMORY_PTSEM
  base_parameters.l_i_cache = l_i*(1.0+tsem_pdf_cache->random_selection());
#else
  base_parameters.l_i_cache = l_i*(1.0+pdf->random_selection());
#endif
#endif
  // [***INCOMPLETE] remember to provide a uniform random pdf option for
  // legacy testing purposes (e.g. 1.0*random_double(0.2,1.8))
  l_i_cache_t = eq->T();
  return base_parameters.l_i_cache;
}

void terminal_segment_elongation_model_base::elongate(terminal_segment * ts) {
  // PROTOCOL: This is called during growth.
  // This function calls:
  //   perturbed_expected_elongation()
  //   sum_of_perturbed_expected_elongations()
  //   ElongationModel()->elongation()
  //   ts->add_length()
  //   ts->update_segment_vector()
  // (TL#200603021224.1 documents the start of considerations about this
  // implementation.)
  if (eq->T()<=prev_t) return;
  // 1., 2. & 3 compute perturbed expected elongation
  // The three regular steps of the model call have been turned into
  // a separate function here, so that the calculations can be invoked
  // for all terminal segments as needed, while the sum of those results
  // is needed for the elongation by proportional allocation of resources.
  // [***NOTE] If the elongation calculations for specific terminal segments
  // need to access other information about the terminal segment, or about
  // other objects in the ts->Arbor(), then we can pass the ts parameter to
  // other member functions, such as perturbed_expected_elongation().
#ifdef REDUCED_MEMORY_PTSEM
  tsem_pdf_cache = ts->Arbor()->TSEM_Pdf();
#endif
  double l_i = perturbed_expected_elongation();
  // 4. normalize with regard to the sum of all perturbed expected elongations
  //    of all terminal segments on the same arbor
  register fibre_structure * arbor = ts->Arbor();
  double sum_l_i = arbor->sum_of_perturbed_expected_elongations();
  if (sum_l_i!=0.0) l_i /= sum_l_i; // otherwise all are "delayed"
#ifdef LEGACY_ELONGATION
  l_i *= random_double(0.2,1.8);
#endif
  // 5. elongate by proportion of available resources
  double L_i = l_i * arbor->ElongationModel()->elongation(arbor);
#ifdef TESTING_ELONGATION_TOTAL
  usedarborelongationfraction += l_i;
#endif
#ifdef DEBUGGING_ELONGATION
  if (L_i<0.0) debugging.tsemb_elongate_decreasing++;
  else if (L_i==0.0) debugging.tsemb_elongate_stopped++;
#endif
  ts->add_length(L_i);
  if (figattr_update_terminal_segments_visibly) ts->update_segment_vector();
  prev_t = eq->T();
  return;
}

String terminal_segment_elongation_model_base::report_parameters() {
  // This calls report_parameters_specific() in any derived class and
  // propagates parameter reporting tocontributing models.
  String res(report_parameters_specific());
  res += " p_TSEM=" + pdf->report_parameters();
  if (contributing) {
    res += String(contributingweight,"\n  [contrib.w=%.2f] ");
    res += contributing->report_parameters();
  }
  return res;
}

// As described in TL#200603071348.1 and in the methods and future
// directions in the framework-generation paper, the elongation function
// (1) checks how much time has passed since the last elongation update
// for this terminal segment, (2) requests resources available to the
// arbor, and (3) determines this terminal segment's updated proportion
// of resource consumption. Then the terminal segment length can be updated.

simple_terminal_segment_elongation_model::simple_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, simple_terminal_segment_elongation_model & schema): terminal_segment_elongation_model_base(tsemcontrib,tsemweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

simple_terminal_segment_elongation_model::simple_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): terminal_segment_elongation_model_base(thislabel,label,clp) {
  // This simple model has no local parameters to specify.
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * simple_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new simple_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double simple_terminal_segment_elongation_model::predict_elongate() {
  // This model function is called directly by perturbed_expected_elongation()
  // or via predict() (also due to perturbed_expected_elongation()) only once
  // for each update time. (See cache time and update time tests above.)
  return 1.0; // expected_L_i
}

String simple_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" simple");
  return res;
}

// *** inertia may want to use a cached result of the previous NORMALIZED
//     perturbed expected elongation, and then store its calculated result
//     in the l_i_cache for access by other segment updates with comparable
//     scale. I have to read the paper very carefully, to see exactly what
//     values are to be used in which steps.

inertia_terminal_segment_elongation_model::inertia_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, inertia_terminal_segment_elongation_model & schema): terminal_segment_elongation_model_base(tsemcontrib,tsemweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

inertia_terminal_segment_elongation_model::inertia_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): terminal_segment_elongation_model_base(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * inertia_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new inertia_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double inertia_terminal_segment_elongation_model::predict_elongate() {
  // This model function is called directly by perturbed_expected_elongation()
  // or via predict() (also due to perturbed_expected_elongation()) only once
  // for each update time. (See cache time and update time tests above.)
  // [***NOTE] The paper describes this as being normalized, but since the
  // normalization is applied equally to all terminal segments, it is not
  // necessary, unless we wish to express the predicted elongation of a
  // new terminal segment in relation to a proportions between 0.0 and 1.0.
  // Note: At the head of a chain, the combined result provides inertia,
  // elsewhere in a chain, a local inertial component is contributed.
  if (base_parameters.l_i_cache==0.0) base_parameters.l_i_cache = 1.0+pdf->random_selection(); // updated, see TL#200709150740.1
  return base_parameters.l_i_cache;
}

String inertia_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" inertia");
  return res;
}

second_order_terminal_segment_elongation_model::second_order_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, second_order_terminal_segment_elongation_model & schema): terminal_segment_elongation_model_base(tsemcontrib,tsemweight,schema), parameters(schema.parameters) {
  // Chaining of models is taken care of in the base constructor.
}

second_order_terminal_segment_elongation_model::second_order_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): terminal_segment_elongation_model_base(thislabel,label,clp), parameters(0.5,86400.0) {
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"tsem_inertia"))>=0) parameters.inertia = atof(clp.ParValue(n));
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * second_order_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new second_order_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double second_order_terminal_segment_elongation_model::predict_elongate() {
  // This model function is called directly by perturbed_expected_elongation()
  // or via predict() (also due to perturbed_expected_elongation()) only once
  // for each update time. (See cache time and update time tests above.)
  // [***NOTE] The paper describes this as being normalized, but since the
  // normalization is applied equally to all terminal segments, it is not
  // necessary, unless we wish to express the predicted elongation of a
  // new terminal segment in relation to a proportions between 0.0 and 1.0.
  // The acceleration in the first terminal segment of an arbor is initialized
  // to 0.5, so that all terminal segments initially tend to increase their
  // relative speed of elongation. The acceleration is constrained between
  // hard-coded limits of -1.0 and 1.0, between which perturbations are
  // scaled by inertia. The resulting elongation is always >= 0.0.
  // Note: At the head of the chain, the second order model is applied to the
  // perturbed result of chained models. Elsewhere in the chain, a local
  // variable is maintained in base_parameters.l_i_cache, which is updated
  // without further perturbation and contributes to the chain response.
  double dt = eq->T()-prev_t;
  parameters.l_i_acceleration += (dt/parameters.inertia)*pdf->random_selection(); // Note: "l_i_acceleration" is a bit of a misnomer, it is called the velocity in the paper.
  if (parameters.l_i_acceleration>1.0) parameters.l_i_acceleration=1.0;
  else if (parameters.l_i_acceleration<-1.0) parameters.l_i_acceleration=-1.0;
  // modeled analogous to v = u + at
#define expected_L_i base_parameters.l_i_cache
  expected_L_i = base_parameters.l_i_cache + parameters.l_i_acceleration*dt; // updated, see TL#200709150740.1, was separate "double expected_L_i" local variable
  if (expected_L_i<0.0) return 0.0;
  return expected_L_i;
#undef expected_L_i
}

String second_order_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" second order, inertia=");
  res += String(parameters.inertia,"%.2f");
  return res;
}

constrained_second_order_terminal_segment_elongation_model::constrained_second_order_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, constrained_second_order_terminal_segment_elongation_model & schema): second_order_terminal_segment_elongation_model(tsemcontrib,tsemweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

constrained_second_order_terminal_segment_elongation_model::constrained_second_order_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): second_order_terminal_segment_elongation_model(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * constrained_second_order_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new constrained_second_order_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double constrained_second_order_terminal_segment_elongation_model::predict_elongate() {
  double expected_L_i = second_order_terminal_segment_elongation_model::predict_elongate();
  // With an initial acceleration of 0.5 and assuming mean perturbation around
  // zero, some terminal segments may reach a relative speed of DAYSECONDS in
  // seven days.
#define TSEM_SPEED_CONSTRAINT 3.5*DAYSECONDS
  if (expected_L_i>TSEM_SPEED_CONSTRAINT) return TSEM_SPEED_CONSTRAINT;
  return expected_L_i;
}

initialized_CSO_terminal_segment_elongation_model::initialized_CSO_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, initialized_CSO_terminal_segment_elongation_model & schema): constrained_second_order_terminal_segment_elongation_model(tsemcontrib,tsemweight,schema), icsoparameters(schema.icsoparameters) {
  // Beware: That which I am calling "l_i_acceleration" in the source code
  // is $\dot{\TSEMquote}(t)$ in ~/doc/tex/generation-framework/generation-framework.tex.
  // This stems from the former emphasis of the update of fiber length over
  // time as an elongation rate, and there for the rate of change in that
  // was an acceleration.
  // Chaining of models is taken care of in the base constructor.
  if (icsoparameters.initial_l_i_acceleration>=0.0) parameters.l_i_acceleration = icsoparameters.initial_l_i_acceleration;
  // Otherwise the cloned value from parent or schema parameters is used.
}

#define ICSO_CLONE_PARENT_OR_SCHEMA -1.0
initialized_CSO_terminal_segment_elongation_model::initialized_CSO_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): constrained_second_order_terminal_segment_elongation_model(thislabel,label,clp), icsoparameters(ICSO_CLONE_PARENT_OR_SCHEMA) {
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"tsem_initial_acceleration"))>=0) icsoparameters.initial_l_i_acceleration = atof(clp.ParValue(n));
  // Chaining of models is taken care of in the base constructor.
  if (icsoparameters.initial_l_i_acceleration>=0.0) parameters.l_i_acceleration = icsoparameters.initial_l_i_acceleration;
  // Through this constructor, no parent or schema information is received
  // (this object likely IS a schema), so that the first initial value of the
  // acceleration is set by the base class (probable default = 0.5).
}

terminal_segment_elongation_model_base * initialized_CSO_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new initialized_CSO_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

String initialized_CSO_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" initialized "+constrained_second_order_terminal_segment_elongation_model::report_parameters());
  res += String(icsoparameters.initial_l_i_acceleration," tsem_initial_acceleration=%.2f (0.0 simulates delayed branching)");
  return res;
}

decaying_second_order_terminal_segment_elongation_model::decaying_second_order_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, decaying_second_order_terminal_segment_elongation_model & schema): second_order_terminal_segment_elongation_model(tsemcontrib,tsemweight,schema), dsoparameters(schema.dsoparameters) {
  // Chaining of models is taken care of in the base constructor.
}

decaying_second_order_terminal_segment_elongation_model::decaying_second_order_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): second_order_terminal_segment_elongation_model(thislabel,label,clp), dsoparameters(DAYSECONDS/4.0) {
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"tsem_decay"))>=0) dsoparameters.t_decay = atof(clp.ParValue(n));
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * decaying_second_order_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new decaying_second_order_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double decaying_second_order_terminal_segment_elongation_model::predict_elongate() {
  // As in the second order model, except that acceleration decays toward zero
  // unless perturbed.
  // Note: At the head of the chain, the decaying second order model is applied to
  // the perturbed result of chained models. Elsewhere in the chain, a local
  // variable is maintained in base_parameters.l_i_cache, which is updated
  // without further perturbation and contributes to the chain response.
  double dt = eq->T()-prev_t;
  parameters.l_i_acceleration *= exp(-dt/dsoparameters.t_decay);
  parameters.l_i_acceleration += (dt/parameters.inertia)*pdf->random_selection();
  if (parameters.l_i_acceleration>1.0) parameters.l_i_acceleration=1.0;
  else if (parameters.l_i_acceleration<-1.0) parameters.l_i_acceleration=-1.0;
  // modeled analogous to v = u + at
#define expected_L_i base_parameters.l_i_cache
  expected_L_i = base_parameters.l_i_cache + parameters.l_i_acceleration*dt; // updated, see TL#200709150740.1, was separate "double expected_L_i" local variable
  if (expected_L_i<0.0) return 0.0;
  return expected_L_i;
#undef expected_L_i
}

String decaying_second_order_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" decaying "+second_order_terminal_segment_elongation_model::report_parameters());
  res += String(dsoparameters.t_decay," t_decay=%.2f");
  return res;
}

delayed_branch_terminal_segment_elongation_model::delayed_branch_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, delayed_branch_terminal_segment_elongation_model & schema): terminal_segment_elongation_model_base(tsemcontrib,tsemweight,schema), parameters(schema.parameters) {
  // Chaining of models is taken care of in the base constructor.
  // [***INCOMPLETE] This requires a random_selection function with a
  // decaying probability, perhaps just a random_positive of a normal
  // function, using the delay time constant.
  elongation_commencement = eq->T() + 0.0;
}

delayed_branch_terminal_segment_elongation_model::delayed_branch_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): terminal_segment_elongation_model_base(thislabel,label,clp), parameters(3600.0) {
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"tsem_tdelay"))>=0) parameters.delay_time_constant = atof(clp.ParValue(n));
  // Chaining of models is taken care of in the base constructor.
  // [***INCOMPLETE] This requires a random_selection function with a
  // decaying probability, perhaps just a random_positive of a normal
  // function, using the delay time constant.
  // [***INCOMPLETE] We might want to avoid a delay for the first segment,
  // but perhaps not, since that could allow different fiber structures to
  // begin growing at slightly different times.
  // The paper specifies that this should be a model option. Implement this
  // DBSTEM_at_first_segment parameter.
  elongation_commencement = eq->T() + 0.0;
}

terminal_segment_elongation_model_base * delayed_branch_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new delayed_branch_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double delayed_branch_terminal_segment_elongation_model::predict_elongate() {
  // An exponential function with delay_time_constant is used to determine
  // the likelihood that a delayed branch begins to elongate. When that
  // happens, the terminal segment continues to elongate according to a
  // different elongation function. Until then, the proportion of elongation
  // resources requested is 0.0.
  if (eq->T()<elongation_commencement) return 0.0;
  // *** old method: double min_dt = parameters.creation_time - eq->T();
  // *** old method: double threshold = exp(min_dt/parameters.delay_time_constant);
  // *** old method: if (random_double()<=threshold) return 0.0; // no elongation yet
  // [***INCOMPLETE] The case where elongation begins.
  // This specific model should always return 0.0 (see paper), but it should
  // activating the chain of models at this point if it is not already
  // active.
  // There are two ways to control the application of chained models and
  // consequently two ways to remember the chain during the delay period:
  // 1) Detach the chain and attach it to a pointer cache variable.
  // 2) Define a replacement for the perturbed_expected_elongation()
  //    function that can take the delay into account by testing
  //    if ((eq->T()>=elongation_commencement) && contributing) rather than
  //    just if (contributing).
  return 0.0;
}

String delayed_branch_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" delayed, tdelay=");
  res += String(parameters.delay_time_constant,"%.2f");
  return res;
}

BESTL_terminal_segment_elongation_model::BESTL_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, BESTL_terminal_segment_elongation_model & schema): terminal_segment_elongation_model_base(tsemcontrib,tsemweight,schema), branchpdf(NULL) {
  // Chaining of models is taken care of in the base constructor.
  branchpdf = schema.branchpdf->clone();
}

BESTL_terminal_segment_elongation_model::BESTL_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): terminal_segment_elongation_model_base(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
  base_parameters.l_i_cache = 1.0; // this is the assumed mean of the BESTL quota distribution
  // Note: The mean can shift if other models contribute to the elongation quota determination.
  branchpdf = pdfselection(thislabel+"tsem.branch.",clp,X_elongate);
  if (!branchpdf) branchpdf = new normal_pdf(X_elongate,2.0,1.0); // a normal PDF is the default for TSEM branch length initialization
}

terminal_segment_elongation_model_base * BESTL_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  return new BESTL_terminal_segment_elongation_model(contributing,contributingweight,*this);
}

double BESTL_terminal_segment_elongation_model::predict_elongate() {
  // This model expects no change of elongation rate until the next bifurcation.
  // The base_parameters.l_i_cache value is initialized in the constructor and
  // by an Elongation_Rate_Initialization model.
  // Note: If the BESTL_TSEM is at the head of a chain then the use of the
  // base_parameters.l_i_cache means that it attempts to maintain the rate of
  // elongation computed by the chain (unless further modified by chained
  // contributions). Elsewhere in a chain, the BESTL_TSEM contributes a stable
  // expected elongation quota.
  // For an unperturbed BESTL_TSEM, use the perturbation PDF delta_PDF (with
  // the default mean value 0.0).
  return base_parameters.l_i_cache; // see TL#200709150740.1
}

void BESTL_terminal_segment_elongation_model::handle_branching(terminal_segment & ts1, terminal_segment & ts2) {
  ts2.set_elongation_model(clone(&ts2));
  ts1.set_elongation_model(this);
  if (branchpdf) {
    ts1.AngularCoords().add_X(branchpdf->random_positive());
    ts2.AngularCoords().add_X(branchpdf->random_positive());
  }
}

String BESTL_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" BESTL: branchPDF: ");
  if (branchpdf) res += branchpdf->report_parameters();
  return res;
}

BESTL_nonnormalizing_terminal_segment_elongation_model::BESTL_nonnormalizing_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, BESTL_nonnormalizing_terminal_segment_elongation_model & schema): BESTL_terminal_segment_elongation_model(tsemcontrib,tsemweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

BESTL_nonnormalizing_terminal_segment_elongation_model::BESTL_nonnormalizing_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): BESTL_terminal_segment_elongation_model(thislabel,label,clp) {
  // [*** Note:] See TL#200807020156.5 - WITHDRAWN! See TL#200807020156.10.
  //probability_distribution_function * rootratepdf = pdfselection(thislabel+"tsem.rootrate.",clp,X_elongate);
  //if (rootratepdf) base_parameters.l_i_cache = rootratepdf->random_positive();
  //else base_parameters.l_i_cache = 0.00013889;
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * BESTL_nonnormalizing_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  terminal_segment_elongation_model_base * tsemb = new BESTL_nonnormalizing_terminal_segment_elongation_model(contributing,contributingweight,*this);
  if (ts) {
    if ((!ts->Prev()) && (!ts->Next())) { // We still use the regular ERI->initialize() call from the terminal_segment::branch() function when branching
      if (!(ts->ElongationRateInitializationModel())) error("ElongationRateInitializationModel() is NULL in BESTL_nonnormalizing_terminal_segment_elongation_model::clone().\n");
      else ts->ElongationRateInitializationModel()->root_initialize(tsemb->base_parameters.l_i_cache); // Insures that initial segments also draw from the ERI distribution (see TL#200807020156.10)
    }
  }
  return tsemb;
}

void BESTL_nonnormalizing_terminal_segment_elongation_model::elongate(terminal_segment * ts) {
  // PROTOCOL: This is called during growth.
  // This function calls:
  //   perturbed_expected_elongation()
  //   ElongationModel()->elongation()
  //   ts->add_length()
  //   ts->update_segment_vector()
  if (eq->T()<=prev_t) return;
  // 1., 2. & 3 compute perturbed expected elongation
  // The three regular steps of the model call have been turned into
  // a separate function here, so that the calculations can be invoked
  // for all terminal segments as needed, while the sum of those results
  // is needed for the elongation by proportional allocation of resources.
  // [***NOTE] If the elongation calculations for specific terminal segments
  // need to access other information about the terminal segment, or about
  // other objects in the ts->Arbor(), then we can pass the ts parameter to
  // other member functions, such as perturbed_expected_elongation().
#ifdef REDUCED_MEMORY_PTSEM
  tsem_pdf_cache = ts->Arbor()->TSEM_Pdf();
#endif
  double l_i = perturbed_expected_elongation();
  // Note: There is no normalization to other growth cones here!
#ifdef LEGACY_ELONGATION
  l_i *= random_double(0.2,1.8);
#endif
  // 5. elongate by proportion of available resources
  // double L_i = l_i * ts->Arbor()->ElongationModel()->elongation(ts->Arbor()); // [***NOTE] See TL#200807020156.5.
  double L_i = l_i * (eq->T()-prev_t); // All is done locally for these absolute elongation rates! (See TL#200807020156.5.)
#ifdef TESTING_ELONGATION_TOTAL
  usedarborelongationfraction += l_i;
#endif
#ifdef DEBUGGING_ELONGATION
  if (L_i<0.0) debugging.tsemb_elongate_decreasing++;
  else if (L_i==0.0) debugging.tsemb_elongate_stopped++;
#endif
  ts->add_length(L_i);
  // Test actual elongation pieces created per dt:
  //cout << String(L_i,"_%.8f") << '&' << String(l_i,"%.8f");
  if (figattr_update_terminal_segments_visibly) ts->update_segment_vector();
  prev_t = eq->T();
  return;
}

String BESTL_nonnormalizing_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" non-normalizing ");
  res += BESTL_terminal_segment_elongation_model::report_parameters_specific();
  return res;
}

BESTLNN_pyramidal_AD_terminal_segment_elongation_model::BESTLNN_pyramidal_AD_terminal_segment_elongation_model(terminal_segment_elongation_model_base * tsemcontrib, double & tsemweight, BESTLNN_pyramidal_AD_terminal_segment_elongation_model & schema): BESTL_nonnormalizing_terminal_segment_elongation_model(tsemcontrib,tsemweight,schema), parameters(schema.parameters), trunklengthpdf(NULL), obliquespdf(NULL), obliqueanglepdf(NULL) {
  // Chaining of models is taken care of in the base constructor.
  trunklengthpdf = schema.trunklengthpdf->clone();
  // Note that we do not generate a new parameters.trunk_length_SQ value here, so that the expected trunk length is not changed when passing branch nodes that connect to obliques.
  obliquespdf = schema.obliquespdf->clone();
  // Note that we do not generate a new number of obliques here, so that the expected number of oblique branches does not change as we pass branch nodes that connect to them.
  if (schema.obliqueanglepdf) obliqueanglepdf = schema.obliqueanglepdf->clone();
}

void BESTLNN_pyramidal_AD_terminal_segment_elongation_model::initialize_parameters() {
  // This is used to initialize trunk length and number of obliques parameters when new
  // PDFs may be selected by a constructor or when the TSEM of a root fiber terminal
  // segment is allocated (see TL#200808050218.2).
  parameters.trunk_length_SQ = trunklengthpdf->random_positive();
  parameters.trunk_length_SQ *= parameters.trunk_length_SQ;
  parameters.num_obliques = (int) obliquespdf->random_positive();
  parameters.dist2nextoblique = 0;
  set_next_oblique_distance();
}

BESTLNN_pyramidal_AD_terminal_segment_elongation_model::BESTLNN_pyramidal_AD_terminal_segment_elongation_model(String & thislabel, String & label, Command_Line_Parameters & clp): BESTL_nonnormalizing_terminal_segment_elongation_model(thislabel,label,clp), parameters(700.0*700.0,0,DBL_MAX,"pyrAP"), trunklengthpdf(NULL), obliquespdf(NULL), obliqueanglepdf(NULL) {
  // [*** Note:] See TL#200807020156.5 - WITHDRAWN! See TL#200807020156.10.
  trunklengthpdf = pdfselection(thislabel+"tsem.trunklength.",clp,X_elongate);
  if (!trunklengthpdf) trunklengthpdf = new normal_pdf(X_elongate,700.0,100.0);
  obliquespdf = pdfselection(thislabel+"tsem.obliques.",clp,X_elongate);
  if (!obliquespdf) obliquespdf = new normal_pdf(X_elongate,7.0,3.0);
  obliqueanglepdf = pdfselection(thislabel+"tsem.obliqueangle.",clp,X_elongate); // if NULL then there is no perturbation
  int n;
  if ((n=clp.Specifies_Parameter(thislabel+"tsem.prefix"))>=0) parameters.prefix = clp.ParValue(n);
  initialize_parameters();// Here we generate new values, since the PDFs may be new (see TL#200808050218.2).
  // Chaining of models is taken care of in the base constructor.
}

terminal_segment_elongation_model_base * BESTLNN_pyramidal_AD_terminal_segment_elongation_model::clone(terminal_segment * ts) {
  BESTLNN_pyramidal_AD_terminal_segment_elongation_model * tsemb = new BESTLNN_pyramidal_AD_terminal_segment_elongation_model(contributing,contributingweight,*this);
  if (ts) {
    if ((!ts->Prev()) && (!ts->Next())) { // We still use the regular ERI->initialize() call from the terminal_segment::branch() function when branching
      tsemb->initialize_parameters(); // Note: Fixed according to TL#200808050218.2.
      if (!(ts->ElongationRateInitializationModel())) error("ElongationRateInitializationModel() is NULL in BESTL_nonnormalizing_terminal_segment_elongation_model::clone().\n");
      else ts->ElongationRateInitializationModel()->root_initialize(tsemb->base_parameters.l_i_cache); // Insures that initial segments also draw from the ERI distribution (see TL#200807020156.10)
    }
  }
  return tsemb;
}

void BESTLNN_pyramidal_AD_terminal_segment_elongation_model::set_next_oblique_distance() {
  if (parameters.num_obliques>0) {
    parameters.dist2nextoblique = sqrt(parameters.dist2nextoblique) + (sqrt(parameters.trunk_length_SQ)/((double) parameters.num_obliques));
    parameters.dist2nextoblique *= parameters.dist2nextoblique;
    //cout << "NUM OBLIQUES: " << parameters.num_obliques << " NEXT OBLIQUE: " << parameters.dist2nextoblique << '\n';
  } else parameters.dist2nextoblique = DBL_MAX;
}

void BESTLNN_pyramidal_AD_terminal_segment_elongation_model::set_tuft_models(terminal_segment * ts) {
  // BEWARE: This function causes a self-destruct of the present terminal segment. Do not try to
  // modify any variables of the terminal segment object or its dependents after calling this
  // function!
  //cout << "TUFTING!"; cout.flush();
  if (Txt_tuftrootbranchnodelist) (*Txt_tuftrootbranchnodelist) += String((long) ts->TerminalSegment()) + '\n';
  ts->branch(NULL);
  String prefixstr(parameters.prefix);
  if (!prefixstr.empty()) prefixstr += ".tuft.";
  else prefixstr = "tuft.";
  // TSEMs
  terminal_segment_elongation_model_base * tsembptr1 = NULL;
  terminal_segment_elongation_model_selection(prefixstr,*main_clp,terminal_segment_elongation_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],tsembptr1);
  most_recent_branch1_ts->set_elongation_model(tsembptr1);
  terminal_segment_elongation_model_base * tsembptr2 = tsembptr1->clone(most_recent_branch2_ts);
  most_recent_branch2_ts->set_elongation_model(tsembptr2);
  // ERI models
  elongation_rate_initialization_model_base * eribptr = NULL;
  elongation_rate_initialization_model_selection(prefixstr,*main_clp,elongation_rate_initialization_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],eribptr);
  most_recent_branch1_ts->set_ERI_model(eribptr);
  eribptr->root_initialize(tsembptr1->base_parameters.l_i_cache); // reinitialize according to the new ERI model
  eribptr = eribptr->clone();
  most_recent_branch2_ts->set_ERI_model(eribptr);
  eribptr->root_initialize(tsembptr2->base_parameters.l_i_cache); // reinitialize according to the new ERI model
  // TSB models
  TSBM_base * tsbmbptr = NULL;
  TSBM_selection(prefixstr,*main_clp,TSBM_region_subset_schemas[0][all_pyramidal_dendrites_sps],tsbmbptr,most_recent_branch1_ts);
  most_recent_branch1_ts->set_TSBM_model(tsbmbptr);
  most_recent_branch2_ts->set_TSBM_model(tsbmbptr->clone(most_recent_branch2_ts));
  // Branch angle models
  branch_angle_model_base * bambptr = NULL;
  branch_angle_model_selection(prefixstr,*main_clp,branch_angle_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],bambptr);
  most_recent_branch1_ts->set_branch_angle_model(bambptr);
  most_recent_branch2_ts->set_branch_angle_model(bambptr->clone());
  // [***NOTE] Possibly recompute branch angles here in a manner similar to that used in terminal_segment::branch().
  // Direction models
  direction_model_base * dmbptr = NULL;
  direction_model_selection(prefixstr,*main_clp,direction_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],dmbptr);
  most_recent_branch1_ts->set_direction_model(dmbptr);
  most_recent_branch2_ts->set_direction_model(dmbptr->clone());
}

void BESTLNN_pyramidal_AD_terminal_segment_elongation_model::set_oblique_models(terminal_segment * ts) {
  // BEWARE: This function causes a self-destruct of the present terminal segment. Do not try to
  // modify any variables of the terminal segment object or its dependents after calling this
  // function!
  //cout << "OBLIQUE!"; cout.flush();
  if (Txt_obliquerootbranchnodelist) (*Txt_obliquerootbranchnodelist) += String((long) ts->TerminalSegment()) + '\n';
  // 1. For obliques, preserve the growth angle of the main branch and prepare an oblique angle
  spatial mainbranchacoords(ts->AngularCoords());
  mainbranchacoords.set_X(0.0);
  double veerangle = M_PI/2.0;
  if (obliqueanglepdf) veerangle += obliqueanglepdf->random_selection();
#ifdef VECTOR3D
  spatial obliqueacoords;
  veer(ts,veerangle,obliqueacoords,-1.0); // for the oblique branch
#endif
#ifdef VECTOR2D
  spatial obliqueacoords(ts->AngularCoords());
  if (X_turn.get_rand_real1()<0.5) obliqueacoords.add_Y(-veerangle);
  else obliqueacoords.add_Y(veerangle);
#endif
  obliqueacoords.set_X(0.0);
  ts->branch(NULL);
  // 2. Modify the growth angles of oblique branches to preserve the angle of the main branch and give obliques orthogonal angles
  most_recent_branch1_ts->set_angularcoords(mainbranchacoords); // Here, environmental pressures are also applied.
  most_recent_branch1_ts->update_segment_vector();
  most_recent_branch2_ts->set_angularcoords(obliqueacoords); // Here, environmental pressures are also applied.
  most_recent_branch2_ts->update_segment_vector();
  // 3. Set models for the oblique branch
  String prefixstr(parameters.prefix);
  if (!prefixstr.empty()) prefixstr += ".oblique.";
  else prefixstr = "oblique.";
  // TSEMs
  terminal_segment_elongation_model_base * tsembptr = NULL;
  terminal_segment_elongation_model_selection(prefixstr,*main_clp,terminal_segment_elongation_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],tsembptr);
  // most_recent_branch1_ts retains the current apical trunk settings
  most_recent_branch2_ts->set_elongation_model(tsembptr);
  // ERI models
  elongation_rate_initialization_model_base * eribptr = NULL;
  elongation_rate_initialization_model_selection(prefixstr,*main_clp,elongation_rate_initialization_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],eribptr);
  // most_recent_branch1_ts retains the current apical trunk settings
  most_recent_branch2_ts->set_ERI_model(eribptr);
  eribptr->root_initialize(tsembptr->base_parameters.l_i_cache); // reinitialize according to the new ERI model
  // TSB models
  TSBM_base * tsbmbptr = NULL;
  TSBM_selection(prefixstr,*main_clp,TSBM_region_subset_schemas[0][all_pyramidal_dendrites_sps],tsbmbptr,most_recent_branch2_ts);
  most_recent_branch2_ts->set_TSBM_model(tsbmbptr);
  // Branch angle models
  branch_angle_model_base * bambptr = NULL;
  branch_angle_model_selection(prefixstr,*main_clp,branch_angle_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],bambptr);
  most_recent_branch2_ts->set_branch_angle_model(bambptr);
  // [***NOTE] Possibly recompute branch angles here in a manner similar to that used in terminal_segment::branch().
  // Direction models
  direction_model_base * dmbptr = NULL;
  direction_model_selection(prefixstr,*main_clp,direction_model_region_subset_schemas[0][all_pyramidal_dendrites_sps],dmbptr);
  most_recent_branch2_ts->set_direction_model(dmbptr);
}

void BESTLNN_pyramidal_AD_terminal_segment_elongation_model::elongate(terminal_segment * ts) {
  // PROTOCOL: This is called during growth.
  // This function calls:
  //   perturbed_expected_elongation()
  //   ElongationModel()->elongation()
  //   ts->add_length()
  //   ts->update_segment_vector()
  if (eq->T()<=prev_t) return;
  // 1., 2. & 3 compute perturbed expected elongation
  // The three regular steps of the model call have been turned into
  // a separate function here, so that the calculations can be invoked
  // for all terminal segments as needed, while the sum of those results
  // is needed for the elongation by proportional allocation of resources.
  // [***NOTE] If the elongation calculations for specific terminal segments
  // need to access other information about the terminal segment, or about
  // other objects in the ts->Arbor(), then we can pass the ts parameter to
  // other member functions, such as perturbed_expected_elongation().
#ifdef REDUCED_MEMORY_PTSEM
  tsem_pdf_cache = ts->Arbor()->TSEM_Pdf();
#endif
  double l_i = perturbed_expected_elongation();
  // Note: There is no normalization to other growth cones here!
#ifdef LEGACY_ELONGATION
  l_i *= random_double(0.2,1.8);
#endif
  // 5. elongate by proportion of available resources
  // double L_i = l_i * ts->Arbor()->ElongationModel()->elongation(ts->Arbor()); // [***NOTE] See TL#200807020156.5.
  double L_i = l_i * (eq->T()-prev_t); // All is done locally for these absolute elongation rates! (See TL#200807020156.5.)
#ifdef TESTING_ELONGATION_TOTAL
  usedarborelongationfraction += l_i;
#endif
#ifdef DEBUGGING_ELONGATION
  if (L_i<0.0) debugging.tsemb_elongate_decreasing++;
  else if (L_i==0.0) debugging.tsemb_elongate_stopped++;
#endif
  ts->add_length(L_i);
  // Test actual elongation pieces created per dt:
  //cout << String(L_i,"_%.8f");
  // Is it time to tuft?
  ts->update_segment_vector();
  double dist2tosoma = ts->TerminalSegment()->cartesian_soma_center_distance2();
  prev_t = eq->T(); // do this first to avoid self-destruct problems (see set_tuft_models() and set_oblique_models())
  if (dist2tosoma>=parameters.trunk_length_SQ) set_tuft_models(ts);
  else { // Is it time for an oblique branch?
    if (dist2tosoma>=parameters.dist2nextoblique) {
      set_next_oblique_distance(); // do this first to avoid self-destruct problems (see set_oblique_models())
      set_oblique_models(ts);
    }
  }
  return;
}

String BESTLNN_pyramidal_AD_terminal_segment_elongation_model::report_parameters_specific() {
  String res(" pyramidal apical dendrite prototyping ");
  res += BESTL_nonnormalizing_terminal_segment_elongation_model::report_parameters_specific();
  res += " trunk length PDF: " + trunklengthpdf->report_parameters();
  res += " obliques PDF: " + obliquespdf->report_parameters();
  res += " prefix: " + parameters.prefix;
  return res;
}

elongation_rate_initialization_model_base::elongation_rate_initialization_model_base(elongation_rate_initialization_model_base * ericontrib, double & eriweight, elongation_rate_initialization_model_base & schema): contributing(NULL), contributingweight(1.0) { //, base_parameters(schema.base_parameters) {
  if (ericontrib) { // Clone chained models
    contributingweight = eriweight;
    contributing = ericontrib->clone();
  }
  //pdf = schema.pdf->clone();
}

elongation_rate_initialization_model_base::elongation_rate_initialization_model_base(String & thislabel, String & label, Command_Line_Parameters & clp): contributing(NULL), contributingweight(1.0) { //, base_parameters(0.0) {
  // This constructor is intended for use by schemas, which can set up model
  // chains, as well as look for general (thislabel=="") or subset parameter
  // settings that differ from the compile-time defaults.
  // (See TL#200603120618.5.)
  // [***NOTE] This is not a CLP_Modifiable, because I do not wish to have to
  // store the label in order to be able to parse_CLP at any time.
  //pdf = pdfselection(thislabel+"tsem.",clp,X_elongate);
  //if (!pdf) pdf = new normal_pdf(X_elongate,0.0,0.3,1.0); // a normal PDF is the default for TSEMs
  //else if (pdf->amplitude()>1.0) warning("Warning: Elongation Rate Initialization Model has perturbation with amplitude greater than 1.0!\n");
  if (label.empty()) return;
  int n;
  if ((n=clp.Specifies_Parameter(label+"eri_weight"))>=0) contributingweight = atof(clp.ParValue(n));
  elongation_rate_initialization_model_selection(label,clp,contributing,contributing);
}

void elongation_rate_initialization_model_base::handle_branching(terminal_segment & ts1, terminal_segment & ts2) {
  ts2.set_ERI_model(clone());
  ts1.set_ERI_model(this);
}

void elongation_rate_initialization_model_base::handle_turning(terminal_segment & ts) {
  ts.set_ERI_model(this);
}

#define SWAPDOUBLES(a,b) { \
double swaptmp = a; \
a = b; \
b = swaptmp; \
}

double elongation_rate_initialization_model_base::Get_Mean_and_Max_Quotas(terminal_segment * t, double & maxquota) {
  if (!t) {
    maxquota = 1.0;
    return 1.0;
  }
  double meanquota = 0.0;
  int quotacnt = 0;
  PLL_LOOP_FORWARD(terminal_segment,t,1) {
    double quota = e->ElongationModel()->base_parameters.l_i_cache;
    meanquota += quota;
    if (quota>maxquota) maxquota = quota;
    quotacnt++;
  }
  if (quotacnt<=1) return meanquota;
  return meanquota / ((double) quotacnt);
}

double elongation_rate_initialization_model_base::predict(double weight, double & predictedquota1, double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2) {
  // Called in a chain of ERI models to apply, where the first model
  // in the chain is automatically assigned the weight 1.0 and the weighting
  // of other direction models is proportional to that (i.e. it can be > 1.0).
  // The actual weighting of contributions is done in the function predict_initial_quota().
  // The sum of all weights is returned, so that division by that sum can produce
  // properly scaled combined branch angles.
  predict_initial_quota(predictedquota1,predictedquota2,ts1,ts2,weight);
  if (contributing) weight += contributing->predict(contributingweight,predictedquota1,predictedquota2,ts1,ts2);
  return weight;
}

void elongation_rate_initialization_model_base::initialize(terminal_segment * ts1, terminal_segment * ts2) {
  // PROTOCOL: This is called at bifurcation during growth.
  // This function calls:
  //   predict_initial_quota()
  //   predict()
  double predictedquota1 = 0.0, predictedquota2 = 0.0;
  predict_initial_quota(predictedquota1,predictedquota2,ts1,ts2);
  if (contributing) {
    double summedcontributingweights = 1.0 + contributing->predict(contributingweight,predictedquota1,predictedquota2,ts1,ts2);
    predictedquota1 /= summedcontributingweights; predictedquota2 /= summedcontributingweights;
  }
  // [***NOTE] Do we need to do anything about the initial elongation quotas stored in
  //           local l_i_cache variables of contributing models, or should those remain
  //           explicitly unaffected, in case they convey a local normalized contribution?
  ts1->ElongationModel()->base_parameters.l_i_cache = predictedquota1;
  ts2->ElongationModel()->base_parameters.l_i_cache = predictedquota2;
}

String elongation_rate_initialization_model_base::report_parameters() {
  // This calls report_parameters_specific() in any derived class and
  // propagates parameter reporting tocontributing models.
  String res(report_parameters_specific());
  //res += " p_TSEM=" + pdf->report_parameters();
  if (contributing) {
    res += String(contributingweight,"\n  [contrib.w=%.2f] ");
    res += contributing->report_parameters();
  }
  return res;
}

length_distribution_eri_model::length_distribution_eri_model(elongation_rate_initialization_model_base * ericontrib, double & eriweight, length_distribution_eri_model & schema): elongation_rate_initialization_model_base(ericontrib,eriweight,schema) {
  // Chaining of models is taken care of in the base constructor.
  pdf = schema.pdf->clone();
}

length_distribution_eri_model::length_distribution_eri_model(String & thislabel, String & label, Command_Line_Parameters & clp): elongation_rate_initialization_model_base(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
  //cout << "SETTING UP LENGTH DISTR. model\n";
  pdf = pdfselection(thislabel+"eri.",clp,X_elongate);
  //if (!pdf) cout << "NO SPECIFIC PDF FOUND\n";
  if (!pdf) pdf = new normal_pdf(X_elongate,0.0,1.0,3.0); // a normal PDF is the default for TSEMs
  //  else if (pdf->amplitude()>1.0) warning("Warning: Elongation Rate Initialization Model has perturbation with amplitude greater than 1.0!\n");
}

elongation_rate_initialization_model_base * length_distribution_eri_model::clone() {
  return new length_distribution_eri_model(contributing,contributingweight,*this);
}

void length_distribution_eri_model::predict_initial_quota(double & predictedquota1,double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2,double weight) {
  // Note that this model relates to considerations described in
  // TL#200709152200.1 and linked task log entries.
  // (See source code dated prior to 20080425 for the older implementation
  // that was changed in response to problems dealt with in TL#200804182330.1.)
  // We compute a (normal) random number. The standard mean value is the mean
  // of existing quotas. Negative values are compressed through the sigmoid
  // function 2*(1/1+e(-t)), so that a value of -inf equates to a zero quota
  // and a value of 0 equates to 1.0*meanquota of previously existing growth
  // cones. Positive values are converted as (0.5*t)+1, so that 0 is again
  // equal to 1.0*meanquota and 2 becomes 2*meanquota. Clearly now, it is
  // possible change both the possible relative slowdown or speedup by changing
  // the standard deviation of the normal function. The std 1.0 will be set as
  // the compiled default. Changing the mean value away from 0.0 can do more
  // drastic things by biasing new initial elongation rates to be slower or
  // faster than the elongation rates of existing growth cones.
  //cout << "ENTERED LENGTH-DISTR. quota prediction" << pdf->report_parameters() << '\n';
  double len1 = ts1->Length();
  double len2 = ts2->Length();
  double Xa = pdf->random_selection();
  double Xb = pdf->random_selection();
  if (Xa<0.0) Xa = 2.0/(1.0+exp(-Xa));
  else Xa = (0.5*Xa)+1.0;
  if (Xb<0.0) Xb = 2.0/(1.0+exp(-Xb));
  else Xb = (0.5*Xb)+1.0;
  // Initial quotas take into account the ratio len1:len2, or at least their order
  if (len1>len2) {
    if (Xa<Xb) SWAPDOUBLES(Xa,Xb);
  } else {
    if (Xa>Xb) SWAPDOUBLES(Xa,Xb);
  }
  // If the terminal segment elongation models of ts1 and ts2 do not have any
  // contributing models (contributing==NULL), and they are of a BESTL type,
  // then there is no need to compute a mean to compete with, and all will
  // be initialized around the same mean 1.0. Otherwise, a mean is computed
  // by inspecting all terminal segments. For simplicity, we always obtain
  // the current mean and maximum here.
  // [***NOTE] This depends on ts1->Next() being the old first terminal segment
  // in the list.
  double maxquota = 1.0;
  double meanquota = Get_Mean_and_Max_Quotas(ts1->Next(),maxquota);
  // What to do about unbounded probability distributions (amplitude == DBL_MAX - mean_X)?
  // Watch out for amplitude == 0
  // Suggest the use of a truncation value
  // Scaling
  //double maxamp = 2*(maxquota-meanquota);
  Xa *= meanquota;
  Xb *= meanquota;
  predictedquota1 += (weight*Xa);
  predictedquota2 += (weight*Xb);
#ifdef TESTQUOTAS
  quotas += String(eq->T(),"t=%.2f, ") + String(meanquota,"MQ=%.5f, ") + String(predictedquota1,"b1Q=%.5f, ") + String(predictedquota2,"b2Q=%.2f\n");
#endif
}

String length_distribution_eri_model::report_parameters_specific() {
  String res(" length distribution");
  res += pdf->report_parameters();
  return res;
}

nonnorm_BESTL_length_distribution_eri_model::nonnorm_BESTL_length_distribution_eri_model(elongation_rate_initialization_model_base * ericontrib, double & eriweight, nonnorm_BESTL_length_distribution_eri_model & schema): length_distribution_eri_model(ericontrib,eriweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

nonnorm_BESTL_length_distribution_eri_model::nonnorm_BESTL_length_distribution_eri_model(String & thislabel, String & label, Command_Line_Parameters & clp): length_distribution_eri_model(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
}

elongation_rate_initialization_model_base * nonnorm_BESTL_length_distribution_eri_model::clone() {
  return new nonnorm_BESTL_length_distribution_eri_model(contributing,contributingweight,*this);
}

void nonnorm_BESTL_length_distribution_eri_model::predict_initial_quota(double & predictedquota1,double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2,double weight) {
  // This model applies the same correlation between initial branch lengths and
  // initial branch elongation rates as the length_distribution_eri_model, but
  // considers elongation rates absolute in accordance with notes in TL#200807020156.5.
  double len1 = ts1->Length();
  double len2 = ts2->Length();
  double Xa = pdf->random_positive();
  double Xb = pdf->random_positive();
  // Initial quotas take into account the ratio len1:len2, or at least their order
  if (len1>len2) {
    if (Xa<Xb) SWAPDOUBLES(Xa,Xb);
  } else {
    if (Xa>Xb) SWAPDOUBLES(Xa,Xb);
  }
  predictedquota1 += (weight*Xa);
  predictedquota2 += (weight*Xb);
}

void nonnorm_BESTL_length_distribution_eri_model::root_initialize(double & l_i_cache) {
  // [***INCOMPLETE] We may add "contributing" parts here, just as in the initialize() function, resulting in a weighted sum.
  l_i_cache = pdf->random_positive();
}

String nonnorm_BESTL_length_distribution_eri_model::report_parameters_specific() {
  String res(" non-normalizing BESTL length distribution");
  res += pdf->report_parameters();
  return res;
}

pure_stochastic_eri_model::pure_stochastic_eri_model(elongation_rate_initialization_model_base * ericontrib, double & eriweight, pure_stochastic_eri_model & schema): elongation_rate_initialization_model_base(ericontrib,eriweight,schema) {
  // Chaining of models is taken care of in the base constructor.
  pdf = schema.pdf->clone();
}

pure_stochastic_eri_model::pure_stochastic_eri_model(String & thislabel, String & label, Command_Line_Parameters & clp): elongation_rate_initialization_model_base(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
  pdf = pdfselection(thislabel+"eri.",clp,X_elongate);
  if (!pdf) pdf = new normal_pdf(X_elongate,1.0,0.3,2.0); // a normal PDF is the default for TSEMs
  else if (pdf->amplitude()>1.0) warning("Warning: Elongation Rate Initialization Model has perturbation with amplitude greater than 1.0!\n");
}

elongation_rate_initialization_model_base * pure_stochastic_eri_model::clone() {
  return new pure_stochastic_eri_model(contributing,contributingweight,*this);
}

void pure_stochastic_eri_model::predict_initial_quota(double & predictedquota1,double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2,double weight) {
  // Note that this model relates to considerations described in
  // TL#200709152200.1 and linked task log entries.
  // (See source code dated prior to 20080425 for the older implementation
  // that was changed in response to problems dealt with in TL#200804182330.1.)
  // We compute a (normal) random number. The standard mean value is the mean
  // of existing quotas. Negative values are compressed through the sigmoid
  // function 2*(1/1+e(-t)), so that a value of -inf equates to a zero quota
  // and a value of 0 equates to 1.0*meanquota of previously existing growth
  // cones. Positive values are converted as (0.5*t)+1, so that 0 is again
  // equal to 1.0*meanquota and 2 becomes 2*meanquota. Clearly now, it is
  // possible change both the possible relative slowdown or speedup by changing
  // the standard deviation of the normal function. The std 1.0 will be set as
  // the compiled default. Changing the mean value away from 0.0 can do more
  // drastic things by biasing new initial elongation rates to be slower or
  // faster than the elongation rates of existing growth cones.
  double Xa = pdf->random_selection();
  double Xb = pdf->random_selection();
  if (Xa<0.0) Xa = 2.0/(1.0+exp(-Xa));
  else Xa = (0.5*Xa)+1.0;
  if (Xb<0.0) Xb = 2.0/(1.0+exp(-Xb));
  else Xb = (0.5*Xb)+1.0;
  // If the terminal segment elongation models of ts1 and ts2 do not have any
  // contributing models (contributing==NULL), and they are of a BESTL type,
  // then there is no need to compute a mean to compete with, and all will
  // be initialized around the same mean 1.0. Otherwise, a mean is computed
  // by inspecting all terminal segments. For simplicity, we always obtain
  // the current mean and maximum here.
  // [***NOTE] This depends on ts1->Next() being the old first terminal segment
  // in the list.
  double maxquota = 1.0;
  double meanquota = Get_Mean_and_Max_Quotas(ts1->Next(),maxquota);
  // What to do about unbounded probability distributions (amplitude == DBL_MAX - mean_X)?
  // Watch out for amplitude == 0
  // Suggest the use of a truncation value
  // Scaling
  //double maxamp = 2*(maxquota-meanquota);
  Xa *= meanquota;
  Xb *= meanquota;
  predictedquota1 += (weight*Xa);
  predictedquota2 += (weight*Xb);
}

String pure_stochastic_eri_model::report_parameters_specific() {
  String res(" pure stochastic");
  return res;
}

zero_eri_model::zero_eri_model(elongation_rate_initialization_model_base * ericontrib, double & eriweight, zero_eri_model & schema): elongation_rate_initialization_model_base(ericontrib,eriweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

zero_eri_model::zero_eri_model(String & thislabel, String & label, Command_Line_Parameters & clp): elongation_rate_initialization_model_base(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor.
}

elongation_rate_initialization_model_base * zero_eri_model::clone() {
  return new zero_eri_model(contributing,contributingweight,*this);
}

void zero_eri_model::predict_initial_quota(double & predictedquota1,double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2,double weight) {
//predictedquota1 += 0.0; This code is omitted, as it does not actually do anything.
//predictedquota2 += 0.0;
}

String zero_eri_model::report_parameters_specific() {
  String res(" zero");
  return res;
}

unitary_eri_model::unitary_eri_model(elongation_rate_initialization_model_base * ericontrib, double & eriweight, unitary_eri_model & schema): elongation_rate_initialization_model_base(ericontrib,eriweight,schema), unitary_parameters(schema.unitary_parameters) {
  // Chaining of models is taken care of in the base constructor.
}

unitary_eri_model::unitary_eri_model(String & thislabel, String & label, Command_Line_Parameters & clp): elongation_rate_initialization_model_base(thislabel,label,clp), unitary_parameters(1.0) {
  // Chaining of models is taken care of in the base constructor
  int n;
  if ((n=clp.Specifies_Parameter(label+"eri_initialquota"))>=0) unitary_parameters.initialquota = atof(clp.ParValue(n));
}

elongation_rate_initialization_model_base * unitary_eri_model::clone() {
  return new unitary_eri_model(contributing,contributingweight,*this);
}

void unitary_eri_model::predict_initial_quota(double & predictedquota1,double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2,double weight) {
  predictedquota1 += unitary_parameters.initialquota;
  predictedquota2 += unitary_parameters.initialquota;
}

String unitary_eri_model::report_parameters_specific() {
  String res(" unitary");
  return res;
}

continue_defaults_eri_model::continue_defaults_eri_model(elongation_rate_initialization_model_base * ericontrib, double & eriweight, continue_defaults_eri_model & schema): elongation_rate_initialization_model_base(ericontrib,eriweight,schema) {
  // Chaining of models is taken care of in the base constructor.
}

continue_defaults_eri_model::continue_defaults_eri_model(String & thislabel, String & label, Command_Line_Parameters & clp): elongation_rate_initialization_model_base(thislabel,label,clp) {
  // Chaining of models is taken care of in the base constructor
}

elongation_rate_initialization_model_base * continue_defaults_eri_model::clone() {
  return new continue_defaults_eri_model(contributing,contributingweight,*this);
}

void continue_defaults_eri_model::predict_initial_quota(double & predictedquota1,double & predictedquota2, terminal_segment * ts1, terminal_segment * ts2,double weight) {
  predictedquota1 += ts1->ElongationModel()->base_parameters.l_i_cache;
  predictedquota2 += ts2->ElongationModel()->base_parameters.l_i_cache;
}

String continue_defaults_eri_model::report_parameters_specific() {
  String res(" continue defaults");
  return res;
}

void terminal_segment_elongation_event::event() {
  // [***NOTE] I can log this event (see IFspike): devres->elongation(ts,t); 
  ts->ElongationModel()->elongate(ts);
}

String arbor_elongation_model_idstr[NUM_aem] = {
  "van_pelt", // downcased!
  "polynomial_o1",
  "polynomial_o3"
};

arbor_elongation_model_base * arbor_elongation_model_selection(String & label, Command_Line_Parameters & clp, arbor_elongation_model_base * superior_set, arbor_elongation_model_base_ptr & aembptr) {
  // Note: Providing the result pointer in aembptr enables this function to
  // delete a previously defined schema before linking to a new one.
  // PROTOCOL: [See 200709130802.] Sets for which a arbor elongation model is not defined explicitly inherit the arbor elongation model of
  //           their specific direct superior by cloning it, thereby also inheriting parameters specified therein.
  arbor_elongation_model aem = NUM_aem; // implies undefined
  String nextlabel;
  int n;
  // Determine if a specific model is selected for the set with the given label
  if ((n=clp.Specifies_Parameter(label+"arbor_elongation_model"))>=0) {
    String aemstr(downcase(clp.ParValue(n)));
    for (arbor_elongation_model i = arbor_elongation_model(0); i < NUM_aem; i = arbor_elongation_model(i+1)) if (aemstr==arbor_elongation_model_idstr[i]) { aem = i; break; }
    if (aem==NUM_aem) return NULL; // catch arbor_elongation_model syntax errors
    if (label.empty()) general_arbor_elongation_model = aem; // modify default if setting universal
  } else if (label.empty()) aem = general_arbor_elongation_model; // the universal set must have a model, since it cannot inherit
  // Determine if a following model selection should include chaining detection
  if ((n=clp.Specifies_Parameter(label+"arbor_elongation_model_label"))>=0) {
    nextlabel = label+clp.URI_unescape_ParValue(n)+'.';
    if (label.empty()) general_arbor_elongation_model_root = nextlabel; // if at most general level
  } else if ((n=clp.Specifies_Parameter(label+"aem_label"))>=0) { // alternate syntax
    nextlabel = label+clp.URI_unescape_ParValue(n)+'.';
    if (label.empty()) general_arbor_elongation_model_root = nextlabel; // to report at universal level
  }
  // Clean up any previously defined schema
  if (aembptr) aembptr->delete_shared();
  // Set and return specified model schema or general model schema if at most general level
  switch (aem) {
  case van_Pelt_aem:
    aembptr = new van_Pelt_arbor_elongation_model(label,nextlabel,clp);
    break;;
  case polynomial_O1_aem:
    aembptr = new Polynomial_O1_arbor_elongation_model(label,nextlabel,clp);
    break;;
  case polynomial_O3_aem:
    aembptr = new Polynomial_O3_arbor_elongation_model(label,nextlabel,clp);
    break;;
  default: // If not specified, inherit from immediate superior set
    if (!superior_set) return NULL;
    aembptr = superior_set->clone();
    break;;
  }
  // In case a secondary schema reference is provided through aembptr for the universal set level, create a clone for the universal set
  if ((label.empty()) && (arbor_elongation_model_region_subset_schemas[0][universal_sps]!=aembptr)) {
    if (arbor_elongation_model_region_subset_schemas[0][universal_sps]) arbor_elongation_model_region_subset_schemas[0][universal_sps]->delete_shared();
    arbor_elongation_model_region_subset_schemas[0][universal_sps] = aembptr->clone();
  }
  return aembptr;
}

String terminal_segment_elongation_model_idstr[NUM_tsem] = {
  "simple",
  "inertia",
  "second_order",
  "constrained_second_order",
  "initialized_cso",
  "decaying_second_order",
  "bestl",
  "nonnorm_bestl", // downcased!
  "pyrad_bestlnn" // downcased!
};

terminal_segment_elongation_model_base * terminal_segment_elongation_model_selection(String & label, Command_Line_Parameters & clp, terminal_segment_elongation_model_base * superior_set, terminal_segment_elongation_model_base_ptr & tsembptr) {
  // Note: Providing the result pointer in tsembptr enables this function to
  // delete a previously defined schema before linking to a new one.
  // PROTOCOL: [See 200709130802.] Sets for which a terminal segment elongation model is not defined explicitly inherit the terminal segment elongation model of
  //           their specific direct superior by cloning it, thereby also inheriting parameters specified therein.
  terminal_segment_elongation_model tsem = NUM_tsem; // implies undefined
  String nextlabel;
  int n;
  // Determine if a specific model is selected for the set with the given label
  if ((n=clp.Specifies_Parameter(label+"terminal_segment_elongation_model"))>=0) {
    String tsemstr(downcase(clp.ParValue(n)));
    for (terminal_segment_elongation_model i = terminal_segment_elongation_model(0); i < NUM_tsem; i = terminal_segment_elongation_model(i+1)) if (tsemstr==terminal_segment_elongation_model_idstr[i]) { tsem = i; break; }
    if (tsem==NUM_tsem) return NULL; // catch terminal_segment_elongation_model syntax errors
    if (label.empty()) general_terminal_segment_elongation_model = tsem; // modify default if setting universal
  } else if (label.empty()) tsem = general_terminal_segment_elongation_model; // the universal set must have a model, since it cannot inherit
  // Determine if a following model selection should include chaining detection
  if ((n=clp.Specifies_Parameter(label+"terminal_segment_elongation_model_label"))>=0) {
    nextlabel = label+clp.URI_unescape_ParValue(n)+'.';
    if (label.empty()) general_terminal_segment_elongation_model_root = nextlabel; // if at most general level
  } else if ((n=clp.Specifies_Parameter(label+"tsem_label"))>=0) { // alternate syntax
    nextlabel = label+clp.URI_unescape_ParValue(n)+'.';
    if (label.empty()) general_terminal_segment_elongation_model_root = nextlabel; // to report at universal level
  }
  // Clean up any previously defined schema
  if (tsembptr) tsembptr->delete_shared();
  // Set and return specified model schema or general model schema if at most general level
  switch (tsem) {
  case simple_tsem:
    tsembptr = new simple_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case inertia_tsem:
    tsembptr = new inertia_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case second_order_tsem:
    tsembptr = new second_order_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case constrained_second_order_tsem:
    tsembptr = new constrained_second_order_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case initialized_CSO_tsem:
    tsembptr = new initialized_CSO_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case decaying_second_order_tsem:
    tsembptr = new decaying_second_order_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case BESTL_tsem:
    tsembptr = new BESTL_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case nonnorm_BESTL_tsem:
    tsembptr = new BESTL_nonnormalizing_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  case pyrAD_BESTLNN_tsem:
    tsembptr = new BESTLNN_pyramidal_AD_terminal_segment_elongation_model(label,nextlabel,clp);
    break;;
  default: // If not specified, inherit from immediate superior set
    if (!superior_set) return NULL;
    tsembptr = superior_set->clone(NULL);
    break;;
  }
  // In case a secondary schema reference is provided through aembptr for the universal set level, create a clone for the universal set
  if ((label.empty()) && (terminal_segment_elongation_model_region_subset_schemas[0][universal_sps]!=tsembptr)) {
    if (terminal_segment_elongation_model_region_subset_schemas[0][universal_sps]) terminal_segment_elongation_model_region_subset_schemas[0][universal_sps]->delete_shared();
    terminal_segment_elongation_model_region_subset_schemas[0][universal_sps] = tsembptr->clone(NULL);
  }
  return tsembptr;
}

String elongation_rate_initialization_model_idstr[NUM_eri] = {
  "length_distribution",
  "nonnorm_bestl_length_distribution",
  "pure_stochastic",
  "zero",
  "unitary",
  "continue_defaults"
};

elongation_rate_initialization_model_base * elongation_rate_initialization_model_selection(String & label, Command_Line_Parameters & clp, elongation_rate_initialization_model_base * superior_set, elongation_rate_initialization_model_base_ptr & eribptr) {
  // Note: Providing the result pointer in eribptr enables this function to
  // delete a previously defined schema before linking to a new one.
  // PROTOCOL: [See 200709130802.] Sets for which an elongation rate initialization model is not defined explicitly inherit the elongation rate initialization model of
  //           their specific direct superior by cloning it, thereby also inheriting parameters specified therein.
  elongation_rate_initialization_model eri = NUM_eri; // implies undefined
  String nextlabel;
  int n;
  // Determine if a specific model is selected for the set with the given label
  if ((n=clp.Specifies_Parameter(label+"elongation_rate_initialization_model"))>=0) {
    String eristr(downcase(clp.ParValue(n)));
    for (elongation_rate_initialization_model i = elongation_rate_initialization_model(0); i < NUM_eri; i = elongation_rate_initialization_model(i+1)) if (eristr==elongation_rate_initialization_model_idstr[i]) { eri = i; break; }
    if (eri==NUM_eri) return NULL; // catch elongation_rate_initialization_model syntax errors
    if (label.empty()) default_elongation_rate_initialization_model = eri; // modify default if setting universal
  } else if (label.empty()) eri = default_elongation_rate_initialization_model; // the universal set must have a model, since it cannot inherit
  // Determine if a following model selection should include chaining detection
  if ((n=clp.Specifies_Parameter(label+"elongation_rate_initialization_model_label"))>=0) {
    nextlabel = label+clp.URI_unescape_ParValue(n)+'.';
    if (label.empty()) universal_elongation_rate_initialization_model_root = nextlabel; // if at most general level
  } else if ((n=clp.Specifies_Parameter(label+"eri_label"))>=0) { // alternate syntax
    nextlabel = label+clp.URI_unescape_ParValue(n)+'.';
    if (label.empty()) universal_elongation_rate_initialization_model_root = nextlabel; // to report at universal level
  }
  // Clean up any previously defined schema
  if (eribptr) eribptr->delete_shared();
  // Set and return specified model schema or universal model schema if at universal level
  switch (eri) {
  case length_distribution_eri:
    eribptr = new length_distribution_eri_model(label,nextlabel,clp);
    break;;
  case nonnorm_BESTL_length_distribution_eri:
    eribptr = new nonnorm_BESTL_length_distribution_eri_model(label,nextlabel,clp);
    break;;
  case pure_stochastic_eri:
    eribptr = new pure_stochastic_eri_model(label,nextlabel,clp);
    break;;
  case zero_eri:
    eribptr = new zero_eri_model(label,nextlabel,clp);
    break;;
  case unitary_eri:
    eribptr = new unitary_eri_model(label,nextlabel,clp);
    break;;
  case continue_defaults_eri:
    eribptr = new continue_defaults_eri_model(label,nextlabel,clp);
    break;;
  default: // If not specified, inherit from immediate superior set
    if (!superior_set) return NULL;
    eribptr = superior_set->clone();
    break;;
  }
  // In case a secondary schema reference is provided through aembptr for the universal set level, create a clone for the universal set
  if ((label.empty()) && (elongation_rate_initialization_model_region_subset_schemas[0][universal_sps]!=eribptr)) {
    if (elongation_rate_initialization_model_region_subset_schemas[0][universal_sps]) elongation_rate_initialization_model_region_subset_schemas[0][universal_sps]->delete_shared();
    elongation_rate_initialization_model_region_subset_schemas[0][universal_sps] = eribptr->clone();
  }
  return eribptr;
}