-
Notifications
You must be signed in to change notification settings - Fork 0
/
ColDAEpp.hpp
3723 lines (3323 loc) · 161 KB
/
ColDAEpp.hpp
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
#pragma once
#define FMT_HEADER_ONLY
#include "fmt/include/fmt/format.h"
#include "fmt/include/fmt/ranges.h"
#include "fmt/include/fmt/color.h"
#include <vector>
namespace coldae {
enum class printMode {
full = -1,
selected = 0,
none = 1
};
enum class meshMode {
/* causes coldae to generate a uniform initial mesh */
generate = 0,
/* if the initial mesh is provided by the user.
It is defined in fspace as follows : the mesh
aleft = x(1) < x(2) < ... < x(n) < x(n + 1) = aright
will occupy fspace(1), ..., fspace(n + 1).
the user needs to supply only the interior mesh points fspace(j) = x(j) with j = 2, ..., n. */
custom = 1,
/* if the initial mesh is supplied by the user as with meshMode::generate,
and in addition no adaptive mesh selection is to be done. */
customNoAdaptive = 2
};
enum class guessMode {
/* if no initial guess for the solution is provided */
none = 0,
/* if an initial guess is provided by the user in subroutine 'guess' */
custom = 1,
/* if an initial mesh and approximate solution coefficients are provided by the user in 'fspace'.
(the former and new mesh are the same). (see description of output for further details) */
customCoefficients = 2,
/* if a former mesh and approximate solution coefficients are provided by the user in 'fspace',
and the new mesh is to be taken twice as coarse; i.e., every second point from the former mesh.
(see description of output for further details) */
customCoefficientsRefine = 3,
/* if in addition to a former initial mesh approximate solution coefficients,
a new mesh is provided in 'fspace' as well.
(see description of output for further details) */
newMesh = 4
};
enum class regularityControl {
/* if the first relax factor is RSTART (use for an extra sensitive nonlinear problem only) */
sensitive = -1,
/* if the problem is regular */
regular = 0,
/*if the newton iterations are not to be damped (use for initial value problems).*/
noDamping = 1,
/* if we are to return immediately upon
(a) two successive nonconvergences, or
(b) after obtaining error estimate for the first time.*/
fragile = 2
};
enum class indexControl {
/* determines the appropriate projection needed at the right end of each
mesh subinterval using SVD. this is the most expensive and most general option. */
automatic = 0,
/* if the index of the dae is 1. */
one = 1,
/* if the index of the dae is 2 and it is in Hessenberg form */
twoHessenberg = 2
};
enum class result_t {
normal = 1, // for normal return
singular = 0, // if the collocation matrix is singular
outOfMemory = -1, // if the expected no. of subintervals exceeds storage specifications
notConverged = -2, // if the nonlinear iteration has not converged
inputError = -3 // if there is an input data error.
};
/* System specific properties */
struct systemParameters {
int ncomp; // number of differential equations (<= 20)
int ny; // number of constraints (<= 20)
std::vector<int> orders; // orders of odes
double left; // left end of interval
double right; // right end of interval
std::vector<double> bcpoints; // j-th side condition point (boundary point)
bool isNonLinear; // if the problem is nonlinear
regularityControl regularity;
indexControl index; // index of DAE (ignored if ny=0)
// mstar = sum of all orders
int getMstar() const {
int mstar{0};
for (int o: orders) mstar += o;
return mstar;
}
};
/* Options for the solver */
struct options {
int numCollPoints = 0; // no. of collocation points per subinterval (0=auto)
int numSubIntervals = 0; // no. of subintervals in the initial mesh (0=auto)
int fdim; // dimension of fspace
int idim; // dimension of ispace
printMode printLevel; // output control
meshMode meshSource; // mesh control
guessMode guessSource; // guess control
std::vector<int> ltol; // what component of z the tolerances are for
std::vector<double> tol; // the tolerances tol
std::vector<double> fixpnt; // the fixed points other than aleft and aright.
};
/* Define guess callback function type */
using guess_t = void (*)(double x, double const z[], double const y[], double dmval[]);
/* Define a system of equations */
struct system{
systemParameters params;
/* function for evaluating the derivative vector f(x,z,y). Parameters are:
x (input): a point in (left, right)
z (input): the state variable vector, z[mstar]
y (input): the constraint state vector, y[ny]
f (output): the derivative vector, f[ncomp+ny] */
virtual void fsub(double x, double const z[], double const y[], double f[]) const = 0;
/* function for evaluating the Jacobian of the derivative vector f(x,z,y). Parameters are:
x (input): a point in (left, right)
z (input): the state variable vector, z[mstar]
y (input): the constraint state vector, y[ny]
df (output): the matrix of partial derivatives, df[(ncomp+ny)*(mstar+ny)]:
df[i*(ncomp+ny)+j] = df[i]/dz[j] with 0 <= j < mstar
df[i*(ncomp+ny)+mstar+j] = df[i]/dy[j] with 0 <= j < ny */
virtual void dfsub(double x, double const z[], double const y[], double df[]) const = 0;
/* function for evaluating the constraint vector g(x,z). Parameters are:
i (input): the index of the boundary condition (0 <= i < systemParameters::bcpoints.size())
z (input): the state variable vector, z[mstar]
g (output): the value of g_i
Note that g is independent of y and, in case of a higher index dae (index = 2), should include the constraints
sampled at 'left' plus independent additional constraints, or an equivalent set.*/
virtual void gsub(int i, double const z[], double &g) const = 0;
/* function for evaluating the i-th row of the Jacobian of g(x,z). Parameters are:
i (input): the index of the boundary condition (0 <= i < systemParameters::bcpoints.size())
z (input): the state variable vector, z[mstar]
g (output): the vector of partial derivatives, dg[mstar]:
dg[j] = dg_i / dz_j with 0 <= j < mstar */
virtual void dgsub(int i, double const z[], double dg[]) const = 0;
};
/* Short notation for pointers.
Matrices are stored one-dimensional, column-wise.
Use restrict for 4% performance increase (g++). */
using dvec = double *const __restrict;
using ivec = int *const __restrict;
using dmat = double *const __restrict;
using imat = int *const __restrict;
using cdvec = double const *const __restrict;
using civec = int const *const __restrict;
using cdmat = double const *const __restrict;
using cimat = int const *const __restrict;
/* Class that holds the solver state. */
class solver {
// COLOUT
double PRECIS;
int IPRINT;
// COLLOC
double RHO[7];
double COEF[7 * 7];
// COLORD
int K;
int NCOMP;
int NY;
int NCY;
int MSTAR;
int KDY;
int MMAX;
int MT[20];
// COLAPR
int N, NOLD, NMAX, NZ, NDMZ;
// COLMSH
int MSHFLG, MSHNUM, MSHLMT, MSHALT;
// COLSID
double TZETA[40]; // boundary condition points
double TLEFT; // left domain end
double TRIGHT; // right domain end
int IZETA;
int IZSAVE;
// COLNLN
int NONLIN, ITER, LIMIT, ICARE, IGUESS, INDEX;
// COLEST
double TOL[40], WGTMSH[40], WGTERR[40], TOLIN[40], ROOT[40];
int JTOL[40], LTOL[40];
int NTOL; // number of tolerances
// COLBAS
double B[7 * 4];
double ACOL[28 * 7];
double ASAVE[28 * 4];
system const *sys;
public:
result_t COLDAE(systemParameters const ¶ms, options const &opts,
ivec ispace, dvec fspace,
system const &sys, guess_t guess) {
this->NCOMP = params.ncomp;
this->NY = params.ny;
this->TLEFT = params.left;
this->TRIGHT = params.right;
this->sys = &sys;
if (opts.ltol.size() != opts.tol.size()) {
return result_t::inputError;
}
std::copy(opts.ltol.begin(), opts.ltol.end(), this->LTOL);
std::copy(opts.tol.begin(), opts.tol.end(), this->TOL);
//*********************************************************************
//
// the actual subroutine coldae serves as an interface with
// the package of subroutines referred to collectively as
// coldae. the subroutine serves to test some of the input
// parameters, rename some of the parameters (to make understanding
// of the coding easier), to do some initialization,
// and to break the work areas fspace and ispace up into the
// arrays needed by the program.
//
//**********************************************************************
if (opts.printLevel != printMode::none)
fmt::print("VERSION *1* OF COLDAE\n");
// specify machine dependent constant precis = 100 * machine unit roundoff
PRECIS = std::numeric_limits<double>::epsilon();
PRECIS = PRECIS * 100.0;
// in case incorrect input data is detected, the program returns
// immediately with iflag=-3.
NCY = NCOMP + NY;
if (NCOMP < 0 || NCOMP > 20) {
fmt::print(fg(fmt::color::red), "Violated (0 <= NCOMP <= 20).\n");
return result_t::inputError;
}
if (NY < 0 || NY > 20) {
fmt::print(fg(fmt::color::red), "Violated (0 <= NY <= 20).\n");
return result_t::inputError;
}
if (NCY < 1 || NCY > 40) {
fmt::print(fg(fmt::color::red), "Violated (1 <= NCOMP + NY <= 40).\n");
return result_t::inputError;
}
for (int i = 0; i < NCOMP; ++i)
if (params.orders[i] < 1 || params.orders[i] > 4) {
fmt::print(fg(fmt::color::red),
"Violated (1 <= orders[{}] <= 4).\n", i);
return result_t::inputError;
}
// rename some of the parameters and set default values.
NONLIN = params.isNonLinear ? 1 : 0;
K = opts.numCollPoints;
N = opts.numSubIntervals;
if (N == 0)
N = 5;
int IREAD = static_cast<int>(opts.meshSource);
IGUESS = static_cast<int>(opts.guessSource);
if (NONLIN == 0 && IGUESS == 1) IGUESS = 0;
if (IGUESS >= 2 && IREAD == 0) IREAD = 1;
ICARE = static_cast<int>(params.regularity);
NTOL = static_cast<int>(opts.tol.size());
int NDIMF = opts.fdim;
int NDIMI = opts.idim;
int NFXPNT = opts.fixpnt.size();
IPRINT = static_cast<int>(opts.printLevel);
INDEX = static_cast<int>(params.index);
if (NY == 0) INDEX = 0;
MSTAR = 0;
MMAX = 0;
for (int i = 1; i <= NCOMP; ++i) {
MMAX = std::max(MMAX, params.orders[i - 1]);
MSTAR = MSTAR + params.orders[i - 1];
MT[i - 1] = params.orders[i - 1];
}
if (K == 0) K = std::max(MMAX + 1, 5 - MMAX);
for (int i = 1; i <= MSTAR; ++i)
TZETA[i - 1] = params.bcpoints[i - 1];
for (int i = 1; i <= NTOL; ++i) {
LTOL[i - 1] = LTOL[i - 1];
TOLIN[i - 1] = TOL[i - 1];
}
KDY = K * NCY;
// print the input data for checking.
if (IPRINT <= -1) {
if (NONLIN == 0) {
fmt::print("THE NUMBER OF (LINEAR) DIFF EQNS IS {}, THEIR ORDERS ARE {}\n",
NCOMP, params.orders);
} else {
fmt::print("THE NUMBER OF (NONLINEAR) DIFF EQNS IS {}, THEIR ORDERS ARE {}\n",
NCOMP, params.orders);
}
fmt::print("THERE ARE {} ALGEBRAIC CONSTRAINTS\n", NY);
if (NY > 0 && INDEX == 0) {
fmt::print("THE PROBLEM HAS MIXED INDEX CONSTRAINTS\n");
} else {
fmt::print("THE INDEX IS {}\n", INDEX);
}
fmt::print("SIDE CONDITION POINTS ZETA: {}\n", params.bcpoints);
if (NFXPNT > 0) {
fmt::print("THERE ARE {} FIXED POINTS IN THE MESH - {}\n",
NFXPNT, opts.fixpnt);
}
fmt::print("NUMBER OF COLLOC PTS PER INTERVAL IS {}\n", K);
fmt::print("COMPONENTS OF Z REQUIRING TOLERANCES: {}\n",
std::vector<int>(LTOL, LTOL + NTOL));
fmt::print("CORRESPONDING ERROR TOLERANCES: {}\n",
std::vector<double>(TOL, TOL + NTOL));
if (IGUESS >= 2) {
fmt::print("INITIAL MESH(ES) AND Z, DMZ PROVIDED BY USER\n");
}
if (IREAD == 2) {
fmt::print("NO ADAPTIVE MESH SELECTION\n");
}
}
// check for correctness of data
if (K < 0 || K > 7) {
fmt::print(fg(fmt::color::red), "Violated (0 <= K <= 7).\n");
return result_t::inputError;
}
if (N < 0) {
fmt::print(fg(fmt::color::red), "Violated (0 <= N).\n");
return result_t::inputError;
}
if (NTOL < 0 || NTOL > MSTAR) {
fmt::print(fg(fmt::color::red), "Violated (0 <= NTOL <= MSTAR).\n");
return result_t::inputError;
}
if (NFXPNT < 0) {
fmt::print(fg(fmt::color::red), "Violated (0 <= NFXPNT).\n");
return result_t::inputError;
}
if (MSTAR < 0 || MSTAR > 40) {
fmt::print(fg(fmt::color::red), "Violated (0 <= MSTAR <= 40).\n");
return result_t::inputError;
}
int IP = 1;
for (int i = 1; i <= MSTAR; ++i) {
if (std::abs(params.bcpoints[i - 1] - TLEFT) < PRECIS ||
std::abs(params.bcpoints[i - 1] - TRIGHT) < PRECIS)
continue;
while (true) {
if (IP > NFXPNT)
return result_t::inputError;
if (params.bcpoints[i - 1] - PRECIS < opts.fixpnt[IP])
break;
IP = IP + 1;
}
if (params.bcpoints[i - 1] + PRECIS < opts.fixpnt[IP])
return result_t::inputError;
}
// set limits on iterations and initialize counters.
// limit = maximum number of newton iterations per mesh.
// see subroutine newmsh for the roles of mshlmt , mshflg , mshnum , and mshalt .
MSHLMT = 3;
MSHFLG = 0;
MSHNUM = 1;
MSHALT = 1;
LIMIT = 40;
// compute the maxium possible n for the given sizes of ispace and fspace.
int NREC = 0;
for (int i = 1; i <= MSTAR; ++i) {
int IB = MSTAR + 1 - i;
if (params.bcpoints[IB - 1] >= TRIGHT)
NREC = i;
}
int NFIXI = MSTAR;
int NSIZEI = 3 + KDY + MSTAR;
int NFIXF = NREC * (2 * MSTAR) + 5 * MSTAR + 3;
int NSIZEF =
4 + 3 * MSTAR + (KDY + 5) * (KDY + MSTAR) + (2 * MSTAR - NREC) * 2 * MSTAR +
(MSTAR + NY + 2) * NCOMP +
KDY;
int NMAXF = (NDIMF - NFIXF) / NSIZEF;
int NMAXI = (NDIMI - NFIXI) / NSIZEI;
if (IPRINT < 1) {
fmt::print(
"THE MAXIMUM NUMBER OF SUBINTERVALS IS MIN({}(ALLOWED FROM FSPACE),{}(ALLOWED FROM ISPACE))\n",
NMAXF, NMAXI);
}
NMAX = std::min(NMAXF, NMAXI);
if (NMAX < N)
return result_t::inputError;
if (NMAX < NFXPNT + 1)
return result_t::inputError;
if (NMAX < 2 * NFXPNT + 2 && IPRINT < 1) {
fmt::print("INSUFFICIENT SPACE TO DOUBLE MESH FOR ERROR ESTIMATE\n");
}
// generate pointers to break up fspace and ispace .
int LXI = 1;
int LG = LXI + NMAX + 1;
int LXIOLD = LG + 2 * MSTAR * (NMAX * (2 * MSTAR - NREC) + NREC);
int LW = LXIOLD + NMAX + 1;
int LV = LW + (KDY * KDY) * NMAX;
int LFC = LV + MSTAR * KDY * NMAX;
int LZ = LFC + (MSTAR + NY + 2) * NCOMP * NMAX;
int LDMZ = LZ + MSTAR * (NMAX + 1);
int LDMV = LDMZ + KDY * NMAX;
int LDELZ = LDMV + KDY * NMAX;
int LDELDZ = LDELZ + MSTAR * (NMAX + 1);
int LDQZ = LDELDZ + KDY * NMAX;
int LDQDMZ = LDQZ + MSTAR * (NMAX + 1);
int LRHS = LDQDMZ + KDY * NMAX;
int LVALST = LRHS + KDY * NMAX + MSTAR;
int LSLOPE = LVALST + 4 * MSTAR * NMAX;
int LACCUM = LSLOPE + NMAX;
int LSCL = LACCUM + NMAX + 1;
int LDSCL = LSCL + MSTAR * (NMAX + 1);
int LPVTG = 1;
int LPVTW = LPVTG + MSTAR * (NMAX + 1);
int LINTEG = LPVTW + KDY * NMAX;
// if iguess .ge. 2, move xiold, z, and dmz to their proper
// locations in fspace.
if (IGUESS >= 2) {
NOLD = N;
if (IGUESS == 4)
NOLD = ispace[1 - 1];
NZ = MSTAR * (NOLD + 1);
NDMZ = KDY * NOLD;
int NP1 = N + 1;
if (IGUESS == 4)
NP1 = NP1 + NOLD + 1;
for (int i = 1; i <= NZ; ++i)
fspace[LZ + i - 1 - 1] = fspace[NP1 + i - 1];
int IDMZ = NP1 + NZ;
for (int i = 1; i <= NDMZ; ++i)
fspace[LDMZ + i - 1 - 1] = fspace[IDMZ + i - 1];
NP1 = NOLD + 1;
if (IGUESS == 4) {
for (int i = 1; i <= NP1; ++i)
fspace[LXIOLD + i - 1 - 1] = fspace[N + 1 + i - 1];
} else {
for (int i = 1; i <= NP1; ++i)
fspace[LXIOLD + i - 1 - 1] = fspace[LXI + i - 1 - 1];
}
}
// initialize collocation points, constants, mesh.
CONSTS();
int NYCB = (NY == 0 ? 1 : NY);
int meshmode = 3 + IREAD;
NEWMSH(meshmode, fspace + (LXI - 1), fspace + (LXIOLD - 1),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
NFXPNT, opts.fixpnt.data(), nullptr, nullptr, nullptr, NYCB);
// determine first approximation, if the problem is nonlinear.
if (IGUESS < 2) {
for (int i = 1; i <= N + 1; ++i)
fspace[i + LXIOLD - 1 - 1] = fspace[i + LXI - 1 - 1];
NOLD = N;
if (NONLIN != 0 && IGUESS != 1) {
// system provides first approximation of the solution.
// choose z(j) = 0 for j=1,...,mstar.
for (int i = 1; i <= NZ; ++i)
fspace[LZ - 1 + i - 1] = 0.0;
for (int i = 1; i <= NDMZ; ++i)
fspace[LDMZ - 1 + i - 1] = 0.0;
}
}
if (IGUESS >= 2)
IGUESS = 0;
result_t iflag;
CONTRL(fspace + (LXI - 1), fspace + (LXIOLD - 1), fspace + (LZ - 1), fspace + (LDMZ - 1),
fspace + (LDMV - 1),
fspace + (LRHS - 1), fspace + (LDELZ - 1), fspace + (LDELDZ - 1), fspace + (LDQZ - 1),
fspace + (LDQDMZ - 1), fspace + (LG - 1), fspace + (LW - 1), fspace + (LV - 1), fspace + (LFC - 1),
fspace + (LVALST - 1), fspace + (LSLOPE - 1), fspace + (LSCL - 1), fspace + (LDSCL - 1),
fspace + (LACCUM - 1), ispace + (LPVTG - 1), ispace + (LINTEG - 1), ispace + (LPVTW - 1),
NFXPNT, opts.fixpnt.data(), iflag, guess);
// prepare output
ispace[1 - 1] = N;
ispace[2 - 1] = K;
ispace[3 - 1] = NCOMP;
ispace[4 - 1] = NY;
ispace[5 - 1] = MSTAR;
ispace[6 - 1] = MMAX;
ispace[7 - 1] = NZ + NDMZ + N + 2;
int K2 = K * K;
ispace[8 - 1] = ispace[7 - 1] + K2 - 1;
for (int i = 1; i <= NCOMP; ++i)
ispace[8 + i - 1] = params.orders[i - 1];
for (int i = 1; i <= NZ; ++i)
fspace[N + 1 + i - 1] = fspace[LZ - 1 + i - 1];
int IDMZ = N + 1 + NZ;
for (int i = 1; i <= NDMZ; ++i)
fspace[IDMZ + i - 1] = fspace[LDMZ - 1 + i - 1];
int IC = IDMZ + NDMZ;
for (int i = 1; i <= K2; ++i)
fspace[IC + i - 1] = COEF[i - 1];
return iflag;
}
// * ****************************************************************
//
// purpose
//
// set up a standard call to approx to evaluate the
// approximate solution z = z(u(x)), y = y(x) at a
// point x(it has been computed by a call to coldae).
// the parameters needed for approx are retrieved
// from the work arrays ispace and fspace .
//
//*****************************************************************
void APPSLN(double &X, dvec Z, dvec Y, cdvec FSPACE, civec ISPACE) {
double A[28];
int IS6 = ISPACE[6];
int IS5 = ISPACE[0] + 2;
int IS4 = IS5 + ISPACE[4] * (ISPACE[0] + 1);
int i = 1;
APPROX(i, X, Z, Y, A, FSPACE + (IS6 - 1),
FSPACE, ISPACE[0],
FSPACE + (IS5 - 1), FSPACE + (IS4 - 1), ISPACE[1], ISPACE[2],
ISPACE[3], ISPACE[5], ISPACE + 8, ISPACE[4], 2, nullptr, 1);
}
private:
//**********************************************************************
//
// purpose
// this subroutine is the actual driver. the nonlinear iteration
// strategy is controlled here ( see [6] ). upon convergence, errchk
// is called to test for satisfaction of the requested tolerances.
//
// variables
//
// check - maximum tolerance value, used as part of criteria for
// checking for nonlinear iteration convergence
// relax - the relaxation factor for damped newton iteration
// relmin - minimum allowable value for relax (otherwise the
// jacobian is considered singular).
// rlxold - previous relax
// rstart - initial value for relax when problem is sensitive
// ifrz - number of fixed jacobian iterations
// lmtfrz - maximum value for ifrz before performing a reinversion
// iter - number of iterations (counted only when jacobian
// reinversions are performed).
// xi - current mesh
// xiold - previous mesh
// ipred = 0 if relax is determined by a correction
// = 1 if relax is determined by a prediction
// ifreez = 0 if the jacobian is to be updated
// = 1 if the jacobian is currently fixed (frozen)
// iconv = 0 if no previous convergence has been obtained
// = 1 if convergence on a previous mesh has been obtained
// icare =-1 no convergence occurred (used for regular problems)
// = 0 a regular problem
// = 1 no damped newton
// = 2 used for continuation (see description of ipar(10)
// in coldae).
// rnorm - norm of rhs (right-hand side) for current iteration
// rnold - norm of rhs for previous iteration
// anscl - scaled norm of newton correction
// anfix - scaled norm of newton correction at next step
// anorm - scaled norm of a correction obtained with jacobian fixed
// nz - number of components of z (see subroutine approx)
// ndmz - number of components of dmz (see subroutine approx)
// imesh - a control variable for subroutines newmsh and errchk
// = 1 the current mesh resulted from mesh selection
// or is the initial mesh.
// = 2 the current mesh resulted from doubling the
// previous mesh
//
//**********************************************************************
void CONTRL(dvec XI, dvec XIOLD, dvec Z, dvec DMZ, dvec DMV, dvec RHS, dvec DELZ, dvec DELDMZ,
dvec DQZ, dvec DQDMZ, dvec G, dvec W, dvec V, dvec FC, dvec VALSTR, dvec SLOPE, dvec SCALE,
dvec DSCALE,
dvec ACCUM, ivec IPVTG, ivec INTEGS, ivec IPVTW, const int NFXPNT, cdvec FIXPNT, result_t &iflag,
guess_t guess) {
double DF[800];
std::vector<double> FCSP(NCOMP * 60);
double CBSP[20 * 20];
double RNORM, RNOLD;
// constants for control of nonlinear iteration
double RELMIN = 1.e-3;
double RSTART = 1.e-2;
double LMTFRZ = 4;
// compute the maximum tolerance
double CHECK = 0.0;
for (int i = 1; i <= NTOL; ++i)
CHECK = std::max(TOLIN[i - 1], CHECK);
int IMESH = 1;
int ICONV = (NONLIN == 0);
int ICOR = 0;
int NOCONV = 0;
int MSING = 0;
int ISING = 0;
double RLXOLD;
int IPRED, IFREEZ;
int IFRZ;
double RELAX;
double ANORM = 0.0;
double ANFIX = 0.0;
// the main iteration begins here .
// loop 20 is executed until error tolerances are satisfied or
// the code fails (due to a singular matrix or storage limitations)
while (true) {
{
// initialization for a new mesh
ITER = 0;
if (NONLIN <= 0) {
// the linear case.
// set up and solve equations
LSYSLV(MSING, XI, XIOLD, nullptr, nullptr,
Z, DMZ, G,
W, V, FC, RHS,
nullptr, INTEGS, IPVTG, IPVTW,
RNORM, 0, guess, ISING);
// check for a singular matrix
if (ISING != 0) {
if (IPRINT < 1) {
fmt::print("SINGULAR PROJECTION MATRIX DUE TO INDEX > 2\n");
}
iflag = result_t::singular;
return;
}
if (MSING == 0)
goto n400;
n30:
if (MSING >= 0) {
if (IPRINT < 1) {
fmt::print(fg(fmt::color::red), "A LOCAL ELIMINATION MATRIX IS SINGULAR\n");
}
goto n460;
}
if (IPRINT < 1) {
fmt::print(fg(fmt::color::red), "THE GLOBAL BVP - MATRIX IS SINGULAR\n");
}
iflag = result_t::singular;
return;
}
// iteration loop for nonlinear case
// define the initial relaxation parameter (= relax)
RELAX = 1.0;
// check for previous convergence and problem sensitivity
if (ICARE == -1)
RELAX = RSTART;
if (ICARE == 1)
RELAX = 1.0;
if (ICONV == 0)
goto n160;
// convergence on a previous mesh has been obtained. thus
// we have a very good initial approximation for the newton
// process. proceed with one full newton and then iterate
// with a fixed jacobian.
IFREEZ = 0;
// evaluate right hand side and its norm and
// find the first newton correction
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DELZ, DELDMZ, G,
W, V, FC, RHS,
DQDMZ, INTEGS, IPVTG, IPVTW,
RNOLD, 1, guess, ISING);
if (IPRINT < 0) {
fmt::print("FIXED JACOBIAN ITERATIONS\n");
}
if (IPRINT < 0) {
fmt::print("ITERATION = {}, NORM(RHS) = {}\n", ITER, RNOLD);
}
goto n70;
}
n60:
{
// solve for the next iterate .
// the value of ifreez determines whether this is a full
// newton step (=0) or a fixed jacobian iteration (=1).
if (IPRINT < 0) {
fmt::print("ITERATION = {}, NORM(RHS) = {}\n", ITER, RNORM);
}
RNOLD = RNORM;
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DELZ, DELDMZ, G,
W, V, FC, RHS, nullptr,
INTEGS, IPVTG, IPVTW, RNORM,
3 + IFREEZ, guess, ISING);
}
n70:
{
// check for a singular matrix
if (MSING != 0)
goto n30;
if (ISING != 0) {
if (IPRINT < 1) {
fmt::print(fg(fmt::color::red), "SINGULAR PROJECTION MATRIX DUE TO INDEX > 2\n");
}
iflag = result_t::singular;
return;
}
if (IFREEZ != 1) {
// a full newton step
ITER = ITER + 1;
IFRZ = 0;
}
// update z and dmz , compute new rhs and its norm
for (int i = 1; i <= NZ; ++i)
Z[i - 1] += DELZ[i - 1];
for (int i = 1; i <= NDMZ; ++i)
DMZ[i - 1] += DELDMZ[i - 1];
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DELZ, DELDMZ, G,
W, V, FC, RHS, nullptr,
INTEGS, IPVTG, IPVTW, RNORM, 2,
guess, ISING);
// check monotonicity. if the norm of rhs gets smaller,
// proceed with a fixed jacobian; else proceed cautiously,
// as if convergence has not been obtained before (iconv=0).
if (RNORM < PRECIS)
goto n390;
if (RNORM > RNOLD)
goto n130;
if (IFREEZ == 1)
goto n110;
IFREEZ = 1;
goto n60;
}
n110:
{
// verify that the linear convergence with fixed jacobian
// is fast enough.
IFRZ = IFRZ + 1;
if (IFRZ >= LMTFRZ) IFREEZ = 0;
if (RNOLD < 4.0 * RNORM) IFREEZ = 0;
// check convergence (iconv = 1).
for (int IT = 1; IT <= NTOL; ++IT) {
int INZ = LTOL[IT - 1];
for (int IZ = INZ; IZ <= NZ; IZ += MSTAR) {
if (std::abs(DELZ[IZ - 1]) > TOLIN[IT - 1] * (std::abs(Z[IZ - 1]) + 1.0))
goto n60;
}
}
// convergence obtained
if (IPRINT < 1)
fmt::print("CONVERGENCE AFTER {} ITERATIONS\n", ITER);
goto n400;
}
n130:
{
// convergence of fixed jacobian iteration failed.
if (IPRINT < 0)
fmt::print("ITERATION = {}, NORM(RHS) = {}\nSWITCH TO DAMPED NEWTON ITERATION\n", ITER, RNORM);
ICONV = 0;
if (ICARE != 1)
RELAX = RSTART;
for (int i = 1; i <= NZ; ++i)
Z[i - 1] -= DELZ[i - 1];
for (int i = 1; i <= NDMZ; ++i)
DMZ[i - 1] -= DELDMZ[i - 1];
// update old mesh
for (int i = 1; i <= N + 1; ++i)
XIOLD[i - 1] = XI[i - 1];
NOLD = N;
ITER = 0;
}
n160:
{
// no previous convergence has been obtained. proceed
// with the damped newton method.
// evaluate rhs and find the first newton correction.
if (IPRINT < 0)
fmt::print("FULL DAMPED NEWTON ITERATION\n");
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DELZ, DELDMZ, G,
W, V, FC, RHS,
DQDMZ, INTEGS, IPVTG, IPVTW,
RNOLD, 1, guess, ISING);
// check for a singular matrix
if (MSING != 0)
goto n30;
if (ISING != 0) {
if (IPRINT < 1)
fmt::print(fg(fmt::color::red), "SINGULAR PROJECTION MATRIX DUE TO INDEX > 2\n");
iflag = result_t::singular;
return;
}
// bookkeeping for first mesh
if (IGUESS == 1)
IGUESS = 0;
// find initial scaling
SKALE(Z, DMZ, XI, SCALE, DSCALE);
RLXOLD = RELAX;
IPRED = 1;
goto n220;
}
n170:
{
// main iteration loop
RNOLD = RNORM;
if (ITER >= LIMIT)
goto n430;
// update scaling
SKALE(Z, DMZ, XI, SCALE, DSCALE);
// compute norm of newton correction with new scaling
double ANSCL = 0.0;
for (int i = 1; i <= NZ; ++i)
ANSCL = ANSCL + pow(DELZ[i - 1] * SCALE[i - 1], 2);
for (int i = 1; i <= NDMZ; ++i)
ANSCL = ANSCL + pow(DELDMZ[i - 1] * DSCALE[i - 1], 2);
ANSCL = sqrt(ANSCL / double(NZ + NDMZ));
// find a newton direction
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DELZ, DELDMZ, G,
W, V, FC, RHS, nullptr,
INTEGS, IPVTG, IPVTW, RNORM, 3,
guess, ISING);
// check for a singular matrix
if (MSING != 0)
goto n30;
if (ISING != 0) {
if (IPRINT < 1)
fmt::print(fg(fmt::color::red), "SINGULAR PROJECTION MATRIX DUE TO INDEX > 2\n");
iflag = result_t::singular;
return;
}
// predict relaxation factor for newton step.
if (ICARE != 1) {
double ANDIF = 0.0;
for (int i = 1; i <= NZ; ++i)
ANDIF = ANDIF + pow((DQZ[i - 1] - DELZ[i - 1]) * SCALE[i - 1], 2);
for (int i = 1; i <= NDMZ; ++i)
ANDIF = ANDIF + pow((DQDMZ[i - 1] - DELDMZ[i - 1]) * DSCALE[i - 1], 2);
ANDIF = sqrt(ANDIF / double(NZ + NDMZ) + PRECIS);
RELAX *= ANSCL / ANDIF;
if (RELAX > 1.0) RELAX = 1.0;
RLXOLD = RELAX;
IPRED = 1;
}
}
n220:
{
ITER++;
// determine a new z and dmz and find new rhs and its norm
for (int i = 1; i <= NZ; ++i)
Z[i - 1] += RELAX * DELZ[i - 1];
for (int i = 1; i <= NDMZ; ++i)
DMZ[i - 1] += RELAX * DELDMZ[i - 1];
}
n250:
{
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DQZ, DQDMZ, G,
W, V, FC, RHS, nullptr,
INTEGS, IPVTG, IPVTW, RNORM, 2,
guess, ISING);
// compute a fixed jacobian iterate (used to control relax)
LSYSLV(MSING, XI, XIOLD, Z, DMZ,
DQZ, DQDMZ, G,
W, V, FC, RHS, nullptr,
INTEGS, IPVTG, IPVTW, RNORM, 4,
guess, ISING);
// find scaled norms of various terms used to correct relax
ANORM = 0.0;
ANFIX = 0.0;
for (int i = 1; i <= NZ; ++i) {
ANORM += pow(DELZ[i - 1] * SCALE[i - 1], 2);
ANFIX += pow(DQZ[i - 1] * SCALE[i - 1], 2);
}
for (int i = 1; i <= NDMZ; ++i) {
ANORM += pow(DELDMZ[i - 1] * DSCALE[i - 1], 2);
ANFIX += pow(DQDMZ[i - 1] * DSCALE[i - 1], 2);
}
ANORM = sqrt(ANORM / double(NZ + NDMZ));
ANFIX = sqrt(ANFIX / double(NZ + NDMZ));
if (ICOR == 1) {
if (IPRINT < 0)
fmt::print("RELAXATION FACTOR CORRECTED TO RELAX = {}\n"
" NORM OF SCALED RHS CHANGES FROM {} TO {}\n"
" NORM OF RHS CHANGES FROM {} TO {}\n",
RELAX, ANORM, ANFIX, RNOLD, RNORM);
} else {
if (IPRINT < 0)
fmt::print("ITERATION = {} RELAXATION FACTOR = {}\n"
" NORM OF SCALED RHS CHANGES FROM {} TO {}\n"
" NORM OF RHS CHANGES FROM {} TO {}\n",
ITER, RELAX, ANORM, ANFIX, RNOLD, RNORM);
}
ICOR = 0;
// check for monotonic decrease in delz and deldmz.
if (ANFIX < PRECIS || RNORM < PRECIS)
goto n390;
{
if (ANFIX <= ANORM || ICARE == 1) {
// we have a decrease.
// if dqz and dqdmz small, check for convergence
if (ANFIX <= CHECK)
goto n350;
// correct the predicted relax unless the corrected
// value is within 10 percent of the predicted one.
if (IPRED != 1)
goto n170;
}
if (ITER >= LIMIT)
goto n430;
if (ICARE == 1)
goto n170;
}
{
// correct the relaxation factor.