Skip to content

Commit 7565bb3

Browse files
committed
Add least_squares, reset_knots, keep_knots across all fitter types
- Add least_squares(smoothing, reset_knots) to all 9 fitter types (was missing from fitpack_parametric_curve). When reset_knots=.true., a smoothing pre-fit determines sparser knots before the LS pass. - Add interpolate(reset_knots) to all 9 types (default .true.). When .false., FITPACK continues from current knots. - Add keep_knots parameter to fit() to bypass the iopt guard. - Extend test_umbrella_api with parametric curve interpolate/LS tests. - Mark PR A (Public API Cleanup) as done in refactoring plan.
1 parent 15e0bab commit 7565bb3

12 files changed

+269
-70
lines changed

docs/refactoring/04_oop_interface_analysis.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ that serializes all allocatable components).
223223

224224
# PR Plans
225225

226-
## PR A: Public API Cleanup & Umbrella Module
226+
## PR A: Public API Cleanup & Umbrella Module (DONE)
227227

228228
**Goal**: Make the `fitpack` umbrella module self-sufficient — users should never need
229229
`use fitpack_core` directly.
@@ -270,9 +270,9 @@ A user writing `curve%bc = OUTSIDE_EXTRAPOLATE` must also `use fitpack_core`. Si
270270

271271
---
272272

273-
## PR B: Abstract Base Type
273+
## PR B: Abstract Base Type (DONE — PR #41)
274274

275-
**Goal**: Extract shared state and logic into an abstract `fitpack_spline` base type that
275+
**Goal**: Extract shared state and logic into an abstract `fitpack_fitter` base type that
276276
all 9 concrete types extend.
277277

278278
### Design

project/fitpack.cbp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
<Unit filename="../src/fitpack_constrained_c.f90">
9191
<Option weight="0" />
9292
</Unit>
93+
<Unit filename="../src/fitpack_core.F90" />
9394
<Unit filename="../src/fitpack_core.f90">
9495
<Option weight="0" />
9596
</Unit>
@@ -102,6 +103,7 @@
102103
<Unit filename="../src/fitpack_curves_c.f90">
103104
<Option weight="0" />
104105
</Unit>
106+
<Unit filename="../src/fitpack_fitters.f90" />
105107
<Unit filename="../src/fitpack_grid_surfaces.f90">
106108
<Option weight="0" />
107109
</Unit>

src/fitpack_curves.f90

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -331,39 +331,57 @@ pure function curve_eval_many_pure(this,x) result(y)
331331
end function curve_eval_many_pure
332332

333333
! Interpolating curve
334-
integer(FP_FLAG) function interpolating_curve(this,order) result(ierr)
334+
integer(FP_FLAG) function interpolating_curve(this,order,reset_knots) result(ierr)
335335
class(fitpack_curve), intent(inout) :: this
336336
integer(FP_SIZE), optional, intent(in) :: order
337+
logical, optional, intent(in) :: reset_knots
337338

338-
! Set zero smoothing
339-
this%iopt = IOPT_NEW_SMOOTHING
340-
341-
! Update order if necessary
342-
ierr = curve_fit_automatic_knots(this,smoothing=zero,order=order)
339+
logical :: do_reset
340+
341+
do_reset = .true.; if (present(reset_knots)) do_reset = reset_knots
342+
if (do_reset) this%iopt = IOPT_NEW_SMOOTHING
343+
344+
ierr = curve_fit_automatic_knots(this,smoothing=zero,order=order,keep_knots=.not.do_reset)
343345

344346
end function interpolating_curve
345347

346348
! Least-squares curve fit with current knots
347-
integer(FP_FLAG) function curve_fit_least_squares(this) result(ierr)
349+
integer(FP_FLAG) function curve_fit_least_squares(this,smoothing,reset_knots) result(ierr)
348350
class(fitpack_curve), intent(inout) :: this
351+
real(FP_REAL), optional, intent(in) :: smoothing
352+
logical, optional, intent(in) :: reset_knots
353+
354+
logical :: do_reset
355+
356+
! Optionally recompute knots via a smoothing fit first
357+
do_reset = .false.; if (present(reset_knots)) do_reset = reset_knots
358+
if (do_reset) then
359+
this%iopt = IOPT_NEW_SMOOTHING
360+
ierr = this%fit(smoothing)
361+
if (.not.FITPACK_SUCCESS(ierr)) return
362+
end if
363+
349364
this%iopt = IOPT_NEW_LEASTSQUARES
350365
ierr = this%fit()
351366
end function curve_fit_least_squares
352367

