• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trixi-framework / HOHQMesh / 30318764346

28 Jul 2026 12:55AM UTC coverage: 76.972% (+1.1%) from 75.909%
30318764346

Pull #166

github

web-flow
Merge 3346edfd3 into 955f92dca
Pull Request #166: Add L2/H1 boundary optimization

1631 of 1964 new or added lines in 40 files covered. (83.04%)

48 existing lines in 4 files now uncovered.

9563 of 12424 relevant lines covered (76.97%)

634449.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.08
/Source/BoundaryOptimization/ConstrainedMultiH1Optimizer.f90
1
! MIT License
2
!
3
! Copyright (c) 2010-present David A. Kopriva and other contributors: AUTHORS.md
4
!
5
! Permission is hereby granted, free of charge, to any person obtaining a copy
6
! of this software and associated documentation files (the "Software"), to deal
7
! in the Software without restriction, including without limitation the rights
8
! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
! copies of the Software, and to permit persons to whom the Software is
10
! furnished to do so, subject to the following conditions:
11
!
12
! The above copyright notice and this permission notice shall be included in all
13
! copies or substantial portions of the Software.
14
!
15
! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
! SOFTWARE.
22
!
23
! FTObjectLibrary contains code that, to the best of our knowledge, has been released as
24
! public domain software:
25
! * `b3hs_hash_key_jenkins`: originally by Rich Townsend,
26
! https://groups.google.com/forum/#!topic/comp.lang.fortran/RWoHZFt39ng, 2005
27
!
28
! --- End License
29
!
30
!////////////////////////////////////////////////////////////////////////
31
!
32
!      ConstrainedMultiH1Optimizer.f90
33
!      Created: September 29, 2025 at 9:28 AM
34
!      By: David Kopriva
35
!
36
!      Solves the least squares problem with constraints for multiple
37
!      subdivisions of a curve. Solves the system
38
!
39
!         S\cdot b = Rhs
40
!
41
!      where
42
!
43
!      b   = [b_1 b_2 ... b_K | \lambda_0 \lambda_1 ... \lambda_K]^T
44
!      Rhs = [h_1/2*r_1 h_2/2*r_2 ... h_K/2*r_K | x(t_0) 0 ... 0 x(t_{max})]^T
45
!      K = # subdivisions
46
!
47
!      S = | A   C |
48
!          | C^T 0 |
49
!
50
!      The main entries are:
51
!
52
!      1. Setup
53
!
54
!      SUBROUTINE ConstructCMH1O(self, N, M, nSegments, cuts, options)
55
!
56
!        ----------------------------------------------------------------
57
!        N                    = Approximation order
58
!        M                    = Quadrature order
59
!        nSegments            = Number of segments in the total domain
60
!        cuts(0:nSegments)    = segment points (TODO: Come up with better names)
61
!
62
!        options               =
63
!         % internalConstraint = smoothness order at internal interfaces.
64
!                                0 = continuity, 1 = 1st derivative, etc.
65
!         % endConstraint      = FIXED_CONSTRAINT .or. PERIODIC_CONSTRAINT
66
!         % whichnorm          = L2_NORM .OR. H1_NORM
67
!     ----------------------------------------------------------------
68
!
69
!
70
!      2. Solve
71
!
72
!      SUBROUTINE Optimize( self, curveFun, optimalCoefficients )
73
!         CLASS(MultiH1Optimizer)    :: self
74
!         REAL(KIND=RP), INTENT(out) :: optimalCoefficients(0:self % N,2,self % nSegments)
75
!         CLASS(SMCurve), POINTER    :: curveFun
76
!
77
!      To use the results, create a MultiSegmentModalCurve from the optimal coefficients.
78
!
79
!////////////////////////////////////////////////////////////////////////
80
!
81
   Module ConstrainedMultiH1Optimization
82
      USE ProgramGlobals
83
      USE LegendreAlgorithms
84
      USE SMCurveClass
85
      USE GaussQuadratureModule
86
      IMPLICIT NONE
87

88
      INTEGER, PARAMETER :: NO_CONSTRAINT       = 0
89
      INTEGER, PARAMETER :: FIXED_CONSTRAINT    = -1
90
      INTEGER, PARAMETER :: PERIODIC_CONSTRAINT = -2
91
      INTEGER, PARAMETER :: H1O_OK = 0, H1O_CONVERT = 1
92
!!
93
!!     --------------------------------
94
!!     Utility to use to compute errors
95
!!     --------------------------------
96
!!
97
!      TYPE GaussQuadratureType
98
!         INTEGER                    :: N
99
!         REAL(KIND=RP), ALLOCATABLE :: nodes(:), weights(:)
100
!      END TYPE GaussQuadratureType
101

102
      TYPE OptimizerOptions
