Skip to content

Commit 4d5325f

Browse files
Merge pull request #7 from jacobwilliams/develop
Develop
2 parents 10d655e + e092d5d commit 4d5325f

File tree

2 files changed

+142
-41
lines changed

2 files changed

+142
-41
lines changed

numdiff.code-workspace

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"folders": [
3+
{
4+
"path": "."
5+
}
6+
],
7+
"settings": {
8+
"files.trimTrailingWhitespace": true,
9+
"editor.insertSpaces": true,
10+
"editor.tabSize": 4,
11+
"editor.trimAutoWhitespace": true
12+
}
13+
}

src/numerical_differentiation_module.f90

+129-41
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ module numerical_differentiation_module
109109
real(wp),dimension(:),allocatable :: xlow !! lower bounds on `x`
110110
real(wp),dimension(:),allocatable :: xhigh !! upper bounds on `x`
111111

112+
logical :: print_messages = .true. !! if true, warning messages are printed
113+
!! to the `error_unit` for any errors.
114+
112115
integer :: chunk_size = 100 !! chuck size for allocating the arrays (>0)
113116

114117
integer :: perturb_mode = 1 !! perturbation mode:
@@ -416,9 +419,8 @@ subroutine get_formula(me,formula)
416419
character(len=:),allocatable,intent(out) :: formula
417420

418421
integer :: i !! counter
419-
integer :: istat !! write `iostat` flag
420-
character(len=10) :: x !! temp variable for integer to string conversion
421-
character(len=10) :: f !! temp variable for integer to string conversion
422+
character(len=:),allocatable :: x !! temp variable for integer to string conversion
423+
character(len=:),allocatable :: f !! temp variable for integer to string conversion
422424

423425
if (allocated(me%dx_factors) .and. allocated(me%df_factors)) then
424426

@@ -436,9 +438,9 @@ subroutine get_formula(me,formula)
436438
formula = formula//'-f('
437439
else
438440
if (i==1) then
439-
write(f,'(I10)',iostat=istat) int(me%df_factors(i)) ! integer to string
441+
f = integer_to_string(int(me%df_factors(i)))
440442
else
441-
write(f,'(SP,I10)',iostat=istat) int(me%df_factors(i)) ! integer to string (with sign)
443+
f = integer_to_string(int(me%df_factors(i)), with_sign = .true.)
442444
end if
443445
formula = formula//trim(adjustl(f))//'f('
444446
end if
@@ -450,15 +452,15 @@ subroutine get_formula(me,formula)
450452
elseif (int(me%dx_factors(i))==-1) then
451453
formula = formula//'x-h'
452454
else
453-
write(x,'(SP,I10)',iostat=istat) int(me%dx_factors(i)) ! integer to string (with sign)
455+
x = integer_to_string(int(me%dx_factors(i)), with_sign = .true.)
454456
formula = formula//'x'//trim(adjustl(x))//'h'
455457
end if
456458

457459
formula = formula//')'
458460

459461
end do
460462

