 |
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
jjgermis
Joined: 21 Jun 2006 Posts: 404 Location: N�rnberg, Germany
|
Posted: Wed Aug 22, 2012 10:02 am Post subject: |
|
|
Hi John,
thanks for your offer! I will come back to that. For the moment my roadmap is only to clean up and document what we have. After that I would really like to improve the code - in fact I already have some ideas where one can improve.
Our solver uses the ACTCOL from Zienkewich. I actually bought a German translation of the third edition to get hold of the Fortran IV code in chapter 24.
In the fifth edition the explanation and example of in the direct solver part is still the same (p. 610). However, I think that the profile storage has change a little bit. In the new version the diagonal elements are stored first and then the rest of the elements in each column. In our version each column is stored as it is, i.e. the diagonal terms are spread through the array.
I have also updated my eps format to plot the elements in the lower triangle blue and the ones in the upper triangle green  |
|
Back to top |
|
 |
jjgermis
Joined: 21 Jun 2006 Posts: 404 Location: N�rnberg, Germany
|
Posted: Wed Aug 22, 2012 7:54 pm Post subject: |
|
|
Hi John,
one further remark: initially I thought I can easily just replace GPS with Sloan. The adjacency list is formed in row-major - as required by GPS. As usual is everything catastrophe
Anyway, here is an example with the colors for lower and upper triangle. For the profile solver one actually needs to calculate the column heights. In our program practically the row widths are determined - works due to symmetry. However, is sometimes confusing if you look for something specific and find the opposite.
 |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2621 Location: Sydney
|
Posted: Thu Aug 23, 2012 1:57 am Post subject: |
|
|
The data structures you choose for the re-ordering algorithm are very important for the efficiency of the algorithm. The secret to efficiency is to have a number of node and element status arrays, so you don't have to calculate an updated status in inner loops.
My algorithm starts with 5 basic vectors.
Based on the element definitions you can readily generate:
IELEM(num_elements+1) ! which is the pointer to the first node in a list vector of all nodes connected to each element.
LNODE(*) is this list of nodes connected to each element.
Based on these two vectors there is a complimentary list structure for nodes:
INODE(num_nodes+1) ! which is the pointer to the first element in a list of all elements connected to each node.
LELEM(*) is this list of elements connected to each node.
NFREE(num_nodes) ! which is the number of freedoms(equations) associated with each node.
The size of LNODE and LELEM are the same.
You can filter these lists to remove:
- all nodes with no freedoms
- all elements which are not connected to multiple free nodes.
This reduces the size of the problem, although you need to keep 2 vectors that point to the original node and element numbers if you compress the numbering to only those active nodes and elements. ( that�s 7 vectors)
You also need further vectors for:
- the node weightings
- the element weightings
- the best node order
- the next possible node order
- the next possible element order
- status vectors for each node and element as to how they have progressed through the front.
That�s 14 vectors and I use a few extra as I go along. The good thing is these are all 4-byte vectors and the storage required is insignificant compared to the storage required for the final equation matrix.
The use of ALLOCATE works very well with this, as all vectors can be sized before you start.
(The size of LNODE can be scanned from the element connectivity info before you start, using IELEM to first store the count of nodes as you proceed, noting active nodes and elements. It can be all Fortran standard conforming, removing all the memory management tricks of F77)
After 30 years, I'm still changing the code from time to time, most recently to be standard conforming F95. Your graphics has got me to thinking that I should place it in a %gr window and draw the order for each iteration as I go through the list of possible starting nodes. It might identify different ways of defining the node and element weights.
Generating the starting node list is the other half of this very interesting auto-ordering problem !
John |
|
Back to top |
|
 |
