Skip to content

Commit 364bee9

Browse files
committed
Add integral, cross_section, derivative_spline to surface types
Wrap three orphaned fitpack_core routines into the OOP surface API: - integral(lower, upper): double integration over a 2D bounding box (dblint) - cross_section(u, along_y): extract a 1D fitpack_curve from a 2D spline (profil) - derivative_spline(nux, nuy): partial derivative spline coefficients (pardtc) All three methods are available on both fitpack_surface and fitpack_grid_surface. Add meshgrid helper and 3 new tests.
1 parent 851238a commit 364bee9

File tree

4 files changed

+549
-12
lines changed

4 files changed

+549
-12
lines changed

src/fitpack_grid_surfaces.f90

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ module fitpack_grid_surfaces
2121
use fitpack_core, only: FITPACK_SUCCESS,FP_REAL,FP_SIZE,FP_FLAG,FP_COMM,zero,IOPT_NEW_SMOOTHING,IOPT_OLD_FIT, &
2222
IOPT_NEW_LEASTSQUARES,bispev,fitpack_error_handling,get_smoothing,regrid, &
2323
parder,pardeu,FITPACK_INPUT_ERROR, &
24+
dblint,profil,pardtc, &
2425
FP_COMM_SIZE,FP_COMM_PACK,FP_COMM_EXPAND
2526
use fitpack_fitters
27+
use fitpack_curves, only: fitpack_curve
2628
implicit none
2729
private
2830

@@ -80,6 +82,15 @@ module fitpack_grid_surfaces
8082
generic :: dfdx => gridded_derivatives_one,gridded_derivatives_many
8183
generic :: dfdx_ongrid => gridded_derivatives_gridded
8284

85+
!> Double integration over a rectangular domain
86+
procedure :: integral => gridsurf_integral
87+
88+
!> Extract a 1D cross-section curve from the surface
89+
procedure :: cross_section => gridsurf_cross_section
90+
91+
!> Compute the derivative spline coefficients
92+
procedure :: derivative_spline => gridsurf_derivative_spline
93+
8394
!> Parallel communication
8495
procedure :: comm_size => gridsurf_comm_size
8596
procedure :: comm_pack => gridsurf_comm_pack
@@ -464,6 +475,126 @@ real(FP_REAL) function gridded_derivatives_one(this,x,y,dx,dy,ierr) result(f)
464475

465476
end function gridded_derivatives_one
466477