353368
! Curve fitting driver: automatic number of knots
354-
integer(FP_FLAG) function curve_fit_automatic_knots(this,smoothing,order) result(ierr)
369+
integer(FP_FLAG) function curve_fit_automatic_knots(this,smoothing,order,keep_knots) result(ierr)
355370
class(fitpack_curve), intent(inout) :: this
356371
real(FP_REAL), optional, intent(in) :: smoothing
357372
integer(FP_SIZE), optional, intent(in) :: order
373+
logical, optional, intent(in) :: keep_knots
358374

359375
integer(FP_SIZE) :: loop,nit
360376
real(FP_REAL) :: smooth_now(3)
377+
logical :: do_guard
361378

362379
!> Get smoothing trajectory
363380
call get_smoothing(this%smoothing,smoothing,nit,smooth_now)
364381

365-
!> Ensure we start with new knots
366-
if (this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
382+
!> Ensure we start with new knots (unless caller wants to keep them)
383+
do_guard = .true.; if (present(keep_knots)) do_guard = .not.keep_knots
384+
if (do_guard .and. this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
367385

368386
!> Set/update order
369387
if (present(order)) this%order = order

src/fitpack_grid_surfaces.f90

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,37 +94,56 @@ module fitpack_grid_surfaces
9494
contains
9595

9696
! Fit a surface to least squares of the current knots
97-
integer function surface_fit_least_squares(this) result(ierr)
97+
integer function surface_fit_least_squares(this,smoothing,reset_knots) result(ierr)
9898
class(fitpack_grid_surface), intent(inout) :: this
99+
real(FP_REAL), optional, intent(in) :: smoothing
100+
logical, optional, intent(in) :: reset_knots
101+
102+
logical :: do_reset
103+
104+
! Optionally recompute knots via a smoothing fit first
105+
do_reset = .false.; if (present(reset_knots)) do_reset = reset_knots
106+
if (do_reset) then
107+
this%iopt = IOPT_NEW_SMOOTHING
108+
ierr = this%fit(smoothing)
109+
if (.not.FITPACK_SUCCESS(ierr)) return
110+
end if
99111

100112
this%iopt = IOPT_NEW_LEASTSQUARES
101113
ierr = this%fit()
102114

103115
end function surface_fit_least_squares
104116

105117
! Find interpolating surface
106-
integer function surface_fit_interpolating(this) result(ierr)
118+
integer function surface_fit_interpolating(this,reset_knots) result(ierr)
107119
class(fitpack_grid_surface), intent(inout) :: this
120+
logical, optional, intent(in) :: reset_knots
121+
122+
logical :: do_reset
108123

109-
! Set zero smoothing
110-
ierr = surface_fit_automatic_knots(this,smoothing=zero)
124+
do_reset = .true.; if (present(reset_knots)) do_reset = reset_knots
125+
if (do_reset) this%iopt = IOPT_NEW_SMOOTHING
126+
ierr = surface_fit_automatic_knots(this,smoothing=zero,keep_knots=.not.do_reset)
111127

112128
end function surface_fit_interpolating
113129

114130

115131
! Fit a surface z = s(x,y) defined on a meshgrid: x[1:n], y[1:m]
116-
integer(FP_FLAG) function surface_fit_automatic_knots(this,smoothing,order) result(ierr)
132+
integer(FP_FLAG) function surface_fit_automatic_knots(this,smoothing,order,keep_knots) result(ierr)
117133
class(fitpack_grid_surface), intent(inout) :: this
118134
real(FP_REAL), optional, intent(in) :: smoothing
119135
integer, optional, intent(in) :: order
136+
logical, optional, intent(in) :: keep_knots
120137

121138
integer(FP_SIZE) :: loop,nit
122139
real(FP_REAL) :: smooth_now(3)
140+
logical :: do_guard
123141

124142
call get_smoothing(this%smoothing,smoothing,nit,smooth_now)
125143

126-
!> Ensure we start with new knots
127-
if (this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
144+
!> Ensure we start with new knots (unless caller wants to keep them)
145+
do_guard = .true.; if (present(keep_knots)) do_guard = .not.keep_knots
146+
if (do_guard .and. this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
128147

129148
! User may want to change the order for both x and y
130149
if (present(order)) this%order = order

src/fitpack_gridded_polar.f90

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,36 +106,55 @@ module fitpack_gridded_polar
106106
contains
107107

108108
! Fit a surface to least squares of the current knots
109-
integer function polr_fit_least_squares(this) result(ierr)
109+
integer function polr_fit_least_squares(this,smoothing,reset_knots) result(ierr)
110110
class(fitpack_grid_polar), intent(inout) :: this
111+
real(FP_REAL), optional, intent(in) :: smoothing
112+
logical, optional, intent(in) :: reset_knots
113+
114+
logical :: do_reset
115+
116+
! Optionally recompute knots via a smoothing fit first
117+
do_reset = .false.; if (present(reset_knots)) do_reset = reset_knots
118+
if (do_reset) then
119+
this%iopt = IOPT_NEW_SMOOTHING
120+
ierr = this%fit(smoothing)
121+
if (.not.FITPACK_SUCCESS(ierr)) return
122+
end if
111123

112124
this%iopt = IOPT_NEW_LEASTSQUARES
113125
ierr = this%fit()
114126

115127
end function polr_fit_least_squares
116128

117129
! Find interpolating surface
118-
integer function polr_fit_interpolating(this) result(ierr)
130+
integer function polr_fit_interpolating(this,reset_knots) result(ierr)
119131
class(fitpack_grid_polar), intent(inout) :: this
132+
logical, optional, intent(in) :: reset_knots
133+
134+
logical :: do_reset
120135

121-
! Set zero smoothing
122-
ierr = polr_fit_automatic_knots(this,smoothing=zero)
136+
do_reset = .true.; if (present(reset_knots)) do_reset = reset_knots
137+
if (do_reset) this%iopt = IOPT_NEW_SMOOTHING
138+
ierr = polr_fit_automatic_knots(this,smoothing=zero,keep_knots=.not.do_reset)
123139

124140
end function polr_fit_interpolating
125141

126142

127143
! Fit a surface z = s(x,y) defined on a meshgrid: x[1:n], y[1:m]
128-
integer function polr_fit_automatic_knots(this,smoothing) result(ierr)
144+
integer function polr_fit_automatic_knots(this,smoothing,keep_knots) result(ierr)
129145
class(fitpack_grid_polar), intent(inout) :: this
130146
real(FP_REAL), optional, intent(in) :: smoothing
147+
logical, optional, intent(in) :: keep_knots
131148

132149
integer :: loop,nit,iopt(3),ider(2)
133150
real(FP_REAL) :: smooth_now(3)
151+
logical :: do_guard
134152

135153
call get_smoothing(this%smoothing,smoothing,nit,smooth_now)
136154

137-
!> Ensure we start with new knots
138-
if (this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
155+
!> Ensure we start with new knots (unless caller wants to keep them)
156+
do_guard = .true.; if (present(keep_knots)) do_guard = .not.keep_knots
157+
if (do_guard .and. this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
139158

140159
do loop=1,nit
141160

src/fitpack_gridded_sphere.f90

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,36 +99,55 @@ module fitpack_gridded_sphere
9999
contains
100100

101101
! Fit a surface to least squares of the current knots
102-
integer function spgrid_fit_least_squares(this) result(ierr)
102+
integer function spgrid_fit_least_squares(this,smoothing,reset_knots) result(ierr)
103103
class(fitpack_grid_sphere), intent(inout) :: this
104+
real(FP_REAL), optional, intent(in) :: smoothing
105+
logical, optional, intent(in) :: reset_knots
106+
107+
logical :: do_reset
108+
109+
! Optionally recompute knots via a smoothing fit first
110+
do_reset = .false.; if (present(reset_knots)) do_reset = reset_knots
111+
if (do_reset) then
112+
this%iopt = IOPT_NEW_SMOOTHING
113+
ierr = this%fit(smoothing)
114+
if (.not.FITPACK_SUCCESS(ierr)) return
115+
end if
104116

105117
this%iopt = IOPT_NEW_LEASTSQUARES
106118
ierr = this%fit()
107119

108120
end function spgrid_fit_least_squares
109121

110122
! Find interpolating surface
111-
integer function spgrid_fit_interpolating(this) result(ierr)
123+
integer function spgrid_fit_interpolating(this,reset_knots) result(ierr)
112124
class(fitpack_grid_sphere), intent(inout) :: this
125+
logical, optional, intent(in) :: reset_knots
126+
127+
logical :: do_reset
113128

114-
! Set zero smoothing
115-
ierr = spgrid_fit_automatic_knots(this,smoothing=zero)
129+
do_reset = .true.; if (present(reset_knots)) do_reset = reset_knots
130+
if (do_reset) this%iopt = IOPT_NEW_SMOOTHING
131+
ierr = spgrid_fit_automatic_knots(this,smoothing=zero,keep_knots=.not.do_reset)
116132

117133
end function spgrid_fit_interpolating
118134

119135

120136
! Fit a surface z = s(x,y) defined on a meshgrid: x[1:n], y[1:m]
121-
integer function spgrid_fit_automatic_knots(this,smoothing) result(ierr)
137+
integer function spgrid_fit_automatic_knots(this,smoothing,keep_knots) result(ierr)
122138
class(fitpack_grid_sphere), intent(inout) :: this
123139
real(FP_REAL), optional, intent(in) :: smoothing
140+
logical, optional, intent(in) :: keep_knots
124141

125142
integer :: loop,nit,iopt(3),ider(4)
126143
real(FP_REAL) :: smooth_now(3)
144+
logical :: do_guard
127145

128146
call get_smoothing(this%smoothing,smoothing,nit,smooth_now)
129147

130-
!> Ensure we start with new knots
131-
if (this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
148+
!> Ensure we start with new knots (unless caller wants to keep them)
149+
do_guard = .true.; if (present(keep_knots)) do_guard = .not.keep_knots
150+
if (do_guard .and. this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
132151

133152
do loop=1,nit
134153

src/fitpack_parametric.f90

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ module fitpack_parametric_curves
8282

8383
!> Generate/update fitting curve, with optional smoothing
8484
procedure :: fit => curve_fit_automatic_knots
85-
procedure :: interpolate => interpolating_curve
85+
procedure :: interpolate => interpolating_curve
86+
procedure :: least_squares => parcur_fit_least_squares
8687

8788
!> Evaluate curve at given coordinates
8889
procedure :: eval_one => curve_eval_one
@@ -427,31 +428,56 @@ function curve_eval_many(this,u,ierr) result(x)
427428
end function curve_eval_many
428429

429430
! Interpolating curve
430-
integer function interpolating_curve(this,order) result(ierr)
431+
integer function interpolating_curve(this,order,reset_knots) result(ierr)
431432
class(fitpack_parametric_curve), intent(inout) :: this
432433
integer(FP_SIZE), optional, intent(in) :: order
434+
logical, optional, intent(in) :: reset_knots
433435

434-
! Set zero smoothing
435-
this%iopt = IOPT_NEW_SMOOTHING
436+
logical :: do_reset
437+
438+
do_reset = .true.; if (present(reset_knots)) do_reset = reset_knots
439+
if (do_reset) this%iopt = IOPT_NEW_SMOOTHING
436440

437-
! Set zero smoothing
438-
ierr = curve_fit_automatic_knots(this,smoothing=zero,order=order)
441+
ierr = curve_fit_automatic_knots(this,smoothing=zero,order=order,keep_knots=.not.do_reset)
439442

440443
end function interpolating_curve
441444

445+
! Least-squares curve fit with current knots
446+
integer function parcur_fit_least_squares(this,smoothing,reset_knots) result(ierr)
447+
class(fitpack_parametric_curve), intent(inout) :: this
448+
real(FP_REAL), optional, intent(in) :: smoothing
449+
logical, optional, intent(in) :: reset_knots
450+
451+
logical :: do_reset
452+
453+
! Optionally recompute knots via a smoothing fit first
454+
do_reset = .false.; if (present(reset_knots)) do_reset = reset_knots
455+
if (do_reset) then
456+
this%iopt = IOPT_NEW_SMOOTHING
457+
ierr = this%fit(smoothing)
458+
if (.not.FITPACK_SUCCESS(ierr)) return
459+
end if
460+
461+
this%iopt = IOPT_NEW_LEASTSQUARES
462+
ierr = this%fit()
463+
end function parcur_fit_least_squares
464+
442465
! Curve fitting driver: automatic number of knots
443-
integer function curve_fit_automatic_knots(this,smoothing,order) result(ierr)
466+
integer function curve_fit_automatic_knots(this,smoothing,order,keep_knots) result(ierr)
444467
class(fitpack_parametric_curve), intent(inout) :: this
445468
real(FP_REAL), optional, intent(in) :: smoothing
446469
integer(FP_SIZE), optional, intent(in) :: order
470+
logical, optional, intent(in) :: keep_knots
447471

448472
integer :: loop,nit
449473
real(FP_REAL) :: smooth_now(3)
474+
logical :: do_guard
450475

451476
call get_smoothing(this%smoothing,smoothing,nit,smooth_now)
452477

453-
!> Ensure we start with new knots
454-
if (this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
478+
!> Ensure we start with new knots (unless caller wants to keep them)
479+
do_guard = .true.; if (present(keep_knots)) do_guard = .not.keep_knots
480+
if (do_guard .and. this%iopt==IOPT_OLD_FIT) this%iopt = IOPT_NEW_SMOOTHING
455481

456482
! Set order
457483
if (present(order)) this%order = order

0 commit comments

Comments
 (0)