103
         INTEGER       :: internalConstraint ! Default = 0
104
         INTEGER       :: endConstraint      ! Default = FIXED_CONSTRAINT
105
         INTEGER       :: whichNorm          ! DEFAULT = H1_NORM
106
         REAL(KIND=RP) :: toler              ! DEFAULT = 1.0d-4
107
         REAL(KIND=RP) :: safetyFactor       ! DEFAULT = 0.9d0
108
      END TYPE OptimizerOptions
109

110
      TYPE MultiH1Optimizer
111
         INTEGER                    :: N ! Approximation order
112
         INTEGER                    :: M ! Quadrature order
113
         INTEGER                    :: nSegments
114
         REAL(KIND=RP), ALLOCATABLE :: cuts(:)
115
         REAL(KIND=RP), ALLOCATABLE :: dsdt(:)
116
         REAL(KIND=RP), ALLOCATABLE :: breaks(:)
117
         REAL(KIND=RP), ALLOCATABLE :: breakIndices(:)
118
         INTEGER                    :: internalConstraint
119
         INTEGER                    :: endConstraint
120

121
         INTEGER                    :: totalStateVectors
122
         INTEGER                    :: nConstraints
123
         INTEGER                    :: globalStateLength
124

125
         TYPE(GaussQuadratureType)  :: GQuadrature
126
         REAL(KIND=RP), ALLOCATABLE :: L(:,:)  , LPrime(:,:)
127
         REAL(KIND=RP), ALLOCATABLE :: locMatrix(:,:)
128
         REAL(KIND=RP), ALLOCATABLE :: CTrans(:,:)
129
         REAL(KIND=RP), ALLOCATABLE :: SMatrix(:,:)
130
         REAL(KIND=RP), ALLOCATABLE :: rhs(:,:)
131
         INTEGER      , ALLOCATABLE :: iPvt(:)
132
         INTEGER                    :: whichNorm
133
!
134
!        ========
135
         CONTAINS
136
!        ========
137
!
138
         PROCEDURE :: construct => ConstructCMH1O
139
         FINAL     :: DestructCMH10
140
         PROCEDURE :: Optimize
141
      END TYPE MultiH1Optimizer
142

143
      PRIVATE :: InvAffineMap, affineMap
144
!
145
!     ========
146
      CONTAINS
147
!     ========
148
!
149
!!
150
!!////////////////////////////////////////////////////////////////////////
151
!!
152
!   SUBROUTINE ConstructGaussQuadrature(self, N)
153
!      IMPLICIT NONE
154
!      TYPE(GaussQuadratureType) :: self
155
!      INTEGER                   :: N
156
!
157
!      self % N = N
158
!      ALLOCATE(self % nodes(0:N), self % weights(0:N))
159
!      CALL GaussLegendreNodesAndWeights( N, self % nodes, self % weights )
160
!
161
!   END SUBROUTINE ConstructGaussQuadrature
162
!
163
!////////////////////////////////////////////////////////////////////////
164
!
165
   SUBROUTINE SetDefaultOptions(self)
5✔
166
      IMPLICIT NONE
167
      TYPE(OptimizerOptions) :: self
168

169
      self % internalConstraint = 0
5✔
170
      self % endConstraint      = FIXED_CONSTRAINT
5✔
171
      self % toler              = 1.0d-4
5✔
172
      self % whichNorm          = H1_NORM
5✔
173
      self % safetyFactor       = 0.90_RP
5✔
174

175
   END SUBROUTINE SetDefaultOptions
5✔
176
!
177
!////////////////////////////////////////////////////////////////////////
178
!
179
      SUBROUTINE ConstructCMH1O(self, N, M, nSegments, cuts, breakIndices, &
514✔
180
                                options, testModeOptn)
181
!
182
!     ----------------------------------------------------------------
183
!     N                    = Approximation order
184
!     M                    = Quadrature order
185
!     nSegments            = Number of segments in the total domain
186
!     cuts(0:nSegments)    = segment points (TODO: Come up with better names)
187
!     breakIndices         = marks the segments at which breaks are placed
188
!     options(OptimizerOptions):
189
!        options % internalConstraint = smoothness order at internal interfaces.
190
!                                       0 = continuity, 1 = 1st derivative, etc.
191
!        options % endConstraint      = FIXED_CONSTRAINT .or. PERIODIC_CONSTRAINT .OR. NO_CONSTRAINT
192
!        options % whichnorm          = L2_NORM .OR. H1_NORM
193
!     testMode             = (OPTIONAL) Turns on/off test mode
194
!     ----------------------------------------------------------------
195
!
196
      IMPLICIT NONE
197
!
198
!     ---------
199
!     Arguments
200
!     ---------
201
!
202
      CLASS(MultiH1Optimizer) :: self