478+
!> Double integration of the surface over a rectangular domain [lower(1),upper(1)] x [lower(2),upper(2)]
479+
real(FP_REAL) function gridsurf_integral(this, lower, upper)
480+
class(fitpack_grid_surface), intent(in) :: this
481+
real(FP_REAL), intent(in) :: lower(2), upper(2)
482+
483+
real(FP_REAL) :: wrk(this%knots(1)+this%knots(2)-this%order(1)-this%order(2)-2)
484+
485+
gridsurf_integral = dblint(this%t(:,1), this%knots(1), &
486+
this%t(:,2), this%knots(2), &
487+
this%c, &
488+
this%order(1), this%order(2), &
489+
lower(1), upper(1), lower(2), upper(2), wrk)
490+
491+
end function gridsurf_integral
492+
493+
!> Extract a 1D cross-section from the surface.
494+
!> If along_y=.true., returns f(y) = s(u,y); if along_y=.false., returns g(x) = s(x,u).
495+
function gridsurf_cross_section(this, u, along_y, ierr) result(curve)
496+
class(fitpack_grid_surface), intent(in) :: this
497+
real(FP_REAL), intent(in) :: u
498+
logical, intent(in) :: along_y
499+
integer(FP_FLAG), optional, intent(out) :: ierr
500+
type(fitpack_curve) :: curve
501+
502+
integer(FP_FLAG) :: ier
503+
integer(FP_SIZE) :: iopt, nu, nc
504+
real(FP_REAL), allocatable :: cu(:)
505+
506+
if (along_y) then
507+
iopt = 0
508+
nu = this%knots(2)
509+
nc = this%knots(2) - this%order(2) - 1
510+
else
511+
iopt = 1
512+
nu = this%knots(1)
513+
nc = this%knots(1) - this%order(1) - 1
514+
end if
515+
516+
allocate(cu(nu))
517+
518+
call profil(iopt, this%t(:,1), this%knots(1), &
519+
this%t(:,2), this%knots(2), &
520+
this%c, &
521+
this%order(1), this%order(2), &
522+
u, nu, cu, ier)
523+
524+
if (FITPACK_SUCCESS(ier)) then
525+
curve%order = merge(this%order(2), this%order(1), along_y)
526+
curve%knots = nu
527+
curve%nest = nu
528+
if (along_y) then
529+
allocate(curve%t(nu), source=this%t(1:nu, 2))
530+
else
531+
allocate(curve%t(nu), source=this%t(1:nu, 1))
532+
end if
533+
allocate(curve%c(nu), source=zero)
534+
curve%c(:nc) = cu(:nc)
535+
curve%xleft = curve%t(curve%order + 1)
536+
curve%xright = curve%t(curve%knots - curve%order)
537+
end if
538+
539+
call fitpack_error_handling(ier, ierr, 'grid surface cross-section')
540+
541+
end function gridsurf_cross_section
542+
543+
!> Compute the derivative spline d^(nux+nuy)s / dx^nux dy^nuy.
544+
!> Returns a new grid surface with reduced order and trimmed knots.
545+
function gridsurf_derivative_spline(this, nux, nuy, ierr) result(dsurf)
546+
class(fitpack_grid_surface), intent(in) :: this
547+
integer(FP_SIZE), intent(in) :: nux, nuy
548+
integer(FP_FLAG), optional, intent(out) :: ierr
549+
type(fitpack_grid_surface) :: dsurf
550+
551+
integer(FP_FLAG) :: ier
552+
integer(FP_SIZE) :: nx, ny, newkx, newky, newnx, newny, nc_old, nc_new
553+
real(FP_REAL), allocatable :: newc(:)
554+
555+
nx = this%knots(1)
556+
ny = this%knots(2)
557+
nc_old = (nx - this%order(1) - 1) * (ny - this%order(2) - 1)
558+
559+
allocate(newc(nc_old))
560+
561+
call pardtc(this%t(:,1), nx, this%t(:,2), ny, &
562+
this%c, this%order(1), this%order(2), &
563+
nux, nuy, newc, ier)
564+
565+
if (FITPACK_SUCCESS(ier)) then
566+
newkx = this%order(1) - nux
567+
newky = this%order(2) - nuy
568+
newnx = nx - 2*nux
569+
newny = ny - 2*nuy
570+
nc_new = (newnx - newkx - 1) * (newny - newky - 1)
571+
572+
dsurf%order = [newkx, newky]
573+
dsurf%knots = [newnx, newny]
574+
dsurf%nmax = max(newnx, newny)
575+
dsurf%nest = dsurf%nmax
576+
577+
allocate(dsurf%t(dsurf%nmax, 2), source=zero)
578+
dsurf%t(1:newnx, 1) = this%t(1+nux:nx-nux, 1)
579+
dsurf%t(1:newny, 2) = this%t(1+nuy:ny-nuy, 2)
580+
581+
allocate(dsurf%c(nc_new), source=newc(1:nc_new))
582+
583+
dsurf%left = [dsurf%t(newkx+1, 1), dsurf%t(newky+1, 2)]
584+
dsurf%right = [dsurf%t(newnx-newkx, 1), dsurf%t(newny-newky, 2)]
585+
586+
! Allocate evaluation workspace (bispev needs lwrk >= mx*(kx+1)+my*(ky+1), kwrk >= mx+my)
587+
dsurf%lwrk = newnx*(newkx+1) + newny*(newky+1) + nc_new
588+
dsurf%liwrk = newnx + newny
589+
allocate(dsurf%wrk(dsurf%lwrk), source=zero)
590+
allocate(dsurf%iwrk(dsurf%liwrk), source=0_FP_SIZE)
591+
end if
592+
593+
call fitpack_error_handling(ier, ierr, 'grid surface derivative spline')
594+
595+
end function gridsurf_derivative_spline
596+
597+
467598
! =================================================================================================
468599
! PARALLEL COMMUNICATION
469600
! =================================================================================================