461-
write(f,'(I10)',iostat=istat) int(me%df_den_factor) ! integer to string
463+
f = integer_to_string(int(me%df_den_factor))
462464
if (int(me%df_den_factor)==1) then
463465
formula = formula//') / h'
464466
else
@@ -854,20 +856,25 @@ subroutine select_finite_diff_method(me,x,xlow,xhigh,dx,list_of_methods,fd,statu
854856

855857
integer :: i !! counter
856858
integer :: j !! counter
859+
real(wp) :: xs !! if `x` is outside the bounds, this is the value
860+
!! on the nearest bound. otherwise equal to `x`.
857861
real(wp) :: xp !! perturbed `x` value
858862

859863
! initialize:
860864
status_ok = .false.
861865

862866
if (me%exception_raised) return ! check for exceptions
863867

868+
! make sure it is within the bounds
869+
xs = min(xhigh,max(xlow,x))
870+
864871
! try all the methods in the class:
865872
do i = 1, size(list_of_methods%meth)
866873
status_ok = .true. ! will be set to false if any
867874
! perturbation violates the bounds
868875
! check each of the perturbations:
869876
do j = 1, size(list_of_methods%meth(i)%dx_factors)
870-
xp = x + list_of_methods%meth(i)%dx_factors(j)*dx
877+
xp = xs + list_of_methods%meth(i)%dx_factors(j)*dx
871878
if (xp < xlow .or. xp > xhigh) then
872879
status_ok = .false.
873880
exit
@@ -915,19 +922,24 @@ subroutine select_finite_diff_method_for_partition_group(me,x,xlow,xhigh,dx,&
915922
integer :: i !! counter
916923
integer :: j !! counter
917924
real(wp),dimension(size(x)) :: xp !! perturbed `x` values
925+
real(wp),dimension(size(x)) :: xs !! if `x` is outside the bounds, this is the value
926+
!! on the nearest bound. otherwise equal to `x`.
918927

919928
! initialize:
920929
status_ok = .false.
921930

922931
if (me%exception_raised) return ! check for exceptions
923932

933+
! make sure they are within the bounds
934+
xs = min(xhigh,max(xlow,x))
935+
924936
! try all the methods in the class:
925937
do i = 1, size(list_of_methods%meth)
926938
status_ok = .true. ! will be set to false if any
927939
! perturbation violates the bounds
928940
! check each of the perturbations:
929941
do j = 1, size(list_of_methods%meth(i)%dx_factors)
930-
xp = x + list_of_methods%meth(i)%dx_factors(j)*dx
942+
xp = xs + list_of_methods%meth(i)%dx_factors(j)*dx
931943
if (any(xp < xlow) .or. any(xp > xhigh)) then
932944
status_ok = .false.
933945
exit
@@ -959,7 +971,7 @@ subroutine initialize_numdiff_for_diff(me,n,m,xlow,xhigh,&
959971
xlow_for_sparsity,xhigh_for_sparsity,&
960972
dpert_for_sparsity,sparsity_perturb_mode,&
961973
linear_sparsity_tol,function_precision_tol,&
962-
num_sparsity_points)
974+
num_sparsity_points,print_messages)
963975

964976
implicit none
965977

@@ -1015,6 +1027,8 @@ subroutine initialize_numdiff_for_diff(me,n,m,xlow,xhigh,&
10151027
!! considered the same value. This is used
10161028
!! when estimating the sparsity pattern when
10171029
!! `sparsity_mode=2` in [[compute_sparsity_random]]
1030+
logical,intent(in),optional :: print_messages !! if true, print error messages to `error_unit`.
1031+
!! default is True.
10181032

10191033
logical :: cache !! if the cache is to be used
10201034

@@ -1070,10 +1084,11 @@ subroutine initialize_numdiff_for_diff(me,n,m,xlow,xhigh,&
10701084
if (present(num_sparsity_points)) me%num_sparsity_points = num_sparsity_points
10711085

10721086
! optional:
1073-
if (present(chunk_size)) me%chunk_size = abs(chunk_size)
1074-
if (present(eps)) me%eps = eps
1075-
if (present(acc)) me%acc = acc
1076-
if (present(info)) me%info_function => info
1087+
if (present(chunk_size)) me%chunk_size = abs(chunk_size)
1088+
if (present(eps)) me%eps = eps
1089+
if (present(acc)) me%acc = acc
1090+
if (present(info)) me%info_function => info
1091+
if (present(print_messages)) me%print_messages = print_messages
10771092

10781093
end subroutine initialize_numdiff_for_diff
10791094
!*******************************************************************************
@@ -1098,7 +1113,7 @@ subroutine set_numdiff_bounds(me,xlow,xhigh)
10981113

10991114
integer :: i !! counter for error print
11001115
character(len=:),allocatable :: error_info !! error message info
1101-
character(len=10) :: istr !! for integer to string
1116+
character(len=:),allocatable :: istr !! for integer to string
11021117
character(len=30) :: xlow_str, xhigh_str !! for real to string
11031118

11041119
if (me%exception_raised) return ! check for exceptions
@@ -1114,7 +1129,7 @@ subroutine set_numdiff_bounds(me,xlow,xhigh)
11141129
error_info = 'all xlow must be < xhigh'
11151130
do i = 1, size(xlow)
11161131
if (xlow(i)>=xhigh(i)) then
1117-
write(istr,'(I10)') i
1132+
istr = integer_to_string(i)
11181133
write(xlow_str,'(F30.16)') xlow(i)
11191134
write(xhigh_str,'(F30.16)') xhigh(i)
11201135
error_info = error_info//new_line('')//' Error for optimization variable '//trim(adjustl(istr))//&
@@ -1520,15 +1535,17 @@ subroutine dsm_wrapper(me,n,m,info)
15201535
integer,intent(out) :: info !! status output from [[dsm]]
15211536

15221537
integer :: mingrp !! for call to [[dsm]]
1523-
integer,dimension(m+1) :: ipntr !! for call to [[dsm]]
1524-
integer,dimension(n+1) :: jpntr !! for call to [[dsm]]
1525-
integer,dimension(:),allocatable :: irow !! for call to [[dsm]]
1526-
!! (temp copy since [[dsm]]
1527-
!! will modify it)
1528-
integer,dimension(:),allocatable :: icol !! for call to [[dsm]]
1529-
!! (temp copy since [[dsm]]
1530-
!! will modify it)
1531-
1538+
integer,dimension(:),allocatable :: ipntr !! for call to [[dsm]]
1539+
integer,dimension(:),allocatable :: jpntr !! for call to [[dsm]]
1540+
integer,dimension(:),allocatable :: irow !! for call to [[dsm]]
1541+
!! (temp copy since [[dsm]]
1542+
!! will modify it)
1543+
integer,dimension(:),allocatable :: icol !! for call to [[dsm]]
1544+
!! (temp copy since [[dsm]]
1545+
!! will modify it)
1546+
1547+
allocate(ipntr(m+1))
1548+
allocate(jpntr(n+1))
15321549
allocate(me%ngrp(n))
15331550
irow = me%irow
15341551
icol = me%icol
@@ -1644,7 +1661,7 @@ end subroutine compute_indices
16441661
!@note If specifying the linear pattern, all three optional arguments
16451662
! must be present.
16461663

1647-
subroutine set_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals)
1664+
subroutine set_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals,maxgrp,ngrp)
16481665

16491666
implicit none
16501667

@@ -1654,6 +1671,10 @@ subroutine set_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals
16541671
integer,dimension(:),intent(in),optional :: linear_irow !! linear sparsity pattern nonzero elements row indices
16551672
integer,dimension(:),intent(in),optional :: linear_icol !! linear sparsity pattern nonzero elements column indices
16561673
real(wp),dimension(:),intent(in),optional :: linear_vals !! linear sparsity values (constant elements of the Jacobian)
1674+
integer,intent(in),optional :: maxgrp !! DSM sparsity partition
1675+
!! [only used if `me%partition_sparsity_pattern=True`]
1676+
integer,dimension(:),intent(in),optional :: ngrp !! DSM sparsity partition (size `n`)
1677+
!! [only used if `me%partition_sparsity_pattern=True`]
16571678

16581679
integer :: info !! status output form [[dsm]]
16591680

@@ -1674,11 +1695,23 @@ subroutine set_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals
16741695

16751696
call me%sparsity%compute_indices()
16761697
if (me%partition_sparsity_pattern) then
1677-
call me%sparsity%dsm_wrapper(me%n,me%m,info)
1678-
if (info/=1) then
1679-
call me%raise_exception(16,'set_sparsity_pattern',&
1680-
'error partitioning sparsity pattern.')
1681-
return
1698+
if (present(maxgrp) .and. present(ngrp)) then
1699+
! use the user-input partition:
1700+
if (maxgrp>0 .and. all(ngrp>=1 .and. ngrp<=maxgrp) .and. size(ngrp)==me%n) then
1701+
me%sparsity%maxgrp = maxgrp
1702+
me%sparsity%ngrp = ngrp
1703+
else
1704+
call me%raise_exception(28,'set_sparsity_pattern',&
1705+
'invalid sparsity partition inputs.')
1706+
return
1707+
end if
1708+
else
1709+
call me%sparsity%dsm_wrapper(me%n,me%m,info)
1710+
if (info/=1) then
1711+
call me%raise_exception(16,'set_sparsity_pattern',&
1712+
'error partitioning sparsity pattern.')
1713+
return
1714+
end if
16821715
end if
16831716
end if
16841717

@@ -2148,7 +2181,9 @@ end subroutine compute_sparsity_pattern
21482181
! Returns the sparsity pattern from the class.
21492182
! If it hasn't been computed, the output arrays will not be allocated.
21502183

2151-
subroutine get_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals)
2184+
subroutine get_sparsity_pattern(me,irow,icol,&
2185+
linear_irow,linear_icol,linear_vals,&
2186+
maxgrp,ngrp)
21522187