jjgermis
Joined: 21 Jun 2006 Posts: 404 Location: N�rnberg, Germany
|
Posted: Mon Aug 27, 2012 8:37 am Post subject: |
|
|
Hi John,
thanks for your ideas. This is very exciting! This motivates me to finalize my "cleaning and document the code" project. Once everthing is working "again" I am going to try all this out. With the working copy I can test the differences between changes.
I liked you comment on codes changes Our air gap code I change all the time - one always find new possibilties in the programming language.
What is your experience on not programming code in colum-major. For example: Fortran is a column-major language. The GPS code is programmed in a rather row-major. For small programms one will note notice the difference. However, for large meshes this could perhaps be an issue. However, I do not know wheter this is the case with current computers - and reprogramming the code just to check is to much effort. Do you have any experiene on this? |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2621 Location: Sydney
|
Posted: Tue Aug 28, 2012 1:53 am Post subject: |
|
|
You asked:
Quote: |
What is your experience on not programming code in colum-major. For example: Fortran is a column-major language. The GPS code is programmed in a rather row-major. For small programms one will note notice the difference. However, for large meshes this could perhaps be an issue. However, I do not know wheter this is the case with current computers - and reprogramming the code just to check is to much effort. Do you have any experiene on this? |
I have nearly 40 years experience investigating efficiency of numerical calculations with the compiler available at the time. Unfortunately, as compilers change, the experience becomes obsolete, but thinking in terms of trying to address memory sequentially, (as columns in Fortran) is a good idea, but should not be taken to extremes.
Your question could relate to a number of issues about the order of addressing a 2D array or sequential use of vitrual memory ?
Using an addressing sequence such as
DO i = 1,n
DO j = 1,m
array(j,i) = {calculation}
end do
end do
This offers a number of possible benefits, but is definately not essential.
Possible advantages to take include:
You can replace the inner loop, such as using DOT_PRODUCT or some other array syntax.
The sequential addressing of memory improves performance. This might become significant, such as for cacheing to the processor or for large arrays for virtual memory. ( the description "large" relates to the amount of physical memory installed on the PC )
Consider the multiplication of 2 2D matrices : [C] = [A] x [B]
This can be written more "efficiently" if [A] is stored as [A]' (transpose)
The other main issue to consider is documentation and readibility. If the algorithm on which the calculation is based, is row based, it might make sense to write the code so that it looks like the algorithm. ( or change the algorithm so that it looks column based). Documenting and using more readable names for variables reduces bugs and makes the code easier to support. Coming back to code that is confusing wastes a lot of time.
My approach is to break the calculation up into smaller procedures, using the procedure structure to document the code flow and try to optimise the inner routines. Key inner procedures may consist of only 3 or 4 lines of calculation.
You are correct to identify that column based coding is more suitable for Fortran. The best way to address this is when you are defining the data structure you are using. Over the last 10 years, I have experimented with TYPE data structures. I use these extensively for data tables, but tend to retain (column centric) arrays for matrix calculations.
If your arrays are small, say up to A(100,100), if you go to all the effort of changing the index order, you will probably find no run time improvement. The benefit that will come from reviewing the data structures, will be to improve the way you can address the solution you are trying to achieve and probably solve other related problems.
I hope this mix of ideas helps !
John |
|
Back to top |
|
 |
jjgermis
Joined: 21 Jun 2006 Posts: 404 Location: N�rnberg, Germany
|
Posted: Wed Aug 29, 2012 7:39 am Post subject: |
|
|
Hi John,
thanks for your excellent comments and ideas!
I have taken a look at our code and your comment below applies. In many cases the code and the documentation is the other way arround. This is really very confusing and something ons should take care of. The stiffness matrix for our air gap element is such an example. Since this is a dense matrix your suggestion to make use of array syntax on the inner loop might improve the performance.
Quote: |
The other main issue to consider is documentation and readibility. If the algorithm on which the calculation is based, is row based, it might make sense to write the code so that it looks like the algorithm. ( or change the algorithm so that it looks column based). Documenting and using more readable names for variables reduces bugs and makes the code easier to support. Coming back to code that is confusing wastes a lot of time. |
By the way the Zienkiewicz code for the fourth edition,pcfeap, must be the upgrade to the miniFEM in the third edition. This is also a good example of improvements due to new language options and possibilities. Unfortunately one needs to change the code a little bit to be able to use with FTN95. |
|
Back to top |
|
 |
jalih
Joined: 30 Jul 2012 Posts: 196
|
Posted: Wed Aug 29, 2012 4:08 pm Post subject: Re: |
|
|
JohnCampbell wrote: |
You are correct to identify that column based coding is more suitable for Fortran.
|
Using arrays was the first thing I got totally wrong with Fortran. After writing about 300 lines of code for my Tetris game, I then realized my game blocks looked a little bit funny (my data was in row-major order).
I think array initialization is quite complex on Fortran.
In PL/I, I can just write:
Code: |
dcl A (3,4) float init (1,2,3,4,5,6,7,8,9,10,11,12); |
and let compiler do the math.
Does Fortran have something similar to PL/I's iSUB defining?
Code: |
dcl A (m,n) float;
dcl B (n,m) float defined (A(2sub, 1sub));
|
Above, matrix B would be defined to be transpose of the matrix A. |
|
Back to top |
|
 |