203
      TYPE(OptimizerOptions)  :: options            ! parameters for the approximation
204
      INTEGER                 :: N
205
      INTEGER                 :: M
206
      INTEGER                 :: nSegments
207
      REAL(KIND=RP)           :: cuts(0:nSegments)
208
      INTEGER                 :: breakIndices(:)
209
      LOGICAL, OPTIONAL       :: testModeOptn
210
      LOGICAL                 :: testMode
211
!
212
!     ---------------
213
!     Local variables
214
!     ---------------
215
!
216
      INTEGER                    :: nConstraints, totalStateVectors
217
      INTEGER                    :: globalStateLength,  constraintCol, constraintRow
218
      INTEGER                    :: j, k
219
      INTEGER                    :: nc
220

221
      self % whichnorm          = options % whichNorm
514✔
222
      self % nSegments          = nSegments
514✔
223
      self % endConstraint      = options % endConstraint
514✔
224
      self % internalConstraint = options %internalConstraint
514✔
225

226
      ALLOCATE( self % cuts(0:nSegments), source = cuts )
1,580✔
227
      self % breakIndices = breakIndices
1,028✔
228

229
      self % N = N
514✔
230
      self % M = M
514✔
231

232
      totalStateVectors = (N+1)*nSegments
514✔
233

234
!     Total number of constraints
235
!     2 endpoints + subdivision*number of derivatives
236
!     This gives the number of rows in C^T
237

238
      IF ( self % endConstraint == NO_CONSTRAINT )     THEN
514✔
NEW
239
         nc = 0
×
240
      ELSE IF(self % endConstraint == PERIODIC_CONSTRAINT)     THEN
514✔
NEW
241
         nc = 1 + self % internalConstraint
×
242
      ELSE
243
         nc = 2
514✔
244
      END IF
245
      nConstraints      = nc + (nSegments-1)*(self % internalConstraint+1)
514✔
246

247
!     Sizes of the transpose of the constraint matrix. C^T b = d
248
      constraintCol     = totalStateVectors
514✔
249
      constraintRow     = nConstraints
514✔
250

251
!     Size of the global state vector and normal matrix, A , b + \lambda
252
      globalStateLength = totalStateVectors + nConstraints
514✔
253
!
254
      self % globalStateLength = globalStateLength
514✔
255
      self % totalStateVectors = totalStateVectors
514✔
256
      self % nConstraints      = nConstraints
514✔
257
!
258
!     ------------------------------
259
!     Allocate Local arrays/matrices
260
!     ------------------------------
261
!
262
      CALL ConstructGaussQuadrature(self % GQuadrature, M)
514✔
263
      ALLOCATE(self % L(0:M, 0:N), self % LPrime(0:M, 0:N))
514✔
264
      ALLOCATE(self % locMatrix(0:N,0:N))
514✔
265
!
266
!     -------------------------------
267
!     Allocate Global arrays/matrices
268
!     -------------------------------
269
!
270
      ALLOCATE(self % CTrans(constraintRow,constraintCol), source = 0.0_RP)
20,683✔
271
      ALLOCATE(self % SMatrix(globalStateLength,globalStateLength), source = 0.0_RP)
90,848✔
272
      ALLOCATE(self % rhs(globalStateLength,2), source = 0.0_RP)
11,252✔
273
      ALLOCATE(self % iPvt(globalStateLength), source = 0)
5,369✔
274
!
275
!     ------
276
!     Sizing
277
!     ------
278
!
279
      ALLOCATE(self % dsdt(nSegments))
514✔
280
      DO k = 1, nSegments
1,066✔
281
         self % dsdt(k) = 2.0_RP/(cuts(k) - cuts(k-1)) ! = 2/h
1,066✔
282
      END DO
283
!
284
!     ---------------------
285
!     Local Basis functions
286
!     ---------------------
287
!
288
      DO j = 0, M
6,910✔
289
         CALL LegendrePolysAndDerivatives(N, self % gQuadrature % nodes(j), self % L(j,:), self % LPrime(j,:))
6,910✔
290
      END DO
291
      CALL ConstructConstraintMatrix(self)
514✔
292
      CALL ConstructGlobalMatrix(self)
514✔
293
!
294
!     ------------------------------------------------------------
295
!     Do LU decomposition on the matrix, unless it's test mode,
296
!     in which case we don't want to destroy the matrix right away
297
!     ------------------------------------------------------------
298
!
299
      IF ( PRESENT(testModeOptn) )     THEN
514✔
NEW
300
         testMode = testModeOptn
×
301
      ELSE
302
         testMode = .FALSE.
514✔
303
      END IF
304
      IF ( .NOT.testMode )     THEN
514✔
305
         CALL LUFactorization(A = self % SMatrix, pivots = self % iPvt,n = self % globalStateLength)
