Source code for cil.optimisation.operators.FiniteDifferenceOperator
# Copyright 2019 United Kingdom Research and Innovation# Copyright 2019 The University of Manchester## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.## Authors:# CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txtimportnumpyasnpfromcil.optimisation.operatorsimportLinearOperatorfromcil.utilities.errorsimportInPlaceError
[docs]classFiniteDifferenceOperator(LinearOperator):r''' Computes forward/backward/centered finite differences of a DataContainer under Neumann/Periodic boundary conditions :param domain_geometry: Domain geometry for the FiniteDifferenceOperator :param direction: Direction to evaluate finite differences :type direction: string label from domain geometry or integer number :param method: Method for finite differences :type method: 'forward', 'backward', 'centered' :param bnd_cond: 'Neumann', 'Periodic' '''def__init__(self,domain_geometry,range_geometry=None,direction=None,method='forward',bnd_cond='Neumann'):ifisinstance(direction,int):ifdirection>len(domain_geometry.shape)ordirection<0:raiseValueError('Requested direction is not possible. Accepted direction {}, \ngot {}'.format(range(len(domain_geometry.shape)),direction))else:self.direction=directionelse:ifdirectionindomain_geometry.dimension_labels:self.direction=domain_geometry.dimension_labels.index(direction)else:raiseValueError('Requested direction is not possible. Accepted direction is {} or {}, \ngot {}'.format(domain_geometry.dimension_labels,range(len(domain_geometry.shape)),direction))#get voxel spacing, if not use 1stry:self.voxel_size=domain_geometry.spacing[self.direction]except:self.voxel_size=1self.boundary_condition=bnd_condself.method=method# Domain Geometry = Range Geometry if not statedifrange_geometryisNone:range_geometry=domain_geometrysuper(FiniteDifferenceOperator,self).__init__(domain_geometry=domain_geometry,range_geometry=range_geometry)self.size_dom_gm=len(domain_geometry.shape)ifself.voxel_size<=0:raiseValueError(' Need a positive voxel size ')# check direction and "length" of geometryifself.direction+1>self.size_dom_gm:raiseValueError('Finite differences direction {} larger than geometry shape length {}'.format(self.direction+1,self.size_dom_gm))defget_slice(self,start,stop,end=None):tmp=[slice(None)]*self.size_dom_gmtmp[self.direction]=slice(start,stop,end)returntmp
[docs]defdirect(self,x,out=None):ifid(x)==id(out):raiseInPlaceError(message="FiniteDifferenceOperator.direct cannot be used in place")x_asarr=x.as_array()outnone=FalseifoutisNone:outnone=Trueret=self.domain_geometry().allocate()outa=ret.as_array()else:outa=out.as_array()outa[:]=0############################################################################################ Forward differences ####################################################################################################ifself.method=='forward':# interior nodesnp.subtract(x_asarr[tuple(self.get_slice(2,None))], \
x_asarr[tuple(self.get_slice(1,-1))], \
out=outa[tuple(self.get_slice(1,-1))])ifself.boundary_condition=='Neumann':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(1,2))],\
x_asarr[tuple(self.get_slice(0,1))],out=outa[tuple(self.get_slice(0,1))])elifself.boundary_condition=='Periodic':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(1,2))],\
x_asarr[tuple(self.get_slice(0,1))],out=outa[tuple(self.get_slice(0,1))])# right boundarynp.subtract(x_asarr[tuple(self.get_slice(0,1))],\
x_asarr[tuple(self.get_slice(-1,None))],out=outa[tuple(self.get_slice(-1,None))])else:raiseValueError('Not implemented')############################################################################################ Backward differences ###################################################################################################elifself.method=='backward':# interior nodesnp.subtract(x_asarr[tuple(self.get_slice(1,-1))], \
x_asarr[tuple(self.get_slice(0,-2))], \
out=outa[tuple(self.get_slice(1,-1))])ifself.boundary_condition=='Neumann':# right boundarynp.subtract(x_asarr[tuple(self.get_slice(-1,None))], \
x_asarr[tuple(self.get_slice(-2,-1))], \
out=outa[tuple(self.get_slice(-1,None))])elifself.boundary_condition=='Periodic':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(0,1))],\
x_asarr[tuple(self.get_slice(-1,None))],out=outa[tuple(self.get_slice(0,1))])# right boundarynp.subtract(x_asarr[tuple(self.get_slice(-1,None))],\
x_asarr[tuple(self.get_slice(-2,-1))],out=outa[tuple(self.get_slice(-1,None))])else:raiseValueError('Not implemented')############################################################################################ Centered differences ###################################################################################################elifself.method=='centered':# interior nodesnp.subtract(x_asarr[tuple(self.get_slice(2,None))], \
x_asarr[tuple(self.get_slice(0,-2))], \
out=outa[tuple(self.get_slice(1,-1))])outa[tuple(self.get_slice(1,-1))]/=2.ifself.boundary_condition=='Neumann':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(1,2))], \
x_asarr[tuple(self.get_slice(0,1))], \
out=outa[tuple(self.get_slice(0,1))])outa[tuple(self.get_slice(0,1))]/=2.# left boundarynp.subtract(x_asarr[tuple(self.get_slice(-1,None))], \
x_asarr[tuple(self.get_slice(-2,-1))], \
out=outa[tuple(self.get_slice(-1,None))])outa[tuple(self.get_slice(-1,None))]/=2.elifself.boundary_condition=='Periodic':pass# left boundarynp.subtract(x_asarr[tuple(self.get_slice(1,2))], \
x_asarr[tuple(self.get_slice(-1,None))], \
out=outa[tuple(self.get_slice(0,1))])outa[tuple(self.get_slice(0,1))]/=2.# left boundarynp.subtract(x_asarr[tuple(self.get_slice(0,1))], \
x_asarr[tuple(self.get_slice(-2,-1))], \
out=outa[tuple(self.get_slice(-1,None))])outa[tuple(self.get_slice(-1,None))]/=2.else:raiseValueError('Not implemented')else:raiseValueError('Not implemented')ifself.voxel_size!=1.0:outa/=self.voxel_sizeifoutnone:ret.fill(outa)returnretelse:out.fill(outa)
[docs]defadjoint(self,x,out=None):ifid(x)==id(out):raiseInPlaceError# Adjoint operation defined asx_asarr=x.as_array()outnone=FalseifoutisNone:outnone=Trueret=self.range_geometry().allocate()outa=ret.as_array()else:outa=out.as_array()outa[:]=0############################################################################################ Forward differences ####################################################################################################ifself.method=='forward':# interior nodesnp.subtract(x_asarr[tuple(self.get_slice(1,-1))], \
x_asarr[tuple(self.get_slice(0,-2))], \
out=outa[tuple(self.get_slice(1,-1))])ifself.boundary_condition=='Neumann':# left boundaryouta[tuple(self.get_slice(0,1))]=x_asarr[tuple(self.get_slice(0,1))]# right boundaryouta[tuple(self.get_slice(-1,None))]=-x_asarr[tuple(self.get_slice(-2,-1))]elifself.boundary_condition=='Periodic':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(0,1))],\
x_asarr[tuple(self.get_slice(-1,None))],out=outa[tuple(self.get_slice(0,1))])# right boundarynp.subtract(x_asarr[tuple(self.get_slice(-1,None))],\
x_asarr[tuple(self.get_slice(-2,-1))],out=outa[tuple(self.get_slice(-1,None))])else:raiseValueError('Not implemented')############################################################################################ Backward differences ###################################################################################################elifself.method=='backward':# interior nodesnp.subtract(x_asarr[tuple(self.get_slice(2,None))], \
x_asarr[tuple(self.get_slice(1,-1))], \
out=outa[tuple(self.get_slice(1,-1))])ifself.boundary_condition=='Neumann':# left boundaryouta[tuple(self.get_slice(0,1))]=x_asarr[tuple(self.get_slice(1,2))]# right boundaryouta[tuple(self.get_slice(-1,None))]=-x_asarr[tuple(self.get_slice(-1,None))]elifself.boundary_condition=='Periodic':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(1,2))],\
x_asarr[tuple(self.get_slice(0,1))],out=outa[tuple(self.get_slice(0,1))])# right boundarynp.subtract(x_asarr[tuple(self.get_slice(0,1))],\
x_asarr[tuple(self.get_slice(-1,None))],out=outa[tuple(self.get_slice(-1,None))])else:raiseValueError('Not implemented')############################################################################################ Centered differences ###################################################################################################elifself.method=='centered':# interior nodesnp.subtract(x_asarr[tuple(self.get_slice(2,None))], \
x_asarr[tuple(self.get_slice(0,-2))], \
out=outa[tuple(self.get_slice(1,-1))])outa[tuple(self.get_slice(1,-1))]/=2.0ifself.boundary_condition=='Neumann':# left boundarynp.add(x_asarr[tuple(self.get_slice(0,1))],\
x_asarr[tuple(self.get_slice(1,2))],out=outa[tuple(self.get_slice(0,1))])outa[tuple(self.get_slice(0,1))]/=2.0# right boundarynp.add(x_asarr[tuple(self.get_slice(-1,None))],\
x_asarr[tuple(self.get_slice(-2,-1))],out=outa[tuple(self.get_slice(-1,None))])outa[tuple(self.get_slice(-1,None))]/=-2.0elifself.boundary_condition=='Periodic':# left boundarynp.subtract(x_asarr[tuple(self.get_slice(1,2))],\
x_asarr[tuple(self.get_slice(-1,None))],out=outa[tuple(self.get_slice(0,1))])outa[tuple(self.get_slice(0,1))]/=2.0# right boundarynp.subtract(x_asarr[tuple(self.get_slice(0,1))],\
x_asarr[tuple(self.get_slice(-2,-1))],out=outa[tuple(self.get_slice(-1,None))])outa[tuple(self.get_slice(-1,None))]/=2.0else:raiseValueError('Not implemented')else:raiseValueError('Not implemented')outa*=-1.ifself.voxel_size!=1.0:outa/=self.voxel_sizeifoutnone:ret.fill(outa)returnretelse:out.fill(outa)