- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I write a finite volume code dealing with the flow of water in a porous media. For that, I have the following objects :
fluidProperties
This object contains the arrays (on value for each finite volume) of pressure and a lot of quantities that depends on pressure (like density, viscosity, etc..).
rockProperties
This object contains properties for the rock.
Obviously, one need to solve an equation where we need to have access to the value of these arrays in both objects. For the time being, I have a lot of getters to retrieve the values, but it seems that using too many getters is always a sign of bad software design. Does any one of you have something better to propose ?
Best regards,
Francois
Link Copied
8 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Providing some sample code with type information might be useful.
Jim Dempsey
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here is an example :
[fortran]module fluidProperties_class
implicit none
private
public :: fluidProperties
type fluidProperties
private
real, allocatable, dimension(:) :: p
real, allocatable, dimension(:) :: rho
real, allocatable, dimension(:) :: mu
contains
procedure, public :: init => init_fluidProperties
procedure, public :: getP => getP_fluidProperties
procedure, public :: getRho => getRho_fluidProperties
procedure, public :: getMu => getMu_fluidProperties
end type fluidProperties
contains
subroutine init_fluidProperties(this, size, pressure, density, viscosity)
class(fluidProperties) :: this
integer, intent(in) :: size
real, intent(in) :: pressure, density, viscosity
allocate(this%p(1:size))
allocate(this%rho(1:size))
allocate(this%mu(1:size))
this%p = pressure
this%rho = density
this%mu = viscosity
end subroutine init_fluidProperties
function getP_fluidProperties(this, k)
class(fluidProperties) :: this
integer, intent(in) :: k
real :: getP_fluidProperties
getP_fluidProperties = this%p(k)
end function getP_fluidProperties
function getRho_fluidProperties(this, k)
class(fluidProperties) :: this
integer, intent(in) :: k
real :: getRho_fluidProperties
getRho_fluidProperties = this%rho(k)
end function getRho_fluidProperties
function getMu_fluidProperties(this, k)
class(fluidProperties) :: this
integer, intent(in) :: k
real :: getMu_fluidProperties
getMu_fluidProperties = this%mu(k)
end function getMu_fluidProperties
end module fluidProperties_class
module rockProperties_class
implicit none
private
public :: rockProperties
type rockProperties
private
real, allocatable, dimension(:) :: phi
real, allocatable, dimension(:) :: k
contains
procedure, public :: init => init_rockProperties
procedure, public :: getPhi => getPhi_rockProperties
procedure, public :: getK => getK_rockProperties
end type rockProperties
contains
subroutine init_rockProperties(this, size, porosity, permeability)
class(rockProperties) :: this
integer, intent(in) :: size
real, intent(in) :: porosity, permeability
allocate(this%phi(1:size))
allocate(this%k(1:size))
this%phi = porosity
this%k = permeability
end subroutine init_rockProperties
function getPhi_rockProperties(this, k)
class(rockProperties) :: this
integer, intent(in) :: k
real :: getPhi_rockProperties
getPhi_rockProperties = this%phi(k)
end function getPhi_rockProperties
function getK_rockProperties(this, k)
class(rockProperties) :: this
integer, intent(in) :: k
real :: getK_rockProperties
getK_rockProperties = this%k(k)
end function getK_rockProperties
end module rockProperties_class
module equation_class
use rockProperties_class
implicit none
private
public :: equation
type equation
private
integer :: size
type(rockProperties) :: myRockProperties
contains
procedure, public :: init => init_equation
procedure, public :: computeSum => computeSum_equation
end type equation
contains
subroutine init_equation(this, size)
class(equation) :: this
integer, intent(in) :: size
call this%myRockProperties%init(size, 0.3, 1.0e-12)
this%size = size
end subroutine init_equation
function computeSum_equation(this, myFluidProperties)
use fluidProperties_class
class(equation) :: this
class(fluidProperties), intent(in) :: myFluidProperties
integer :: k
real :: sum, computeSum_equation
sum = 0.0
do k = 1, this%size
sum = sum + this%myRockProperties%getK(k) * myFluidProperties%getP(k)
end do
computeSum_equation = sum
end function computeSum_equation
end module equation_class
program main
use fluidProperties_class
use equation_class
implicit none
integer :: nbCells = 10
type(equation) :: myEquation
type(fluidProperties) :: myFluidProperties
call myFluidProperties%init(nbCells, 1.0e+5, 1000.0, 5.0e-4)
call myEquation%init(nbCells)
write (*,'(es15.5)') myEquation%computeSum(myFluidProperties)
end program main[/fortran] - Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
We have a program that is written in a very similar style, using a UML tool which generates part of the code, especially getters and setters,so I know your issue very well.
One thing that can be done is to declare the most frequently used arrays public instead of private. That allows direct access from outside. Obviously this breaksthe OO law. But it will get you better performance.
The other thing to try for that sort of code is interprocedural optimization (-ipo) which allows the compiler to do inlining across source file boundaries. That can reduce or eliminate the usual overhead that comes with getters.
In general I think that performance critical components like solvers should not be writtenthat style. For such codes OO does not add a lot of valueand isexpensive from the performance perspective. For GUI stuff that's certainly o.k.
One thing that can be done is to declare the most frequently used arrays public instead of private. That allows direct access from outside. Obviously this breaksthe OO law. But it will get you better performance.
The other thing to try for that sort of code is interprocedural optimization (-ipo) which allows the compiler to do inlining across source file boundaries. That can reduce or eliminate the usual overhead that comes with getters.
In general I think that performance critical components like solvers should not be writtenthat style. For such codes OO does not add a lot of valueand isexpensive from the performance perspective. For GUI stuff that's certainly o.k.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
mriedman,
Thanks for your reply. I have a few questions :
- Performance : I don't really understand this issue. I agree that making a variable public will give you a direct access to the components of the vector, eliminating the need for a getter. But if you write a getter, I am sure that ifort is going to inline it if you use interprocedural optimization, and you will get the same performance.
- UML Tool : Do you have such a tool that generates Fortran ?
Francois
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Performance: Your are right about -ipo. But most developers don't use aggressive optimizations like this. Most Fortran applications Ihave seenare built with only -O. In fact that is mostly sufficient for classic F77 style codes.
UML Tool: We use Objecteering. Some folks say that we are the only customer using Objecteering with (customized) F95 code generation. In fact they don't advertize it, but we have it and it's supported.
Michael
UML Tool: We use Objecteering. Some folks say that we are the only customer using Objecteering with (customized) F95 code generation. In fact they don't advertize it, but we have it and it's supported.
Michael
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I agree with mriedman, there is no advantage in keeping the arrays private with get/put member functions (at least as indicated from your sketch code). Then your sample sum subroutine could directly use the arrays and presumably take advantage of SSE/AVX.
Jim Dempsey
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Jim,
Do you mean that the compiler can't vectorize things if we use the getters ?Francois
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IPO should be able to do inlining provided that the source to your modules is available. As to if inlineing makes the code vectorizable this may depend upon if your functions are PURE and ELEMENTAL.
Try
!DEC$ ATTRIBUTES INLINE :: Getter
PURE ELEMENTAL Function Getter(this,i)
type(YourType) :: this
REAL :: Getter
integer :: i
Getter = this%YourArray(i)
end Function Getter
*** Untested code ***
Use IPO with optimization (i.e. not Debug build)
Jim Dempsey
Try
!DEC$ ATTRIBUTES INLINE :: Getter
PURE ELEMENTAL Function Getter(this,i)
type(YourType) :: this
REAL :: Getter
integer :: i
Getter = this%YourArray(i)
end Function Getter
*** Untested code ***
Use IPO with optimization (i.e. not Debug build)
Jim Dempsey
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page