21532188
implicit none
21542189

@@ -2161,6 +2196,8 @@ subroutine get_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals
21612196
!! elements column indices
21622197
real(wp),dimension(:),allocatable,intent(out),optional :: linear_vals !! linear sparsity values (constant
21632198
!! elements of the Jacobian)
2199+
integer,intent(out),optional :: maxgrp !! DSM sparsity partition
2200+
integer,dimension(:),allocatable,intent(out),optional :: ngrp !! DSM sparsity partition
21642201

21652202
if (me%exception_raised) return ! check for exceptions
21662203

@@ -2180,6 +2217,10 @@ subroutine get_sparsity_pattern(me,irow,icol,linear_irow,linear_icol,linear_vals
21802217
if (allocated(me%sparsity%linear_vals)) linear_vals = me%sparsity%linear_vals
21812218
end if
21822219

2220+
! optional DSM partition:
2221+
if (present(ngrp) .and. allocated(me%sparsity%ngrp)) ngrp = me%sparsity%ngrp
2222+
if (present(maxgrp)) maxgrp = me%sparsity%maxgrp
2223+
21832224
end subroutine get_sparsity_pattern
21842225
!*******************************************************************************
21852226

@@ -2427,8 +2468,12 @@ subroutine compute_jacobian_for_sparsity(me,i,class_meths,x,jac)
24272468

24282469
call me%select_finite_diff_method(x(i),me%xlow_for_sparsity(i),me%xhigh_for_sparsity(i),&
24292470
dx(i),class_meths,fd,status_ok)
2430-
if (.not. status_ok) write(error_unit,'(A,1X,I5)') &
2431-
'Error in compute_jacobian_for_sparsity: variable bounds violated for column: ',i
2471+
if (.not. status_ok) then
2472+
if (me%print_messages) then
2473+
write(error_unit,'(A,1X,I5)') &
2474+
'Error in compute_jacobian_for_sparsity: variable bounds violated for column: ',i
2475+
end if
2476+
end if
24322477

24332478
! compute this column of the Jacobian:
24342479
df = zero
@@ -2512,8 +2557,12 @@ subroutine compute_jacobian_standard(me,x,dx,jac)
25122557

25132558
call me%select_finite_diff_method(x(i),me%xlow(i),me%xhigh(i),&
25142559
dx(i),me%class_meths(i),fd,status_ok)
2515-
if (.not. status_ok) write(error_unit,'(A,1X,I5)') &
2516-
'Error in compute_jacobian_standard: variable bounds violated for column: ',i
2560+
if (.not. status_ok) then
2561+
if (me%print_messages) then
2562+
write(error_unit,'(A,1X,I5)') &
2563+
'Error in compute_jacobian_standard: variable bounds violated for column: ',i
2564+
end if
2565+
end if
25172566

25182567
! compute this column of the Jacobian:
25192568
df = zero
@@ -2743,11 +2792,13 @@ subroutine compute_jacobian_partitioned(me,x,dx,jac)
27432792
dx(cols),me%class_meths(1),fd,status_ok)
27442793