514✔
306
      END IF
307

308
      END SUBROUTINE ConstructCMH1O
514✔
309
!
310
!////////////////////////////////////////////////////////////////////////
311
!
NEW
312
      SUBROUTINE Reconstruct(self, cuts)
×
313
!
314
!        --------------------------------------------------------------
315
!        For a previously constructed self, readjust matrices for a new
316
!        cuts distribution. Nothing else can change.
317
!        --------------------------------------------------------------
318
!
319
         IMPLICIT NONE
320
         CLASS(MultiH1Optimizer) :: self
321
         REAL(KIND=RP)           :: cuts(0:self % nSegments)
322
         INTEGER                 :: k
323
!
324
!        ------
325
!        Sizing
326
!        ------
327
!
NEW
328
         DO k = 1, self % nSegments
×
NEW
329
            self % dsdt(k) = 2.0_RP/(cuts(k) - cuts(k-1)) ! = 2/h
×
330
         END DO
331
!
332
!        --------------------
333
!        Reconstruct matrices
334
!        --------------------
335
!
NEW
336
         CALL ConstructConstraintMatrix(self)
×
NEW
337
         CALL ConstructGlobalMatrix(self)
×
338
!
NEW
339
         CALL LUFactorization(A = self % SMatrix, pivots = self % iPvt,n = self % globalStateLength)
×
340

NEW
341
      END SUBROUTINE Reconstruct
×
342
!
343
!////////////////////////////////////////////////////////////////////////
344
!
345
      SUBROUTINE DestructCMH10(self)
514✔
346
         IMPLICIT NONE
347
         TYPE(MultiH1Optimizer) :: self
348
         self % N = 0
514✔
349
         self % M = 0
514✔
350

351
         IF(ALLOCATED(self % SMatrix))      DEALLOCATE(self % SMatrix)
514✔
352
         IF(ALLOCATED(self % gQuadrature % nodes))        DEALLOCATE(self % gQuadrature % nodes)
514✔
353
         IF(ALLOCATED(self % gQuadrature % weights))      DEALLOCATE(self % gQuadrature % weights)
514✔
354
         IF(ALLOCATED(self % cuts))         DEALLOCATE(self % cuts)
514✔
355
         IF(ALLOCATED(self % rhs))          DEALLOCATE(self % rhs)
514✔
356
         IF(ALLOCATED(self % L))            DEALLOCATE(self % L)
514✔
357
         IF(ALLOCATED(self % LPrime))       DEALLOCATE(self % LPrime)
514✔
358
         IF(ALLOCATED(self % locMatrix))    DEALLOCATE(self % locMatrix)
514✔
359
         IF(ALLOCATED(self % CTrans))       DEALLOCATE(self % CTrans)
514✔
360
         IF(ALLOCATED(self % iPvt))         DEALLOCATE(self % iPvt)
514✔
361

362
      END SUBROUTINE DestructCMH10
514✔
363
!
364
!////////////////////////////////////////////////////////////////////////
365
!
366
      SUBROUTINE ConstructConstraintMatrix(self)
514✔
367
         IMPLICIT NONE
368
!
369
!        ---------
370
!        Arguments
371
!        ---------
372
!
373
         CLASS(MultiH1Optimizer)    :: self
374
!
375
!        ---------------
376
!        Local Variables
377
!        ---------------
378
!
379
         INTEGER                    :: r, c, j, N, cl, k
380
         REAL(KIND=RP), ALLOCATABLE :: CAPlus(:,:)
514✔
381
         REAL(KIND=RP), ALLOCATABLE :: CAMinus(:,:)
514✔
382

383
         N = self % N
514✔
384

385
         ALLOCATE(CAPlus(0:N,0:self % internalConstraint),CAMinus(0:N,0:self % internalConstraint))
514✔
386
         CALL ConstructContinuityArray(CAPlus ,N,self % internalConstraint, 1.0_RP)
514✔
387
         CALL ConstructContinuityArray(CAMinus,N,self % internalConstraint,-1.0_RP)
514✔
388
!
389
!        ----------------------------
390
!        Set up the constraint matrix
391
!        ----------------------------
392
!
393
         IF ( self % endConstraint == FIXED_CONSTRAINT )     THEN
514✔
394
!
395
!           ------------------
396
!           First and last row
397
!           ------------------
398
!
399
            r = 1; c = 1
514✔
400
            self % CTrans(r,c:c+N) = CAMinus(:,0)
3,969✔
401
            r = self % nConstraints ; c = 1 + (self % nSegments-1)*(N+1)
514✔
402
            self % CTrans(r,c:c+N) = CAPlus(:,0)
3,969✔
403
            r = 2 ! Reset to start the following rows
514✔
404

