Closed tuncaen closed 11 months ago
Sorry it's taken me awhile to get to this, I don't have very much time for Ford at the moment!
Hmm, that's a bit odd, I'm pretty sure it should be only displaying the summary. I think the relevant bit is here: https://github.com/Fortran-FOSS-Programmers/ford/blob/abec2a571e16e867fbbfec347c6d5db5ba6d9348/ford/templates/macros.html#L258
It looks like the routine BasisFuns
has its own page, so presumably it is visible
, so possibly full_docstring
is being set incorrectly somewhere.
Is this project public? If not, would you be able to supply a stripped down minimal example?
Hi, @ZedThree, sorry for the late answer, here is the minimal example,
src/BasisFunctions.f90
module BasisFunctions
!< The module defining spline basis objects and their arguments
integer, parameter :: wp = selected_real_kind(15,307) !< 15 digits, range \([10^{-307} , 10^{+307} - 1]\); 64 bits.
implicit none
private
public :: basis
!# **Spline [[basis(type)]] class**
! Procedures (methods) of this class assume that the spline basis defined by an **open knot vector**
! Object constructor function of this derived type is [[basis(interface)]]
type :: basis
!<
integer :: p !< Polynomial degree \(p\)
integer :: nk !< Number of the knots
integer :: m !< The highest index of the knots ` ( nk-1 ) `
integer :: nb !< Number of basis functions
integer :: n !< The highest index of the basis functions ` ( nb-1 ) `
real(wp), allocatable :: kv(:) !< [Open knot vector](../page/01.fooiga/eqns.html#openvec)
!< ` kv(0:m) ` \( = \left\{u_{0},..., u_{m}\right\} \)
contains
procedure, public :: FindSpan !< **Knot span index search **
procedure, public :: BasisFuns !< **Computation of the non-vanishing basis functions ** **
!# Overloading `=`.
generic :: assignment(=) => assign_basis
procedure, public :: assign_basis !< **Overloading `=`. **
end type basis
!***************************************************************************************************************************
! **OBJECT CONSTRUCTOR**
! - Basis (Int)
! =>
!> **Spline basis constructor**
interface basis
procedure basisConstructor
end interface
!***************************************************************************************************************************
contains
!***************************************************************************************************************************
! **METHODS OF `BASIS` TYPE**
! - FindSpan (Fn)
! - BasisFuns (Sub)
! =>
!#
pure elemental function FindSpan(me,u) result (span)
class(basis), intent(in) :: me
real(wp), intent(in) :: u !< Given \( u \) value
integer :: span !< Index of the corresponding knot span
!locals
integer :: low, high !< indices for binary search
associate( p => me%p, Ui => me%kv, n => me%n ) !get rid of % signs
!/* Special cases */
if (u >= Ui(n+1)) then
span = me%n
return
end if
if (u <= Ui(p)) then
span = p
return
end if
!/* Do binary search */
low = p; high = n+1
span = (low + high) / 2
do while (u < Ui(span) .or. u >= Ui(span+1))
if (u < Ui(span)) then
high = span
else
low = span
end if
span = (low + high) / 2
end do
end associate
end function FindSpan
! .......................................................
!# {!BasisFuns.md!}
pure subroutine BasisFuns(me,u,N,span)
class(basis), intent(in) :: me
real(wp), intent(in) :: u !< Given \( u \) value
integer, intent(in), optional :: span !< Knot span where \( u \) lies on
real(wp), intent(out) :: N(0:me%p) !< \( = \{ N_{i-p,p}(u) \ldots N_{i,p}(u)\} \)
!locals
integer :: i !< Knot span index
integer :: j, r
real(wp) :: left(me%p), right(me%p), saved, temp!, w
associate( p => me%p, Ui => me%kv ) !get rid of % signs
! set the knot span
if(present(span))then
i = span
else
i = me%FindSpan(u)
end if
N = 0.0_wp; right=0.0_wp; left=0.0_wp
N(0) = 1.0_wp
do j = 1, p
left(j) = u - Ui(i+1-j)
right(j) = Ui(i+j) - u
saved = 0.0_wp
do r = 0, j-1
temp = N(r) / (right(r+1) + left(j-r))
N(r) = saved + right(r+1) * temp
saved = left(j-r) * temp
end do
N(j) = saved
end do
end associate
end subroutine BasisFuns
! .......................................................
!***************************************************************************************************************************
!***************************************************************************************************************************
! **TYPE CONSTRUCTORS**
! - basis_constructor (Fn)
! =>
!#
pure function basisConstructor(nk,p,k) result(me)
integer, intent(in) :: nk !< Number of the knots
integer, intent(in) :: p !< Polynomial degree \(p\)
real(wp), intent(in) :: k(nk) !< [Open knot vector](../page/01.fooiga/eqns.html#openvec)
!< ` kv(0:m) ` \( = \left\{u_{0},..., u_{m}\right\} \)
type(basis) :: me !< Spline basis object
!locals
integer :: i
me%nk = nk
me%m = nk - 1
me%p = p
me%nb = nk - ( p + 1 ) !number of basis functions or control points
me%n = me%nb - 1 !highest index of the basis functions or control points
allocate(me%kv(0:nk-1), source=k)
end function basisConstructor
! .......................................................
!***************************************************************************************************************************
!***************************************************************************************************************************
! **OPERATOR OVERLOADING**
! - assign_basis ( `basis=basis` ) (Sub)
! =>
!# Overloading for assignment `=` operator,
pure elemental subroutine assign_basis(lhs, rhs)
class(basis), intent(inout) :: lhs !< Left hand side.
type(basis), intent(in) :: rhs !< Right hand side.
lhs%p = rhs%p
lhs%nk = rhs%nk
lhs%m = rhs%m
lhs%nb = rhs%nb
lhs%n = rhs%n
if(allocated(lhs%kv)) deallocate(lhs%kv)
allocate(lhs%kv, source = rhs%kv)
endsubroutine assign_basis
! .......................................................
!***************************************************************************************************************************
end module BasisFunctions
src_comment/BasisFuns.md
Computes the nonvanishing basis functions based on Algorithm A2.2 in the NURBS Book [^1].
### Implementaton Details of Algorithm A2.2 {#A2.2}
Let \( U=\{u_{0}, \ldots ,u_{m}\}\) be a nondecreasing sequence of real numbers, i.e., \(u_{i} \leq u_{i+1}\), \(i=1, \ldots ,m-1\). The \(u_{i}\) are called *knots*, and \(U\) is the *knot vector*. The \(i\)th B-spline basis function of \(p\)-degree (*order* \(p+1\)), denoted by \(N_{i,p}(u)\), is defined as
<span type="math display" id="2-5">
\begin{equation}
\begin{gathered}
N_{i, 0}(u)=\left\{\begin{array}{ll}{1} &
{\text { if } u_{i} \leq u<u_{i+1}} \\
{0} & {\text { otherwise }}\end{array}\right. \\
N_{i, p}(u)=\dfrac{u-u_{i}}{u_{i+p}-u_{i}} N_{i, p-1}(u)
+\dfrac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}} N_{i+1, p-1}(u).
\end{gathered}
\tag{2.5}
\end{equation} </span>
Note that
- \(N_{i,0}(u)\) is a step function, equal to zero everywhere except on the half-open interval \({[u_{i}, u_{i+1})}\);
- for \(p>0\), \(N_{i,p}(u)\) is a linear combination of two (\(p-1\))-degree basis functions
- computation of a set of basis functions requires specification of a knot vector, \(U\), and the degree, \(p\);
- [Equation (2.5)](#2-5) can yield the quotient \(0/0\); we define this quotient to be zero
- the half-open interval, \({[u_{i}, u_{i+1})}\), is called the *\(i\)th knot span*; it can have zero length, since knots need not be distinct
---
Assuming \(u\) is in the \(i\)th span, \(i=0,\ldots,n\), computation of the nonzero functions results in an inverted triangular scheme
![Algorithm 2.2](|media|/img/img_A2.2.png#small)
Algorithm computes all of the non-vanishing basis functions in an efficient way, [see example](#ex2-3), and return them in the array [` N(0:p) `]. There is no risk of division by zero as we can encounter in [Cox-de Boor recursion formula](#2-5).
**Note:** *This algorithm applies to rational basis functions.*
#### ***Ex2.3 Nonvanishing Basis Functions*** {#ex2-3}
There is a great deal of redundant computation inherent in [Cox-de Boor recursion formula](#2-5). For example, writing out the second-degree functions in general terms, we have
<span type="math display" id="2-14">
\begin{equation}
{N_{i - 2,2}}(u) = \frac{{u - {u_{i - 2}}}}{{{u_i} - {u_{i - 2}}}}{N_{i - 2,1}}(u) - \frac{{{u_{i + 1}} - u}}{{{u_{i + 1}} - {u_{i - 1}}}}{N_{i - 1,1}}(u)
\tag{2.14}
\end{equation} </span>
<span type="math display" id="2-15">
\begin{equation}
{N_{i - 1,2}}(u) = \frac{{u - {u_{i - 1}}}}{{{u_{i + 1}} - {u_{i - 1}}}}{N_{i - 1,1}}(u) - \frac{{{u_{i + 2}} - u}}{{{u_{i + 2}} - {u_i}}}{N_{i,1}}(u)
\tag{2.15}
\end{equation} </span>
<span type="math display" id="2-16">
\begin{equation}
{N_{i,2}}(u) = \frac{{u - {u_i}}}{{{u_{i + 2}} - {u_i}}}{N_{i,1}}(u) - \frac{{{u_{i + 3}} - u}}{{{u_{i + 3}} - {u_{i + 1}}}}{N_{i + 1,1}}(u)
\tag{2.16}
\end{equation} </span>
Note that
- the first term of [Eq. (2.14)](#2-14) and the last term of [Eq. (2.16)](#2-16) are not computed, since \(N_{i-2,1}(u)\) \(= N_{i+1,1}(u)\) \( = 0\);
- the expression \(\frac{{N_{i - 1,1}}(u)}{{{u_{i + 1}} - {u_{i - 1}}}}\) which appears in the second term of [Eq. (2.14)](#2-14) appears in the first term of [Eq. (2.15)](#2-15); a similar statement holds for the second term of [Eq. (2.15)](#2-15) and the first term of [Eq. (2.16)](#2-16).
We introduce the notation
<span type="math display" id="leftrightfun">
\begin{equation}
\mathop{\rm left}[j] = u - {u_{i + 1 - j}}\qquad
\mathop{\rm right}[j] = {u_{i + j}} - u
\notag
\end{equation} </span>
[Eq. (2.15](#2-14)[-2.16)](#2-16) are then
<span type="math display">
\begin{equation}
{N_{i - 2,2}}(u) = \frac{{{\mathop{\rm left}} [ 3 ]}}{{{\mathop{\rm right}} [ 0 ] + {\mathop{\rm left}} [ 3 ]}}{N_{i - 2,1}}(u) + \frac{{{\mathop{\rm right}} [ 1 ]}}{{{\mathop{\rm right}} [ 1 ] + {\mathop{\rm left}} [ 2 ]}}{N_{i - 1,1}}(u)
\notag
\end{equation} </span>
<span type="math display">
\begin{equation}
{N_{i - 1,2}}(u) = \frac{{{\mathop{\rm left}} [ 2 ]}}{{{\mathop{\rm right}} [ 1 ] + {\mathop{\rm left}} [ 2 ]}}{N_{i - 1,1}}(u) + \frac{{{\mathop{\rm right}} [ 2 ]}}{{{\mathop{\rm right}} [ 2 ] + {\mathop{\rm left}} [ 1 ]}}{N_{i,1}}(u)
\notag
\end{equation} </span>
<span type="math display">
\begin{equation}
{N_{i,2}}(u) = \frac{{{\mathop{\rm left}} [ 1 ]}}{{{\mathop{\rm right}} [ 2 ] + {\mathop{\rm left}} [ 1 ]}}{N_{i,1}}(u) + \frac{{{\mathop{\rm right}} [ 3 ]}}{{{\mathop{\rm right}} [ 3 ] + {\mathop{\rm left}} [ 0 ]}}{N_{i + 1,1}}(u)
\notag
\end{equation} </span>
---
[^1]: Piegl, L., and Tiller, W., (1995) The NURBS Book, Springer, Berlin, Heidelberg.
ford/project-file.md
project: minimal
src_dir: ../src/
md_base_dir: ../src_comment/
output_dir: ../doc/
sort: src
docmark: <
predocmark: >
predocmark_alt: #
display: public
protected
private
source: true
graph: true
search: true
preprocess: False
I've also tried the latest commits 9f6d8e4e658e118bd8b9d6db690bbeaa3401375e and d072cc7 for my project, however it comes up with the following error
Traceback (most recent call last):
File "C:\Users\tuncaen\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Users\tuncaen\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "C:\Users\tuncaen\AppData\Local\Programs\Python\Python310\Scripts\ford.exe\__main__.py", line 7, in <module>
File "C:\Users\tuncaen\src\ford\ford\__init__.py", line 491, in run
main(proj_data, proj_docs)
File "C:\Users\tuncaen\src\ford\ford\__init__.py", line 419, in main
project.correlate()
File "C:\Users\tuncaen\src\ford\ford\fortran_project.py", line 361, in correlate
container.correlate(self)
File "C:\Users\tuncaen\src\ford\ford\sourceform.py", line 1292, in correlate
entity.correlate(project)
File "C:\Users\tuncaen\src\ford\ford\sourceform.py", line 1257, in correlate
item = self._find_chain_item(call)
File "C:\Users\tuncaen\src\ford\ford\sourceform.py", line 1487, in _find_chain_item
return get_label_item(context, call_chain[-1])
File "C:\Users\tuncaen\src\ford\ford\sourceform.py", line 1449, in get_label_item
labels[extend_type.name.lower()] = extend_type
AttributeError: 'str' object has no attribute 'name'
Thanks in advance, and thank you for continuing to develop this library Kind regards.
Hi @tuncaen, no worries! I've double checked with your example, and I can confirm that it will be fixed in ford v7.0.0, which will be released very soon!
Hi,
I sometimes write long explanations for procedures. They used to be displayed as a few lines with a "read more" button. However, my last run yields all the procedure explanations of the derived types appear in bulk on the main page. This issue arises on the derived type pages. I attached some screenshots, the former from the modules pages the latter from the derived type pages.
This makes the main page of the derived types a bit messy. Is there any way to add the read more button into derived types pages as in the modules pages?