27452794
if (.not. status_ok) then
2746-
! will not consider this a fatal error for now:
2747-
write(error_unit,'(A,1X,I5,1X,A,1X,*(I5,1X))') &
2748-
'Error in compute_jacobian_partitioned: '//&
2749-
'variable bounds violated for group: ',&
2750-
igroup,'. columns: ',cols
2795+
if (me%print_messages) then
2796+
! will not consider this a fatal error for now:
2797+
write(error_unit,'(A,1X,I5,1X,A,1X,*(I5,1X))') &
2798+
'Error in compute_jacobian_partitioned: '//&
2799+
'variable bounds violated for group: ',&
2800+
igroup,'. columns: ',cols
2801+
end if
27512802
end if
27522803

27532804
! compute the columns of the Jacobian in this group:
@@ -3111,6 +3162,43 @@ subroutine get_error_status(me,istat,error_msg)
31113162
end subroutine get_error_status
31123163
!*******************************************************************************
31133164

3165+
!*******************************************************************************
3166+
!>
3167+
! Convert an integer to a string.
3168+
3169+
function integer_to_string(i, with_sign) result(str)
3170+
3171+
implicit none
3172+
3173+
integer,intent(in) :: i !! the integer
3174+
logical,intent(in),optional :: with_sign !! also include the sign (default is False)
3175+
character(len=:),allocatable :: str !! integer converted to a string
3176+
3177+
integer :: istat !! `iostat` code for write statement
3178+
character(len=100) :: tmp !!
3179+
logical :: sgn !! local copy of `with_sign`
3180+
3181+
if (present(with_sign)) then
3182+
sgn = with_sign
3183+
else
3184+
sgn = .false.
3185+
end if
3186+
3187+
if (sgn) then
3188+
write(tmp,'(SP,I100)',iostat=istat) i
3189+
else
3190+
write(tmp,'(I100)',iostat=istat) i
3191+
end if
3192+
3193+
if (istat == 0) then
3194+
str = trim(adjustl(tmp))
3195+
else
3196+
str = '****'
3197+
end if
3198+
3199+
end function integer_to_string
3200+
!*******************************************************************************
3201+
31143202
!*******************************************************************************
31153203
end module numerical_differentiation_module
31163204
!*******************************************************************************

0 commit comments

Comments
 (0)