NEW
405
         ELSE IF(self % endConstraint == PERIODIC_CONSTRAINT)     THEN
×
406
!
407
!           ---------------------------------------------
408
!           nConstraint+1 rows for smoothness constraints
409
!           ---------------------------------------------
410
!
NEW
411
            r = 1; c =1 + (self % nSegments-1)*(N+1)
×
NEW
412
            DO j = 0, self % internalConstraint
×
NEW
413
               self % CTrans(r,1:1+N) =  CAMinus(:,j)*self % dsdt(1)**j
×
NEW
414
               self % CTrans(r,c:c+N) = -CAPlus(:,j) *self % dsdt(self % nSegments)**j
×
NEW
415
               r = r + 1
×
416
            END DO
417

418
         END IF
419

420
         cl = 1; c = cl + N+1 ! & Pick up r above where boundary constraints left off
514✔
421
         DO k = 2, self % nSegments
552✔
422
            DO j = 0, self % internalConstraint
148✔
423
               self % CTrans(r,c:c+N)   =  CAMinus(:,j)*self % dsdt(k)**j
876✔
424
               self % CTrans(r,cl:cl+N) = -CAPlus(:,j) *self % dsdt(k-1)**j
876✔
425
               r = r + 1
148✔
426
            END DO
427
            c = c + N+1; cl = cl + N+1
552✔
428
         END DO
429

430
      END SUBROUTINE ConstructConstraintMatrix
514✔
431
!
432
!////////////////////////////////////////////////////////////////////////
433
!
434
      SUBROUTINE ConstructGlobalMatrix(self)
514✔
435
         IMPLICIT NONE
436
!
437
!        ---------
438
!        Arguments
439
!        ---------
440
!
441
         CLASS(MultiH1Optimizer)    :: self
442
!
443
!        ---------------
444
!        Local Variables
445
!        ---------------
446
!
447
         INTEGER       :: r, c, i, j, k
448
         INTEGER       :: N, nSegments, constraintRow, constraintCol
449
         REAL(KIND=RP) :: scal
450
         REAL(KIND=RP) :: h(1:self % nSegments)
1,028✔
451

452
         constraintCol = self % totalStateVectors
514✔
453
         constraintRow = self % nConstraints
514✔
454
         N             = self % N
514✔
455
         nSegments     = self % nSegments
514✔
456

457
         DO k = 1, nSegments
1,066✔
458
            h(k) = self % cuts(k) - self % cuts(k-1)
1,066✔
459
         END DO
460
!
461
!        -----------------------------
462
!        Construct the global matrix
463
!        -----------------------------
464
!
465
         DO i = 1, nSegments ! Upper left diagonal
1,066✔
466
            scal = (2.0_RP/h(i))**2
552✔
467
            CALL ConstructLocalNormalMatrix(self, scal)
552✔
468
            j = 1 + (i-1)*(N+1)
552✔
469
            DO r = 0, N
4,783✔
470
               DO c = 0, N
29,694✔
471
                  self % SMatrix(j + r,j + c) = 0.5_RP*h(i)*self % locMatrix(r,c)
29,142✔
472
               END DO
473
            END DO
474
         END DO
475
!
476
!        -----------------------
477
!        Upper right, constraint
478
!        -----------------------
479
!
480
         r = 0 ; c = nSegments*(N+1)
514✔
481
         DO i = 1, constraintRow
1,652✔
482
            DO j = 1, constraintCol
18,104✔
483
               self % SMatrix(r+j,c+i) = self % CTrans(i,j)
17,590✔
484
            END DO
485
         END DO
486
!
487
!        ---------------------
488
!        Lower left constraint
489
!        ---------------------
490
!
491
         r = nSegments*(N+1); c = 0
514✔
492
         DO i = 1, constraintRow
1,652✔
493
            DO j = 1, constraintCol
18,104✔
494
               self % SMatrix(r+i,c+j) = self % CTrans(i,j)
17,590✔
495
            END DO
496
         END DO
497

498
      END SUBROUTINE ConstructGlobalMatrix
514✔
499
!
500
!////////////////////////////////////////////////////////////////////////
501
!
502
      SUBROUTINE Optimize( self, curveFun, optimalCoefficients )
514✔
503
         IMPLICIT NONE
504
!
505
!        ---------
506
!        Arguments
507
!        ---------
508
!
509
         CLASS(MultiH1Optimizer)    :: self
510
         REAL(KIND=RP), INTENT(out) :: optimalCoefficients(0:self % N,2,self % nSegments)
511
         CLASS(SMCurve), POINTER    :: curveFun
512
!
513
!        ---------------
514
!        Local variables
515
!        ---------------
516
!
517
         INTEGER :: j, k, i