src/fitpack_surfaces.f90

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
module fitpack_surfaces
2121
use fitpack_core
2222
use fitpack_fitters
23+
use fitpack_curves, only: fitpack_curve
2324
implicit none
2425
private
2526

@@ -86,6 +87,15 @@ module fitpack_surfaces
8687
generic :: dfdx => surface_derivatives_one,surface_derivatives_many
8788
generic :: dfdx_ongrid => surface_derivatives_gridded
8889

90+
!> Double integration over a rectangular domain
91+
procedure :: integral => surface_integral
92+
93+
!> Extract a 1D cross-section curve from the surface
94+
procedure :: cross_section => surface_cross_section
95+
96+
!> Compute the derivative spline coefficients
97+
procedure :: derivative_spline => surface_derivative_spline
98+
8999
!> Parallel communication interface
90100
procedure :: comm_size => surf_comm_size
91101
procedure :: comm_pack => surf_comm_pack
@@ -541,6 +551,132 @@ real(FP_REAL) function surface_derivatives_one(this,x,y,dx,dy,ierr) result(f)
541551
end function surface_derivatives_one
542552

543553

554+
!> Double integration of the surface over a rectangular domain [lower(1),upper(1)] x [lower(2),upper(2)]
555+
real(FP_REAL) function surface_integral(this, lower, upper)
556+
class(fitpack_surface), intent(in) :: this
557+
real(FP_REAL), intent(in) :: lower(2), upper(2)
558+
559+
real(FP_REAL) :: wrk(this%knots(1)+this%knots(2)-this%order(1)-this%order(2)-2)
560+
561+
surface_integral = dblint(this%t(:,1), this%knots(1), &
562+
this%t(:,2), this%knots(2), &
563+
this%c, &
564+
this%order(1), this%order(2), &
565+
lower(1), upper(1), lower(2), upper(2), wrk)
566+
567+
end function surface_integral
568+
569+
!> Extract a 1D cross-section from the surface.
570+
!> If along_y=.true., returns f(y) = s(u,y); if along_y=.false., returns g(x) = s(x,u).
571+
function surface_cross_section(this, u, along_y, ierr) result(curve)
572+
class(fitpack_surface), intent(in) :: this
573+
real(FP_REAL), intent(in) :: u
574+
logical, intent(in) :: along_y
575+
integer(FP_FLAG), optional, intent(out) :: ierr
576+
type(fitpack_curve) :: curve
577+
578+
integer(FP_FLAG) :: ier
579+
integer(FP_SIZE) :: iopt, nu, nc
580+
real(FP_REAL), allocatable :: cu(:)
581+
582+
if (along_y) then
583+
! f(y) = s(u,y): result has knots=ty, order=ky
584+
iopt = 0
585+
nu = this%knots(2)
586+
nc = this%knots(2) - this%order(2) - 1
587+
else
588+
! g(x) = s(x,u): result has knots=tx, order=kx
589+
iopt = 1
590+
nu = this%knots(1)
591+
nc = this%knots(1) - this%order(1) - 1
592+
end if
593+
594+
allocate(cu(nu))
595+
596+
call profil(iopt, this%t(:,1), this%knots(1), &
597+
this%t(:,2), this%knots(2), &
598+
this%c, &
599+
this%order(1), this%order(2), &
600+
u, nu, cu, ier)
601+
602+
if (FITPACK_SUCCESS(ier)) then
603+
! Build a fitpack_curve from the cross-section coefficients
604+
curve%order = merge(this%order(2), this%order(1), along_y)
605+
curve%knots = nu
606+
curve%nest = nu
607+
allocate(curve%t(nu), source=merge_knots(along_y, this%t(:,1), this%t(:,2), nu))
608+
allocate(curve%c(nu), source=zero)
609+
curve%c(:nc) = cu(:nc)
610+
curve%xleft = curve%t(curve%order + 1)
611+
curve%xright = curve%t(curve%knots - curve%order)
612+
end if
613+
614+
call fitpack_error_handling(ier, ierr, 'surface cross-section')
615+
616+
end function surface_cross_section
617+
618+
!> Compute the derivative spline d^(nux+nuy)s / dx^nux dy^nuy.
619+
!> Returns a new surface with reduced order [kx-nux, ky-nuy] and trimmed knots.
620+
function surface_derivative_spline(this, nux, nuy, ierr) result(dsurf)
621+
class(fitpack_surface), intent(in) :: this
622+
integer(FP_SIZE), intent(in) :: nux, nuy
623+
integer(FP_FLAG), optional, intent(out) :: ierr
624+
type(fitpack_surface) :: dsurf
625+
626+
integer(FP_FLAG) :: ier
627+
integer(FP_SIZE) :: nx, ny, newkx, newky, newnx, newny, nc_old, nc_new
628+
real(FP_REAL), allocatable :: newc(:)
629+
630+
nx = this%knots(1)
631+
ny = this%knots(2)
632+
nc_old = (nx - this%order(1) - 1) * (ny - this%order(2) - 1)
633+
634+
allocate(newc(nc_old))
635+
636+
call pardtc(this%t(:,1), nx, this%t(:,2), ny, &
637+
this%c, this%order(1), this%order(2), &
638+
nux, nuy, newc, ier)
639+
640+
if (FITPACK_SUCCESS(ier)) then
641+
newkx = this%order(1) - nux
642+
newky = this%order(2) - nuy
643+
newnx = nx - 2*nux
644+
newny = ny - 2*nuy
645+
nc_new = (newnx - newkx - 1) * (newny - newky - 1)
646+
647+
dsurf%order = [newkx, newky]
648+
dsurf%knots = [newnx, newny]
649+
dsurf%nmax = max(newnx, newny)
650+
dsurf%nest = dsurf%nmax
651+
652+
allocate(dsurf%t(dsurf%nmax, 2), source=zero)
653+
dsurf%t(1:newnx, 1) = this%t(1+nux:nx-nux, 1)
654+
dsurf%t(1:newny, 2) = this%t(1+nuy:ny-nuy, 2)
655+
656+
allocate(dsurf%c(nc_new), source=newc(1:nc_new))
657+
658+
dsurf%left = [dsurf%t(newkx+1, 1), dsurf%t(newky+1, 2)]
659+
dsurf%right = [dsurf%t(newnx-newkx, 1), dsurf%t(newny-newky, 2)]
660+
end if
661+
662+
call fitpack_error_handling(ier, ierr, 'surface derivative spline')
663+
664+
end function surface_derivative_spline
665+
666+
!> Helper: select knots from the appropriate direction for cross_section
667+
pure function merge_knots(along_y, tx, ty, n) result(knots)
668+
logical, intent(in) :: along_y
669+
real(FP_REAL), intent(in) :: tx(:), ty(:)
670+
integer(FP_SIZE), intent(in) :: n
671+
real(FP_REAL) :: knots(n)
672+
if (along_y) then
673+
knots = ty(1:n)
674+
else
675+
knots = tx(1:n)
676+
end if
677+
end function merge_knots
678+
679+
544680
! =================================================================================================
545681
! PARALLEL COMMUNICATION
546682
! =================================================================================================

0 commit comments

Comments
 (0)