jjgermis
Joined: 21 Jun 2006 Posts: 404 Location: N�rnberg, Germany
|
Posted: Wed Aug 29, 2012 7:02 pm Post subject: |
|
|
Hi John,
how is your visualization with %gr coming on? Unfortunately I am not that fit in Clearwin. Since I use eps in my documents I prefer to do all figures in pure eps rather than converting it. However, I think it should be easy to do it in Clearwin.
Coming back to the numbering: For the same example I only changed the node numbering manually. In this small example one can clearly see that the solution is independent of the node numbering. The profile for the second numbering scheme is larger than the first one. For problems with many nodes this is, as you know, critical for a direct solver - compact storage scheme by Alan Jennings.
Code: |
subroutine spy(a,m,n)
implicit none
C
include 'split_file.ins'
C
integer,dimension(m,n) :: a
integer,dimension(m) :: numbering
real,parameter :: cell_size=10.0
real,parameter :: font_size=0.8*cell_size
real,parameter :: r= 0.35*font_size
integer,parameter :: io=33,tex=35
real :: x_lo,x_hi,y_lo,y_hi,xdiag
real :: scale,x,y
integer :: m,n,i,j
x_lo = 0;
y_lo = 0;
x_hi = m*cell_size;
y_hi = m*cell_size;
C
C Open the eps file and define the bounding box
C
FileToWrite = trim(path)//trim(name)//'-spy.eps'
open(io,file=FileToWrite);
write(io,'(A)') '%!PS-Adobe-1.0'
if (cell_size*real(n) > 595.0) then
scale = 595.0/(cell_size*real(n));
write(io,'(A,4I5)') '%%BoundingBox:',0,0,595,595
write(io,'(2F13.6,1X,A)') 0.0,0.0,'translate'
write(io,'(2F13.6,1X,A)') scale,scale,'scale'
else
write(io,'(A,4I5)') '%%BoundingBox:',
* int(x_lo+0.5),int(y_lo+0.5),int(x_hi+0.5),int(y_hi+0.5)
endif
write(io,'(2F13.6,2x,A)') x_lo,y_lo,'newpath moveto'
write(io,'(2F13.6,2x,A)') x_hi,y_lo,'lineto'
write(io,'(2F13.6,2x,A)') x_hi,y_hi,'lineto'
write(io,'(2F13.6,2x,A)') x_lo,y_hi,'lineto'
write(io,'(2F13.6,2x,A)') x_lo,y_lo,'lineto'
write(io,'(A)') 'stroke'
C
C Plot the nonzero matrix entries
C
do i=1,m
C First the diagonal element is plotted.
write(io,'(A)') '0 setgray'
x = x_lo + (real(i) - 0.5)*cell_size;
y = y_hi - (real(i) - 0.5)*cell_size;
write(io,'(3F13.6,2x,A)') x,y,r,'0 360 arc closepath'
write(io,'(A)') 'gsave 1 0 0 setrgbcolor fill grestore'
write(io,'(A)') 'stroke'
xdiag = x
do j=1,n
if (a(i,j).NE. 0) then
x = x_lo + (real(a(i,j)) - 0.5)*cell_size;
y = y_hi - (real(i) - 0.5)*cell_size;
write(io,'(3F13.6,2I5,2x,A)') x,y,r,0,360,'arc closepath'
if (x.LT.xdiag) then
write(io,'(A,1X,3I3,1X,A)') 'gsave',0,0,1,
* ' setrgbcolor fill grestore'
else
write(io,'(A,1X,3I3,1X,A)') 'gsave',0,1,0,
* ' setrgbcolor fill grestore'
endif
write(io,'(A)') 'stroke'
endif
enddo
enddo
write(io,'(A)') 'showpage'
write(io,'(A)') '%%EOF'
close(io);
return
end subroutine spy |
Last edited by jjgermis on Thu Aug 30, 2012 3:32 pm; edited 1 time in total |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2621 Location: Sydney
|
Posted: Thu Aug 30, 2012 5:34 am Post subject: |
|
|
Jalih,
You said:
Quote: |
I think array initialization is quite complex on Fortran.
|
Although I am not familiar with PL/I, I am not sure why you find initialisation of a fortran array to be complex?
There are a number of ways to initialise a 2D array. You could consider using:
DATA statement, or
array syntax, or
DO loops, or
FORALL structure, or
RESHAPE intrinsic syntax.
The following example is taken from Lahey documentation, which shows how a single vector can be re-shaped into a 2D array.
Code: |
Example
x = reshape((/1,2,3,4/), (/3,2/), pad=(/0/))
! x is assigned |1 4|
! |2 0|
! |3 0|
|
Depending on either a column or row based storage, I don't see how this contributes to the complexity. Just use what is required.
If you need to think in columns, rather than rows, it is not that hard to copy a vector to a 2D array, as in
Code: |
k = 0
do i = 1,n
do j = 1,m
k = k+1
if (k > size(vector) then
array(i,j) = default
else
array(i,j) = vector(k)
end if
end do
end do |
I hope this gives you some options to investigate.
John |
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|