518
!
519
!        ----------------------------------------------
520
!        Compute the RHS for the desired curve function
521
!        ----------------------------------------------
522
!
523
         CALL ConstructRHS(self,curveFun)
514✔
524
!
525
!        ---------------------------------
526
!        Solve the global system S b = rhs
527
!        ---------------------------------
528
!
529
         DO i = 1, 2
1,542✔
530
            CALL ForwardBackwardSolve(A = self % SMatrix, b = self % rhs(:,i), &
531
                                      pivots = self % iPvt,n = self % globalStateLength)
1,028✔
532
!
533
!           ----------------------------------------------------------
534
!           Re-package the global coefficient vector into the the form
535
!           coefs(#modes,[x,y],#segments)
536
!           ----------------------------------------------------------
537
!
538
            DO k = 1, self % nSegments
2,646✔
539
               j                           = 1 + (k-1)*(self % N + 1)
1,104✔
540
               optimalCoefficients(0:,i,k) = self % rhs(j:j+self % N,i)
9,566✔
541
            END DO
542
         END DO
543
!
544
      END SUBROUTINE Optimize
514✔
545
!
546
!////////////////////////////////////////////////////////////////////////
547
!
548
   SUBROUTINE ConstructLocalNormalMatrix( self, scal )
552✔
549
      IMPLICIT NONE
550
!
551
!     ---------
552
!     Arguments
553
!     ---------
554
!
555
      TYPE(MultiH1Optimizer) :: self
556
      REAL(KIND=RP)          :: scal ! = (ds/dt)^2
557

558
      INTEGER :: i, j
559

560
      DO j = 0, self % N
4,269✔
561
         DO i = 0, self % N
29,694✔
562
            self % locMatrix(i,j) = gaussInnerProduct(self % L(:,i), self % L(:,j), &
563
                                                      self % gQuadrature % weights, self % M)
29,142✔
564
         END DO
565
      END DO
566
      IF ( self % whichNorm == H1_NORM )     THEN
552✔
567
         DO j = 0, self % N
4,269✔
568
            DO i = 0, self % N
29,694✔
569
               self % locMatrix(i,j) = self % locMatrix(i,j) + &
570
                                       scal *gaussInnerProduct(self % LPrime(:,i),self % LPrime(:,j),&
571
                                                               self % gQuadrature % weights, self % M)
29,142✔
572
            END DO
573
         END DO
574
      END IF
575

576
   END SUBROUTINE ConstructLocalNormalMatrix
552✔
577
!
578
!////////////////////////////////////////////////////////////////////////
579
!
580
   SUBROUTINE ConstructRHS( self, curveFun )
514✔
581
      IMPLICIT NONE
582
!
583
!     ---------
584
!     Arguments
585
!     ---------
586
!
587
      TYPE(MultiH1Optimizer)  :: self
588
      CLASS(SMCurve)          :: curveFun
589
!
590
!     ---------------
591
!     Local variables
592
!     ---------------
593
!
594
      REAL(KIND=RP)  :: f(0:self % M,2), fp(0:self % M,2)
514✔
595
      REAL(KIND=RP)  :: v(3), z
596
      REAL(KIND=RP)  :: dsdt, hOver2
597
      INTEGER        :: j, k, i, r
598
!
599
       self % rhs = 0.0_RP
11,252✔
600
!
601
!      ----------------
602
!      Curve projection
603
!      ----------------
604
!
605
       DO k = 1, self % nSegments
1,066✔
606
            i      = 1 + (k-1)*(self % N + 1)
552✔
607
            hOver2 = 0.5_RP*(self % cuts(k) - self % cuts(k-1))
552✔
608
            dsdt   = self % dsdt(k) !1.0_RP/hOver2
552✔
609

610
            DO j = 0, self % M
7,434✔
611
               z         = affineMap(t0 = self % cuts(k-1), &
612
                                     t1 = self % cuts(k),   &
613
                                     s  = self % gQuadrature % nodes(j))
6,882✔
614
               v         = curveFun % positionAt(z)
27,528✔
615
               f(j,1:2)  = v(1:2)
20,646✔
616

617
               v         = curveFun % derivativeAt(z)
27,528✔
618
               fp(j,1:2) = v(1:2)
21,198✔
619
            END DO
620

621
            DO j = 0, self % N
4,269✔
622
               self % rhs(i+j,1) =  hOver2*gaussInnerProduct(f(:,1) , self % L(:,j),self % gQuadrature % weights, self % M)
3,717✔
623
               self % rhs(i+j,2) =  hOver2*gaussInnerProduct(f(:,2) , self % L(:,j),self % gQuadrature % weights, self % M)
4,269✔
624
            END DO
625

626
            IF ( self % whichNorm == H1_NORM )     THEN ! note: dsdt*hOver2 = 1. Leaving for clarity.
1,066✔
627
               DO j = 0, self % N
4,269✔
628
                  self % rhs(i+j,1) =  self % rhs(i+j,1) + &
629
                                       dsdt*hOver2*gaussInnerProduct(fp(:,1), self % LPrime(:,j), &
630
                                       self % gQuadrature % weights, self % M)
3,717✔
631
                  self % rhs(i+j,2) =  self % rhs(i+j,2) + &
632
                                       dsdt*hOver2*gaussInnerProduct(fp(:,2), self % LPrime(:,j), &
633
                                       self % gQuadrature % weights, self % M)
4,269✔
634
               END DO
635

636
            END IF
637
       END DO
638
       IF( self % endConstraint == PERIODIC_CONSTRAINT) RETURN
514✔
639
!
640
!      ------------------------------------------------
641
!      Constraint section if the endpoints are included
642
!      ------------------------------------------------
643
!
644
       r = 1 + self % nSegments*(self % N + 1)
514✔
645
       v = curveFun % positionAt( self % cuts(0))
2,056✔
646
       self % rhs(r,:) = v(1:2)
1,542✔
647

648
       r = UBOUND(self % rhs,1)
514✔
649
       v = curveFun % positionAt(self % cuts(self % nSegments))
2,056✔
650
       self % rhs(r,:) = v(1:2)
1,542✔
651

652
   END SUBROUTINE ConstructRHS
653
!
654
!////////////////////////////////////////////////////////////////////////
655
!
656
   SUBROUTINE ConstructContinuityArray(C, N, p, one)
1,029✔
657
      IMPLICIT NONE
658
!
659
!     ---------
660
!     Arguments
661
!     ---------
662
!
663
      INTEGER,       INTENT(IN)       :: N          !     = highest polynomial order
664
      INTEGER,       INTENT(IN)       :: p          ! < N = highest derivative to match
665
      REAL(KIND=RP)                   :: C(0:N,0:p) !     = L_k^m
666
      REAL(KIND=RP), INTENT(IN)       :: one        !     = \pm 1
667
!
668
!     ---------------
669
!     Local variables
670
!     ---------------
671
!
672
      INTEGER :: k, m
673
      C = 0.0_RP
23,907✔
674
      DO k = 0, N
7,945✔
675
         C(k,0) = one**k
7,945✔
676
      END DO
677
      IF ( p == 0 )  RETURN
1,029✔
678

679
      DO k = 0, N
7,463✔
680
         C(k,1) = 0.5_RP*one**(k+1)*k*(k+1)
7,463✔
681
      END DO
682
      IF(p == 1)     RETURN
933✔
683

684
      DO m = 2, p
1,867✔
685
         DO k = m, N
5,601✔
686
            C(k,m) = (2*k-1)*C(k-1,m-1) - C(k-2,m)
5,601✔
687
         END DO
688
      END DO
689

690
   END SUBROUTINE ConstructContinuityArray
691
!
692
!////////////////////////////////////////////////////////////////////////
693
!
694
      REAL(KIND=RP) FUNCTION affineMap(t0,t1,s)
6,882✔
695
         IMPLICIT NONE
696
         REAL(KIND=RP) :: t0, t1, s !\in [-1,1]
697

698
         affineMap = t0 + (t1 - t0)*0.5_RP*(1.0_RP + s)
6,882✔
699

700
      END FUNCTION affineMap
6,882✔
701
!
702
!////////////////////////////////////////////////////////////////////////
703
!
NEW
704
      REAL(KIND=RP) FUNCTION InvAffineMap(t0,t1,t)
×
705
         IMPLICIT NONE
706
         REAL(KIND=RP) :: t0, t1, t !\in [t0,t1]
707

NEW
708
         InvAffineMap = 2.0_RP*(t - t0)/(t1 - t0) - 1.0_RP
×
709

NEW
710
      END FUNCTION InvAffineMap
×
711
!
712
!////////////////////////////////////////////////////////////////////////
713
!
714
   SUBROUTINE LUFactorization(A, pivots, n)
514✔
715
! Computes the LU factorization of the matrix A with partial (row) pivoting.
716
! The factorization is done in-place overwriting the input array A.
717
! The vector pivots stores the row permutations of the partial pivoting
718
! where pivots(k) is the index of the kth pivot row.
719
!
720
! Note, this routine assumes that A is nonsingular as no checks are done.
721
       IMPLICIT NONE
722
       INTEGER, INTENT(IN)                          :: n
723
       REAL(KIND=RP), INTENT(INOUT), DIMENSION(n,n) :: A
724
       INTEGER, INTENT(OUT), DIMENSION(n)           :: pivots
725

726
       ! Local variables
727
       INTEGER :: j, k, pivot_idx
728
       REAL(KIND=RP), DIMENSION(n) :: pivot_row
514✔
729

730
       ! Fill pivots vector with the default order of the matrix
731
       pivots = [(j, j=1,n)]
10,224✔
732

733
       ! Gaussian elimination with row pivoting done in-place
734
       DO j = 1,n-1
4,855✔
735
           ! Find the index of the largest pivot element in the current column
736
           k = MAXLOC(ABS(A(j:n,j)), 1)
48,994✔
737

738
           ! Adjust to be the "global" pivot index of the matrix
739
           k = j - 1 + k
4,341✔
740

741
           ! Swap the rows of the in-place array
742
           pivot_row = A(k,:)
84,965✔
743
           A(k,:) = A(j,:)
84,965✔
744
           A(j,:) = pivot_row
84,965✔
745

746
           ! Swap the "rows" in the pivot vector
747
           pivot_idx = pivots(k)
4,341✔
748
           pivots(k) = pivots(j)
4,341✔
749
           pivots(j) = pivot_idx
4,341✔
750

751
           ! Scale the "active" matrix with the pivot element
752
           A(j+1:n,j) = A(j+1:n,j) / A(j,j)
44,653✔
753

754
           ! Rank-1 update for elimination in the "active" matrix.
755
           ! This requires an outer product
756
           !       A(j+1:n,j) * A(j,j+1:n)
757
           ! which we achieve using SPREAD
758
           A(j+1:n,j+1:n) = A(j+1:n,j+1:n) - &
759
                               SPREAD(A(j+1:n,j), dim=2, ncopies=n-j) * &
760
                               SPREAD(A(j,j+1:n), dim=1, ncopies=n-j)
2,014,105✔
761
       END DO ! j
762

763
       RETURN
514✔
764
   END SUBROUTINE LUFactorization
765
!
766
!////////////////////////////////////////////////////////////////////////
767
!
768
   SUBROUTINE ForwardBackwardSolve(A, b, pivots, n)
1,028✔
769
! Solve the linear system A x = b where A contains the in-place LU
770
! factorization from LUFactorization. The right hand side vector b
771
! is over-written to contain the solution vector x. The vector pivots
772
! contains the row pivoting information from LUFactorization.
773
       IMPLICIT NONE
774
       INTEGER, INTENT(IN)                         :: n
775
       REAL(KIND=RP), INTENT(IN), DIMENSION(n,n)   :: A
776
       REAL(KIND=RP), INTENT(INOUT), DIMENSION(n)  :: b
777
       INTEGER, INTENT(IN), DIMENSION(n)           :: pivots
778

779
       ! Local variables
780
       INTEGER :: j
781

782
       ! Pivot the right hand side vector
783
       b = b(pivots)
20,448✔
784

785
       ! Forward elimination. Note, exploits that the diagonal entries
786
       ! of the lower triangular matrix from the in-place LU are all one
787
       ! such that no divisions are needed.
788
       DO j = 2,n
9,710✔
789
           b(j) = b(j) - DOT_PRODUCT(A(j,1:j-1), b(1:j-1))
90,334✔
790
       END DO ! j
791

792
       ! Backward substitution
793
       b(n) = b(n) / A(n,n)
1,028✔
794
       DO j = n-1,1,-1
9,710✔
795
           b(j) = (b(j) - DOT_PRODUCT(A(j,j+1:n), b(j+1:n))) / A(j,j)
90,334✔
796
       END DO ! j
797

798
       RETURN
1,028✔
799
   END SUBROUTINE ForwardBackwardSolve
800
!
801
!////////////////////////////////////////////////////////////////////////
802
!
803
   FUNCTION spotCheckSmoothnessConditionsIsOK()  RESULT(r)
1✔
804
      IMPLICIT NONE
805
      LOGICAL             :: r
806
      INTEGER, PARAMETER  :: N = 5, p = 3
807
      REAL(KIND=RP)       :: C(0:5,0:3)
808
      REAL(KIND=RP)       :: L2Prime = 3.0_RP
809
      REAL(KIND=RP)       :: L3Prime = 6.0_RP, L3DoublePrime = 15.0_RP
810

811
      CALL ConstructContinuityArray(C,N,p,1.0_RP)
1✔
812

813
      r = .FALSE.
1✔
814

815
      r = (C(2,1) - L2Prime) == 0.0_RP .AND. (C(3,2)-L3DoublePrime) == 0
1✔
816
      r = r .AND. (C(3,1) - L3Prime)       == 0.0_RP
1✔
817
      r = r .AND. (C(3,3) - L3DoublePrime) == 0.0_RP
1✔
818

819
   END FUNCTION spotCheckSmoothnessConditionsIsOK
1✔
820

821
   END Module ConstrainedMultiH1Optimization
1,028✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc