Commit 267870a9 authored by Jorn Bruggeman's avatar Jorn Bruggeman
Browse files

python driver: added logic to read couplings

parent dce8df27
......@@ -2,7 +2,7 @@
!-----------------------------------------------------------------------
!BOP
!
! !MODULE: Helper fort the Python interface to FABM. This functionality may move to the FABM core in time.
! !MODULE: Helper for the Python interface to FABM. This functionality may move to the FABM core in time.
!
! !INTERFACE:
module fabm_python_helper
......@@ -18,7 +18,7 @@
private
public get_environment_metadata
public get_environment_metadata, get_couplings, get_suitable_masters
!EOP
!-----------------------------------------------------------------------
......@@ -95,7 +95,73 @@
end if
link => link%next
end do
end subroutine
end subroutine get_environment_metadata
subroutine get_couplings(model,link_list)
type (type_model), intent(inout) :: model
type (type_link_list),intent(inout) :: link_list
type (type_link),pointer :: link,link2
call link_list%finalize()
link => model%root%links%first
do while (associated(link))
if (link%original%presence/=presence_internal.and..not.link%original%read_indices%is_empty()) then
link2 => link_list%append(link%target,link%name)
link2%original => link%original
end if
link => link%next
end do
end subroutine get_couplings
function get_suitable_masters(model,slave) result(link_list)
type (type_model), intent(inout) :: model
class (type_internal_variable), pointer :: slave
type (type_link_list),pointer :: link_list
type (type_link),pointer :: link,link2
logical :: use
allocate(link_list)
write (*,*) trim(slave%name)
link => model%root%links%first
do while (associated(link))
! Coupled variables cannot serve as master
use = .false.
if (associated(link%target,link%original) & ! Uncoupled
.and..not.associated(link%target,slave) & ! Not self
.and..not.(link%original%state_indices%is_empty().and..not.slave%state_indices%is_empty())) then ! And statet variable if state is state variable
select type (slave)
class is (type_bulk_variable)
write (*,*) trim(slave%name),'bulk'
select type (variable=>link%target)
class is (type_bulk_variable)
use = .true.
end select
class is (type_horizontal_variable)
write (*,*) trim(slave%name),'horizontal'
select type (variable=>link%target)
class is (type_horizontal_variable)
use = .true.
end select
class is (type_scalar_variable)
write (*,*) trim(slave%name),'scalar'
select type (variable=>link%target)
class is (type_scalar_variable)
use = .true.
end select
end select
write (*,*) trim(link%name),use
end if
if (use) then
!write (*,*) link%name
link2 => link_list%append(link%target,link%name)
link2%original => link%original
end if
link => link%next
end do
end function get_suitable_masters
end module fabm_python_helper
......
......@@ -24,11 +24,14 @@ fabm = ctypes.CDLL(dllpath)
# Specify arguments and return types for FABM interfaces.
fabm.initialize.argtypes = [ctypes.c_char_p]
fabm.get_variable_counts.argtypes = [ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int)]
fabm.get_variable_counts.argtypes = [ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int)]
fabm.get_variable_metadata.argtypes = [ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.c_char_p,ctypes.c_char_p,ctypes.c_char_p,ctypes.c_char_p]
fabm.get_variable_metadata_ptr.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_char_p,ctypes.c_char_p,ctypes.c_char_p]
fabm.get_variable_long_path.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_char_p]
fabm.get_parameter_metadata.argtypes = [ctypes.c_int,ctypes.c_int,ctypes.c_char_p,ctypes.c_char_p,ctypes.c_char_p,ctypes.POINTER(ctypes.c_int),ctypes.POINTER(ctypes.c_int)]
fabm.get_dependency_metadata.argtypes = [ctypes.c_int,ctypes.c_int,ctypes.c_char_p,ctypes.c_char_p]
fabm.get_model_metadata.argtypes = [ctypes.c_char_p,ctypes.c_int,ctypes.c_char_p]
fabm.get_coupling.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_void_p),ctypes.POINTER(ctypes.c_void_p)]
fabm.get_real_parameter.argtypes = [ctypes.c_int,ctypes.c_int]
fabm.get_real_parameter.restype = ctypes.c_double
fabm.get_integer_parameter.argtypes = [ctypes.c_int,ctypes.c_int]
......@@ -48,6 +51,10 @@ fabm.link_dependency_data.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_doubl
fabm.get_bulk_diagnostic_data.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.POINTER(ctypes.c_double))]
fabm.get_horizontal_diagnostic_data.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.POINTER(ctypes.c_double))]
fabm.get_rates.argtypes = [numpy.ctypeslib.ndpointer(dtype=ctypes.c_double, ndim=1, flags='CONTIGUOUS')]
fabm.get_suitable_masters_for_ptr.argtypes = [ctypes.c_void_p]
fabm.get_suitable_masters_for_ptr.restype = ctypes.c_void_p
fabm.link_list_count.argtypes = [ctypes.c_void_p]
fabm.link_list_count.restype = ctypes.c_int
BULK_STATE_VARIABLE = 1
SURFACE_STATE_VARIABLE = 2
......@@ -111,6 +118,11 @@ class Variable(object):
if path is None: path = name
self.long_name = long_name
self.path = path
@property
def long_path(self):
return self.long_name
def getOptions(self):
pass
class Dependency(Variable):
def __init__(self,name,index,units=None,long_name=None):
......@@ -204,6 +216,40 @@ class Parameter(Variable):
value = property(getValue, setValue)
default = property(getDefault)
class Coupling(Variable):
def __init__(self,index):
self.master = ctypes.c_void_p()
self.slave = ctypes.c_void_p()
fabm.get_coupling(index,ctypes.byref(self.slave),ctypes.byref(self.master))
strname = ctypes.create_string_buffer(ATTRIBUTE_LENGTH)
strunits = ctypes.create_string_buffer(ATTRIBUTE_LENGTH)
strlong_name = ctypes.create_string_buffer(ATTRIBUTE_LENGTH)
fabm.get_variable_metadata_ptr(self.slave,ATTRIBUTE_LENGTH,strname,strunits,strlong_name)
Variable.__init__(self,strname.value,'',strlong_name.value)
def getValue(self):
strlong_name = ctypes.create_string_buffer(ATTRIBUTE_LENGTH)
fabm.get_variable_long_path(self.master,ATTRIBUTE_LENGTH,strlong_name)
return strlong_name.value
def setValue(self,value):
# TBI
pass
def getOptions(self):
list = fabm.get_suitable_masters_for_ptr(self.slave)
print fabm.link_list_count(list)
@property
def long_path(self):
strlong_name = ctypes.create_string_buffer(ATTRIBUTE_LENGTH)
fabm.get_variable_long_path(self.slave,ATTRIBUTE_LENGTH,strlong_name)
return strlong_name.value
value = property(getValue, setValue)
class Model(object):
def __init__(self,path='fabm.yaml'):
fabm.initialize(path)
......@@ -236,9 +282,10 @@ class Model(object):
nconserved = ctypes.c_int()
ndependencies = ctypes.c_int()
nparameters = ctypes.c_int()
ncouplings = ctypes.c_int()
fabm.get_variable_counts(ctypes.byref(nstate_bulk),ctypes.byref(nstate_surface),ctypes.byref(nstate_bottom),
ctypes.byref(ndiag_bulk),ctypes.byref(ndiag_horizontal),
ctypes.byref(nconserved),ctypes.byref(ndependencies),ctypes.byref(nparameters))
ctypes.byref(nconserved),ctypes.byref(ndependencies),ctypes.byref(nparameters),ctypes.byref(ncouplings))
# Allocate memory for state variable values, and send ctypes.pointer to this memory to FABM.
self.state = numpy.empty((nstate_bulk.value+nstate_surface.value+nstate_bottom.value,),dtype=float)
......@@ -289,6 +336,8 @@ class Model(object):
fabm.get_dependency_metadata(i+1,ATTRIBUTE_LENGTH,strname,strunits)
self.dependencies.append(Dependency(strname.value,i,units=strunits.value))
self.couplings = [Coupling(i+1) for i in range(ncouplings.value)]
# Arrays that combine variables from pelagic and boundary domains.
self.state_variables = self.bulk_state_variables + self.surface_state_variables + self.bottom_state_variables
self.diagnostic_variables = self.bulk_diagnostic_variables + self.horizontal_diagnostic_variables
......
......@@ -32,6 +32,9 @@ class Entry(object):
def addTree(self,arr,category=None):
for variable in arr:
pathcomps = variable.path.split('/')
if len(pathcomps)<2:
#print 'Skipping root level entity %s' % pathcomps[0]
continue
if category is not None: pathcomps = [pathcomps[0],category] + list(pathcomps[1:])
parent = self
for component in pathcomps[:-1]:
......@@ -52,19 +55,28 @@ class ItemModel(QtCore.QAbstractItemModel):
for d in self.model.dependencies: env.addChild(Entry(d,d.name))
root.addTree(self.model.parameters,'parameters')
root.addTree(self.model.state_variables,'initialization')
root.addTree(self.model.couplings,'coupling')
root.addChild(env)
# For all models, create an object that returns appropriate metadata.
class Submodel:
def __init__(self,long_name):
self.units = None
self.units_unicode = None
self.value = None
self.long_name = long_name
def processNode(n,prefix=()):
if isinstance(n.object,basestring) and prefix and prefix[-1] not in ('parameters','initialization','environment'):
n.object = Submodel(self.model.getModelLongName('/'.join(prefix)))
for child in n.children: processNode(child,prefix+(child.name,))
def processNode(n,path=()):
childprefix = path
if isinstance(n.object,basestring) and path:
if path[-1] in ('parameters','initialization','environment','coupling'):
childprefix = path[:-1]
else:
n.object = Submodel(self.model.getModelLongName('/'.join(path)))
for child in n.children: processNode(child,childprefix+(child.name,))
processNode(root)
if self.root is not None:
# We already have an old tree - compare and amend model.
def processChange(newnode,oldnode,parent):
oldnode.object = newnode.object
if not newnode.children: return
......@@ -94,7 +106,7 @@ class ItemModel(QtCore.QAbstractItemModel):
self.beginRemoveRows(parent,ioldstart,len(oldnode.children)-1)
for i in range(len(oldnode.children)-1,ioldstart-1,-1): oldnode.removeChild(i)
self.endRemoveRows()
# We already have an old tree - compare and amend model.
processChange(root,self.root,QtCore.QModelIndex())
else:
# First time a tree was created - store it and move on.
......@@ -144,9 +156,11 @@ class ItemModel(QtCore.QAbstractItemModel):
elif index.column()==3:
return entry.name
elif role==QtCore.Qt.ToolTipRole and index.parent().isValid():
if not isinstance(data,basestring): return '%s (%s)' % (data.long_name,entry.name)
if not isinstance(data,basestring): return data.long_path
elif role==QtCore.Qt.EditRole:
if not isinstance(data,basestring): return data.getValue()
if not isinstance(data,basestring):
print data.getOptions()
return data.getValue()
elif role==QtCore.Qt.FontRole and index.column()==1:
if isinstance(data,pyfabm.Parameter) and data.value!=data.default:
font = QtGui.QFont()
......
......@@ -17,7 +17,7 @@
use fabm
use fabm_config
use fabm_types, only:rk,attribute_length,type_model_list_node,type_base_model,factory
use fabm_types, only:rk,attribute_length,type_model_list_node,type_base_model,factory,type_link,type_link_list,type_internal_variable,type_bulk_variable
use fabm_driver, only: type_base_driver, driver
use fabm_properties
use fabm_python_helper
......@@ -37,6 +37,7 @@
class (type_model),private,pointer,save :: model => null()
real(8),dimension(:),pointer :: state
character(len=1024),dimension(:),allocatable :: environment_names,environment_units
type (type_link_list),save :: coupling_link_list
type (type_property_dictionary),save,private :: forced_parameters,forced_couplings
......@@ -103,6 +104,7 @@
! Retrieve arrays to hold values for environmental variables and corresponding metadata.
call get_environment_metadata(model,environment_names,environment_units)
call get_couplings(model,coupling_link_list)
end subroutine initialize
!EOC
......@@ -152,6 +154,8 @@
! Retrieve arrays to hold values for environmental variables and corresponding metadata.
call get_environment_metadata(model,environment_names,environment_units)
call get_couplings(model,coupling_link_list)
end subroutine
subroutine check_ready()
......@@ -173,11 +177,11 @@
end function
subroutine get_variable_counts(nstate_bulk,nstate_surface,nstate_bottom,ndiagnostic_bulk,ndiagnostic_horizontal,nconserved, &
ndependencies,nparameters) bind(c)
ndependencies,nparameters,ncouplings) bind(c)
!DIR$ ATTRIBUTES DLLEXPORT :: get_variable_counts
integer(c_int),intent(out) :: nstate_bulk,nstate_surface,nstate_bottom
integer(c_int),intent(out) :: ndiagnostic_bulk,ndiagnostic_horizontal
integer(c_int),intent(out) :: nconserved,ndependencies,nparameters
integer(c_int),intent(out) :: nconserved,ndependencies,nparameters,ncouplings
nstate_bulk = size(model%state_variables)
nstate_surface = size(model%surface_state_variables)
nstate_bottom = size(model%bottom_state_variables)
......@@ -186,6 +190,7 @@
nconserved = size(model%conserved_quantities)
ndependencies = size(environment_names)
nparameters = model%root%parameters%size()
ncouplings = coupling_link_list%count()
end subroutine
subroutine get_variable_metadata(category,index,length,name,units,long_name,path) bind(c)
......@@ -210,10 +215,10 @@
case (CONSERVED_QUANTITY)
variable => model%conserved_quantities(index)
end select
call copy_to_c_string(variable%name, name)
call copy_to_c_string(variable%units, units)
call copy_to_c_string(variable%long_name,long_name)
call copy_to_c_string(variable%path, path)
call copy_to_c_string(variable%name, name)
call copy_to_c_string(variable%units, units)
call copy_to_c_string(variable%local_long_name,long_name)
call copy_to_c_string(variable%path, path)
end subroutine
subroutine get_parameter_metadata(index,length,name,units,long_name,typecode,has_default) bind(c)
......@@ -253,6 +258,98 @@
call copy_to_c_string(environment_units(index),units)
end subroutine
subroutine get_coupling(index,slave,master) bind(c)
!DIR$ ATTRIBUTES DLLEXPORT :: get_coupling
integer(c_int), intent(in), value :: index
type (c_ptr), intent(out) :: slave,master
type (type_link),pointer :: link_slave
integer :: i
link_slave => coupling_link_list%first
do i=2,index
link_slave => link_slave%next
end do
slave = c_loc(link_slave%original)
master = c_loc(link_slave%target)
end subroutine
function get_suitable_masters_for_ptr(pvariable) result(plist) bind(c)
!DIR$ ATTRIBUTES DLLEXPORT :: get_suitable_masters_for_ptr
type (c_ptr), intent(in), value :: pvariable
type (c_ptr) :: plist
class (type_internal_variable),pointer :: variable
type (type_link_list), pointer :: list
call c_f_pointer(pvariable, variable)
list => get_suitable_masters(model,variable)
plist = c_loc(list)
end function
function link_list_count(plist) bind(c) result(value)
!DIR$ ATTRIBUTES DLLEXPORT :: link_list_count
type (c_ptr), intent(in), value :: plist
integer(c_int) :: value
type (type_link_list),pointer :: list
call c_f_pointer(plist, list)
value = list%count()
end function
function link_list_index(plist,index) bind(c) result(pvariable)
!DIR$ ATTRIBUTES DLLEXPORT :: link_list_index
type (c_ptr), intent(in), value :: plist
integer(c_int),intent(in), value :: index
type (c_ptr) :: pvariable
type (type_link_list),pointer :: list
type (type_link), pointer :: link
integer :: i
call c_f_pointer(plist, list)
link => list%first
do i=2,index
link => link%next
end do
pvariable = c_loc(link%target)
end function
subroutine get_variable_long_path(pvariable,length,long_name) bind(c)
!DIR$ ATTRIBUTES DLLEXPORT :: get_variable_long_path
type (c_ptr), intent(in), value :: pvariable
integer(c_int), intent(in), value :: length
character(kind=c_char),intent(out),dimension(length) ::long_name
class (type_internal_variable),pointer :: variable
class (type_base_model), pointer :: owner
character(len=attribute_length) :: long_name_
call c_f_pointer(pvariable, variable)
long_name_ = variable%long_name
owner => variable%owner
do while (associated(owner%parent))
long_name_ = trim(owner%long_name)//'/'//trim(long_name_)
owner => owner%parent
end do
call copy_to_c_string(long_name_,long_name)
end subroutine get_variable_long_path
subroutine get_variable_metadata_ptr(pvariable,length,name,units,long_name) bind(c)
!DIR$ ATTRIBUTES DLLEXPORT :: get_variable_metadata_ptr
type (c_ptr), intent(in), value :: pvariable
integer(c_int), intent(in), value :: length
character(kind=c_char),intent(out),dimension(length) :: name,units,long_name
class (type_internal_variable),pointer :: variable
call c_f_pointer(pvariable, variable)
call copy_to_c_string(variable%name, name)
call copy_to_c_string(variable%units, units)
call copy_to_c_string(variable%long_name,long_name)
end subroutine get_variable_metadata_ptr
subroutine get_model_metadata(name,length,long_name) bind(c)
!DIR$ ATTRIBUTES DLLEXPORT :: get_model_metadata
character(kind=c_char),intent(in), target :: name(*)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment