Path: blob/devel/elmerice/Solvers/AdjointSSA/AdjointSSA_CostDiscSolver.F90
3206 views
!/*****************************************************************************/1! *2! * Elmer/Ice, a glaciological add-on to Elmer3! * http://elmerice.elmerfem.org4! *5! *6! * This program is free software; you can redistribute it and/or7! * modify it under the terms of the GNU General Public License8! * as published by the Free Software Foundation; either version 29! * of the License, or (at your option) any later version.10! *11! * This program is distributed in the hope that it will be useful,12! * but WITHOUT ANY WARRANTY; without even the implied warranty of13! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14! * GNU General Public License for more details.15! *16! * You should have received a copy of the GNU General Public License17! * along with this program (in file fem/GPL-2); if not, write to the18! * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,19! * Boston, MA 02110-1301, USA.20! *21! *****************************************************************************/22! ******************************************************************************23! *24! * Authors: f. Gillet-Chaulet (LGGE, Grenoble,France)25! * Email: [email protected]26! * Web: http://elmerice.elmerfem.org27! *28! * Original Date:29! *30! *****************************************************************************31SUBROUTINE AdjointSSA_CostDiscSolver( Model,Solver,dt,TransientSimulation )32!------------------------------------------------------------------------------33!------------------------------------------------------------------------------34!Compute the Cost function of the SSA Adjoint inverse Problem as: SUM_1^Nobs (u-u^obs)235! where u is evaluated at observation location36! Serial/Parallel 2D/3D37!38! OUTPUT are : J and DJDu (==Velocityb variable used as forcing of the SSA adjoint problem)39!40! !! Be careful this solver will reset the cost and DJDu to 0; so it has to41! be used as the first cost solver if regularistaion of flux divergence cost42! solvers are used in the simulation!!43!44!45! INPUT PARAMETERS are:46!47! In solver section:48! Problem Dimension = Integer (default = Coordinate system dimension)49! Cost Filename = File (default = 'CostOfT.dat')50! Cost Variable Name = String (default= 'CostValue')51! Lambda = Real (default= '1.0')52! Observed Variable Name = String53! Observation File Name = File54! Parallel observation Files = Logical55! Save use data = logical56!57! Variables58! Velocityb (forcing for the adjoint pb;DOFs== Pb dimension)59!60!******************************************************************************61USE ParticleUtils62USE GeneralUtils63USE DefUtils64USE Interpolation65#ifdef HAVE_NETCDF66USE Netcdf67#endif68IMPLICIT NONE69!------------------------------------------------------------------------------70TYPE(Solver_t) :: Solver71TYPE(Model_t) :: Model7273REAL(KIND=dp) :: dt74LOGICAL :: TransientSimulation75!76CHARACTER(LEN=MAX_NAME_LEN), PARAMETER :: DefaultCostFile = 'CostOfT.dat'77CHARACTER(LEN=MAX_NAME_LEN) :: SolverName="Adjoint Solver"78CHARACTER(LEN=MAX_NAME_LEN) :: CostFile,UsedDataFile79CHARACTER(LEN=MAX_NAME_LEN) :: CostSolName,VariableName,ObsFileName80TYPE(Mesh_t),POINTER :: Mesh81TYPE(Element_t),POINTER :: Element82TYPE(Variable_t), POINTER :: TimeVar,CostVar83TYPE(Variable_t), POINTER :: VelocitybSol,Variable84TYPE(ValueList_t), POINTER :: SolverParams85TYPE(Nodes_t) :: ElementNodes86REAL(KIND=dp), POINTER :: Vb(:),Values(:)87INTEGER, POINTER :: NodeIndexes(:)88INTEGER, POINTER :: VbPerm(:),Perm(:)89Logical :: Firsttime=.true.,Found,Parallel,ParallelFile,stat,Gotit90integer :: i,j,k,l,s,t,n,NMAX,DIM,ierr,c,ok91real(kind=dp) :: Cost,Cost_S,Lambda92real(kind=dp) :: Coord(3),UVW(3),coeff,SqrtElementMetric,x93REAL(KIND=dp),ALLOCATABLE,SAVE :: Basis(:), dBasisdx(:,:)94REAL(KIND=dp) :: xmin,ymin,xmax,ymax95INTEGER,SAVE :: VDOFs96REAL(KIND=dp) :: V(3)97REAL(KIND=dp),ALLOCATABLE,SAVE :: xobs(:,:),Vobs(:,:)98REAL(KIND=dp),ALLOCATABLE :: xx(:),yy(:),ux(:,:),uy(:,:)99INTEGER,ALLOCATABLE,SAVE :: InElement(:)100INTEGER :: ElementIndex101INTEGER :: XMinIndex,XMaxIndex,YMinIndex,YMaxIndex,nx,ny102INTEGER,SAVE :: NTOT=-1,NTOT_S103integer,SAVE :: nobs104LOGICAL,SAVE :: FirstRound=.True.,SAVE_USED_DATA=.False.105CHARACTER*10 :: date,temps106INTEGER,PARAMETER :: IO=12107LOGICAL :: WarnActive108109LOGICAL :: NETCDFFormat110INTEGER :: NetcdfStatus,varid,ncid111CHARACTER(LEN=MAX_NAME_LEN) :: Xdim,Ydim,VarName112REAL(KIND=dp):: UxFillv,UyFillv113REAL(KIND=dp):: NodalU,NodalV114115save Firsttime,Parallel116save SolverName,CostSolName,CostFile,VariableName117save ElementNodes118119CALL Info(SolverName,'***********************',level=0)120CALL Info(SolverName,' This solver has been replaced by:',level=0)121CALL Info(SolverName,' Adjoint_CostDiscSolver ',level=0)122CALL Info(SolverName,' See documentation under: ',level=0)123CALL Info(SolverName,' elmerice/Solvers/Documentation ',level=0)124CALL Info(SolverName,'***********************',level=0)125CALL FATAL(SolverName,' Use new solver !!')126127SolverParams => GetSolverParams()128DIM=GetInteger(SolverParams ,'Problem Dimension',Found)129If (.NOT.Found) then130CALL WARN(SolverName,'Keyword >Problem Dimension< not found, assume DIM = CoordinateSystemDimension()')131DIM = CoordinateSystemDimension()132Endif133134! Get min/max coordinates of current mesh135xmin=MINVAL(Solver%Mesh%Nodes%x(:))136xmax=MAXVAL(Solver%Mesh%Nodes%x(:))137IF (DIM.EQ.2) THEN138ymin=MINVAL(Solver%Mesh%Nodes%y(:))139ymax=MAXVAL(Solver%Mesh%Nodes%y(:))140ENDIF141142Lambda = GetConstReal( SolverParams,'Lambda', Found)143IF(.NOT.Found) THEN144CALL WARN(SolverName,'Keyword >Lambda< not found in section >Solver<')145CALL WARN(SolverName,'Taking default value Lambda=1.0')146Lambda = 1.0_dp147CALL ListAddConstReal( SolverParams,'Lambda',Lambda)148End if149150If (Firsttime) then151N = model % MaxElementNodes152allocate(ElementNodes % x(N), ElementNodes % y(N), ElementNodes % z(N))153allocate(Basis(N), dBasisdx(N,3))154155!!!!!!! Check for parallel run156Parallel = .FALSE.157IF ( ASSOCIATED( Solver % Matrix % ParMatrix ) ) THEN158IF ( Solver % Matrix % ParMatrix % ParEnv % PEs > 1 ) THEN159Parallel = .TRUE.160END IF161END IF162163WRITE(SolverName, '(A)') 'CostSolver_Adjoint'164165!!!!!!!!!!! get Solver Variables166167CostFile = ListGetString(Solver % Values,'Cost Filename',Found )168IF (.NOT. Found) CostFile = DefaultCostFile169CALL DATE_AND_TIME(date,temps)170If (Parallel) then171if (ParEnv % MyPe.EQ.0) then172OPEN (IO, FILE=CostFile)173write(IO,1000) date(5:6),date(7:8),date(1:4),temps(1:2),temps(3:4),temps(5:6)174write(IO,1001) Lambda175write(IO,'(A)') '# iter, Lambda*J0, rms'176CLOSE(IO)177End if178Else179OPEN (IO, FILE=CostFile)180write(IO,1000) date(5:6),date(7:8),date(1:4),temps(1:2),temps(3:4),temps(5:6)181write(IO,1001) Lambda182write(IO,*)'# iter, Lambda*J0, rms'183CLOSE(IO)184End if185186CostSolName = GetString( SolverParams,'Cost Variable Name', Found)187IF(.NOT.Found) THEN188CALL WARN(SolverName,'Keyword >Cost Variable Name< not found in section >Solver<')189CALL WARN(SolverName,'Taking default value >CostValue<')190WRITE(CostSolName,'(A)') 'CostValue'191END IF192193VariableName = ListGetString( SolverParams,'Observed Variable Name', UnFoundFatal=.TRUE.)194Variable => VariableGet( Solver % Mesh % Variables, Trim(VariableName) ,UnFoundFatal=.TRUE. )195VDOFs=Variable%DOFs196197!! Get the obs198ObsFileName = ListGetString( SolverParams,'Observation File Name', UnFoundFatal=.TRUE.)199200!! I the file netcf?201k = INDEX(ObsFileName ,'.nc' )202NETCDFFormat = ( k /= 0 )203204IF (NETCDFFormat) then205206#ifdef HAVE_NETCDF207208IF (DIM.NE.2) CALL FATAL(Trim(SolverName),'Netcdf only supported for 2D')209210CALL INFO(Trim(SolverName),'Data File is in netcdf format', Level=5)211212NetCDFstatus = NF90_OPEN(trim(ObsFileName),NF90_NOWRITE,ncid)213IF ( NetCDFstatus /= NF90_NOERR ) &214CALL FATAL(Trim(SolverName), 'Unable to open NETCDF File')215216Xdim=ListGetString( SolverParams,"X Dim Name", Found )217if (.NOT.Found) Xdim='x'218219NetCDFstatus = nf90_inq_dimid(ncid, trim(Xdim) , varid)220IF ( NetCDFstatus /= NF90_NOERR ) &221CALL FATAL(Trim(SolverName),'Unable to get netcdf x-dim Id')222223NetCDFstatus = nf90_inquire_dimension(ncid,varid,len=nx)224IF ( NetCDFstatus /= NF90_NOERR ) &225CALL FATAL(Trim(SolverName), 'Unable to get netcdf nx')226227Ydim=ListGetString( SolverParams,"Y Dim Name", Found )228if (.NOT.Found) Ydim='y'229230NetCDFstatus = nf90_inq_dimid(ncid, trim(Ydim) , varid)231IF ( NetCDFstatus /= NF90_NOERR ) &232CALL FATAL(Trim(SolverName),'Unable to get netcdf y-dim Id')233234NetCDFstatus = nf90_inquire_dimension(ncid,varid,len=ny)235IF ( NetCDFstatus /= NF90_NOERR ) &236CALL FATAL(Trim(SolverName), 'Unable to get netcdf ny')237238ALLOCATE(xx(nx),yy(ny))239240!! Get X and Y variable241Xdim=ListGetString( SolverParams, "X Var Name", Found )242if (.NOT.Found) Xdim='x'243NetCDFstatus = nf90_inq_varid(ncid,trim(Xdim),varid)244IF ( NetCDFstatus /= NF90_NOERR ) &245CALL FATAL(Trim(SolverName),'Unable to get netcdf x-variable id')246247NetCDFstatus = nf90_get_var(ncid, varid,xx)248IF ( NetCDFstatus /= NF90_NOERR ) &249CALL FATAL(Trim(SolverName), 'Unable to get netcdf x-variable ')250251Ydim=ListGetString( SolverParams, "Y Var Name", Found )252if (.NOT.Found) Ydim='y'253NetCDFstatus = nf90_inq_varid(ncid,trim(Ydim),varid)254IF ( NetCDFstatus /= NF90_NOERR ) &255CALL FATAL(Trim(SolverName),'Unable to get netcdf x-variable id')256257NetCDFstatus = nf90_get_var(ncid, varid,yy)258IF ( NetCDFstatus /= NF90_NOERR ) &259CALL FATAL(Trim(SolverName), 'Unable to get netcdf x-variable ')260261!! Check that there is data within the domain262IF ((MAXVAL(xx).LT.xmin).OR.(MINVAL(xx).GT.xmax)&263.OR.(MAXVAL(yy).LT.ymin).OR.(MINVAL(yy).GT.ymax)) &264CALL Fatal(Trim(SolverName), &265'No data within model domain')266!!! get the index of values that are within the mesh bounding box267CALL MinMaxIndex(xx,nx,xmin,xmax,XMinIndex,XMaxIndex)268CALL MinMaxIndex(yy,ny,ymin,ymax,YMinIndex,YMaxIndex)269270!! get ux and uy271nx=XMaxIndex-XMinIndex+1272ny=YMaxIndex-YMinIndex+1273ALLOCATE(ux(nx,ny),uy(nx,ny))274275VarName=ListGetString( SolverParams, "Ux Var Name", Found )276if (.NOT.Found) VarName='vx'277NetCDFstatus = nf90_inq_varid(ncid,trim(VarName),varid)278IF ( NetCDFstatus /= NF90_NOERR ) &279CALL Fatal(Trim(SolverName),'Unable to get netcdf ux variable id')280NetCDFstatus = nf90_get_var(ncid, Varid, ux(:, :), &281start = (/ XMinIndex, YMinIndex /), &282count = (/ nx,ny/))283IF ( NetCDFstatus /= NF90_NOERR ) &284CALL Fatal(Trim(SolverName),'Unable to get netcdf ux variable')285NetCDFstatus = nf90_get_att(ncid, varid,"_FillValue",Uxfillv)286IF ( NetCDFstatus /= NF90_NOERR ) &287CALL FATAL(Trim(SolverName),'Ux has no attribute _FillValue')288289VarName=ListGetString( SolverParams, "Uy Var Name", Found )290if (.NOT.Found) VarName='vy'291NetCDFstatus = nf90_inq_varid(ncid,trim(VarName),varid)292IF ( NetCDFstatus /= NF90_NOERR ) &293CALL Fatal(Trim(SolverName),'Unable to get netcdf uy variable id')294NetCDFstatus = nf90_get_var(ncid, Varid, uy(:, :), &295start = (/ XMinIndex, YMinIndex /), &296count = (/ nx,ny /))297IF ( NetCDFstatus /= NF90_NOERR ) &298CALL Fatal(Trim(SolverName),'Unable to get netcdf uy variable')299NetCDFstatus = nf90_get_att(ncid, varid,"_FillValue",Uyfillv)300IF ( NetCDFstatus /= NF90_NOERR ) &301CALL FATAL(Trim(SolverName),'Uy has no attribute _FillValue')302303!! close netcdf304NetCDFstatus = nf90_close(ncid)305306!!307ALLOCATE(xobs(nx*ny,3),Vobs(nx*ny,DIM))308xobs=0._dp309Vobs=0._dp310311nobs=0312Do i=1,nx313Do j=1,ny314NodalU=ux(i,j)315NodalV=uy(i,j)316IF ((NodalU.EQ.Uxfillv).OR.(NodalV.EQ.Uyfillv)) CYCLE317nobs=nobs+1318xobs(nobs,1)=xx(XMinIndex+i-1)319xobs(nobs,2)=yy(YMinIndex+j-1)320Vobs(nobs,1)=NodalU321Vobs(nobs,2)=NodalV322End do323End do324325DEALLOCATE(xx,yy,ux,uy)326327#else328329CALL FATAL(Trim(SolverName),'Elmer/Ice has not been installed with netcdf -convert your file to ASCII table--')330331#endif332333Else !.not.NETCDFFormat334335ParallelFile = .False.336ParallelFile = GetLogical(SolverParams,'Parallel Observation Files', Found)337if (Parallel.AND.ParallelFile) &338write(ObsFileName,'(A,A,I0)') trim(ObsFileName),'.',ParEnv % MyPE339340open(IO,file=trim(ObsFileName),status = 'old',iostat = ok)341if(ok /= 0) then342write(message,'(A,A)') 'Unable to open file ',TRIM(ObsFileName)343CALL Fatal(Trim(SolverName),Trim(message))344end if345nobs=0346do while(ok == 0)347read(io,*,iostat = ok)348if (ok == 0) nobs = nobs + 1349end do350close(IO)351352ALLOCATE(xobs(nobs,3),Vobs(nobs,DIM))353354Vobs=0.0_dp355xobs=0.0_dp356open(IO,file=trim(ObsFileName))357do i=1,nobs358read(IO,*) (xobs(i,j),j=1,DIM),(Vobs(i,j),j=1,DIM)359end do360close(IO)361ENDIF362363ALLOCATE(InElement(nobs))364InElement(:)=-1365366SAVE_USED_DATA = GetLogical(SolverParams,'Save used data', Found)367!!! End of First visit368Firsttime=.false.369Endif370371! Get the variable solution372Variable => VariableGet( Solver % Mesh % Variables, Trim(VariableName), UnFoundFatal=.TRUE. )373Values => Variable % Values374Perm => Variable % Perm375376! Get the adjoint377VelocitybSol => VariableGet( Solver % Mesh % Variables, 'Velocityb', UnFoundFatal=.TRUE. )378Vb => VelocitybSol % Values379VbPerm => VelocitybSol % Perm380Vb=0.0_dp381382IF (VelocitybSol%DOFs.NE.Variable%DOFs) &383CALL FATAL(Trim(SolverName),'DOFs do not correspond')384!385Mesh => GetMesh()386387! Temporary trick to disable Warnings as LocateParticleInMeshOctree388! will send a lot of warning for data points not found in the emsh389IF (FirstRound) THEN390WarnActive=OutputLevelMask(2)391OutputLevelMask(2)=.FALSE.392ENDIF393394! Compute the cost395Cost=0._dp396397Element => GetActiveElement(1)398ElementIndex = Element % ElementIndex399400CALL StartAdvanceOutput(SolverName,'Compute cost')401Do s=1,nobs402403CALL AdvanceOutput(s,nobs)404405IF (FirstRound) then406!Need to find in which Element the data point resides407! First check that it is within the computation domain408Coord=0._dp409Coord(1:DIM)=xobs(s,1:DIM)410IF ((Coord(1).GT.xmax).OR.(Coord(1).LT.xmin)) CYCLE411IF (DIM.EQ.2) THEN412IF ((Coord(2).GT.ymax).OR.(Coord(2).LT.ymin)) CYCLE413END IF414415IF (DIM.LT.CoordinateSystemDimension()) THEN416! we are in a lower dimension than the mesh417! => use brute force search418419! first check in the current elmement420Element => Mesh % Elements(ElementIndex)421n = GetElementNOFNodes(Element)422NodeIndexes => Element % NodeIndexes423ElementNodes % x(1:n) = Solver % Mesh % Nodes % x(NodeIndexes)424IF (DIM == 1) THEN425ElementNodes % y(1:n) = 0.0_dp426ElementNodes % z(1:n) = 0.0_dp427ELSE IF (DIM == 2) THEN428ElementNodes % y(1:n) = Solver % Mesh % Nodes % y(NodeIndexes)429ElementNodes % z(1:n) = 0.0_dp430ENDIF431IF (PointInElement(Element,ElementNodes,Coord(:),UVW)) THEN432IF (.NOT.CheckPassiveElement(Element)) &433InElement(s)=ElementIndex434435ELSE436! go through all elements437Do i=1,Solver%NumberOfActiveElements438Element => GetActiveElement(i)439n = GetElementNOFNodes(Element)440NodeIndexes => Element % NodeIndexes441ElementNodes % x(1:n) = Solver % Mesh % Nodes % x(NodeIndexes)442IF (DIM == 1) THEN443ElementNodes % y(1:n) = 0.0_dp444ElementNodes % z(1:n) = 0.0_dp445ELSE IF (DIM == 2) THEN446ElementNodes % y(1:n) = Solver % Mesh % Nodes % y(NodeIndexes)447ElementNodes % z(1:n) = 0.0_dp448ENDIF449IF (PointInElement(Element,ElementNodes,Coord(:),UVW)) THEN450IF (CheckPassiveElement(Element)) Exit451ElementIndex= Element % ElementIndex452InElement(s)=ElementIndex453Exit454END IF455End Do456END IF457458ELSE459!> use particles meshoctree460ElementIndex=-1 ! don't know why but if i don't reset ElmentIndex it fails461CALL LocateParticleInMeshOctree( ElementIndex,Coord)462If (ElementIndex.NE.0) THEN463InElement(s)=ElementIndex464Element => Mesh % Elements(ElementIndex)465IF (CheckPassiveElement(Element).OR. &466(Element % PartIndex /= ParEnv % myPE)) THEN467InElement(s)=-1468END IF469END IF470471END IF472ENDIF !End if FirstRound473474475! Data Point has been found in one element476IF (InElement(s)>0) THEN477Element => Mesh % Elements(InElement(s))478n = GetElementNOFNodes(Element)479NodeIndexes => Element % NodeIndexes480! set coords of highest occurring dimension to zero (to get correct path element)481!-------------------------------------------------------------------------------482ElementNodes % x(1:n) = Solver % Mesh % Nodes % x(NodeIndexes)483IF (DIM == 1) THEN !1D SSA484ElementNodes % y(1:n) = 0.0_dp485ElementNodes % z(1:n) = 0.0_dp486ELSE IF (DIM == 2) THEN !2D SSA487ElementNodes % y(1:n) = Solver % Mesh % Nodes % y(NodeIndexes)488ElementNodes % z(1:n) = 0.0_dp489ELSE490WRITE(Message,'(a,i1,a)')&491'It is not possible to compute SSA problems with DOFs=',&492DIM, ' . Aborting'493CALL Fatal( SolverName, Message)494END IF495496IF (.NOT.PointInElement(Element,ElementNodes,xobs(s,1:3),UVW)) THEN497PRINT *,ParEnv%MyPE,'eindex',Element % ElementIndex498PRINT *,ParEnv%MyPE,'obs. coords',xobs(s,1:2)499PRINT *,ParEnv%MyPE,'node indexes',NodeIndexes(1:n)500PRINT *,ParEnv%MyPE,'nodex',ElementNodes % x(1:n)501PRINT *,ParEnv%MyPE,'nodey',ElementNodes % y(1:n)502503CALL FATAL(SolverName,'Point was supposed to be found in this element')504ELSE505stat = ElementInfo( Element,ElementNodes,UVW(1),UVW(2),UVW(3),SqrtElementMetric, &506Basis,dBasisdx )507508! Variable at obs point509Do i=1,DIM510V(i)=SUM(Values(VDOFs*(Perm(NodeIndexes(1:n))-1)+i)*basis(1:n))511End do512513! Update cost514Do i=1,DIM515Cost=Cost+0.5*Lambda*(V(i)-Vobs(s,i))*(V(i)-Vobs(s,i))516End do517!PRINT *,V,Vobs518519!Derivative of Cost at nodal Point520Do j=1,n521Do i=1,DIM522k=VDOFS*(VbPerm(NodeIndexes(j))-1)+i523Vb(k)=Vb(k)+Lambda*(V(i)-Vobs(s,i))*basis(j)524End do525End Do526527END IF528529ELSE530531WRITE(Message,'(a,I0,a)')&532'Data Point',s,'found in no element'533CALL Info( SolverName, Message,level=15)534END IF535536END DO !Do on s537538539if (NTOT < 0) NTOT=COUNT(InElement(:)>0)540!! Save Cost as a function of time541542TimeVar => VariableGet( Solver % Mesh % Variables, 'Time' )543544IF (Parallel) THEN545CALL MPI_ALLREDUCE(NTOT,NTOT_S,1,&546MPI_INTEGER,MPI_SUM,ELMER_COMM_WORLD,ierr)547CALL MPI_ALLREDUCE(Cost,Cost_S,1,&548MPI_DOUBLE_PRECISION,MPI_SUM,ELMER_COMM_WORLD,ierr)549CostVar => VariableGet( Solver % Mesh % Variables, CostSolName )550IF (ASSOCIATED(CostVar)) THEN551CostVar % Values(1)=Cost_S552END IF553IF (Solver % Matrix % ParMatrix % ParEnv % MyPE == 0) then554OPEN (IO, FILE=CostFile,POSITION='APPEND')555write(IO,'(e13.5,2x,e15.8,2x,e15.8)') TimeVar % Values(1),Cost_S,sqrt(2.0*Cost_S/(NTOT_S*Lambda))556CLOSE(IO)557write(Message,'(A,A,I0)') trim(SolverName),'total number of data points:',NTOT_S558call INFO(SolverName,Message,level=3)559End if560ELSE561CostVar => VariableGet( Solver % Mesh % Variables, CostSolName )562IF (ASSOCIATED(CostVar)) THEN563CostVar % Values(1)=Cost564END IF565OPEN (IO, FILE=CostFile,POSITION='APPEND')566write(IO,'(e13.5,2x,e15.8,2x,e15.8)') TimeVar % Values(1),Cost,sqrt(2.0*Cost/(NTOT*Lambda))567close(IO)568write(Message,'(A,A,I0)') trim(SolverName),'total number of data points:',NTOT569call INFO(SolverName,Message,level=3)570END IF571572If (FirstRound) THEN573IF (SAVE_USED_DATA) THEN574If (Parallel) then575write(UsedDataFile,'(A,A,I0)') trim(SolverName),'.useddata.',ParEnv % MyPE576Else577write(UsedDataFile,'(A,A)') trim(SolverName),'.useddata'578End if579580open(IO,File=UsedDataFile)581Do s=1,nobs582If (InElement(s)>0) then583if (DIM.eq.1) Then584write(IO,'(e13.5,2x,e15.8)') (xobs(s,i),i=1,DIM),(Vobs(s,i),i=1,DIM)585Else if (DIM.eq.2) Then586write(IO,'(e15.8,2x,e15.8,2x,e15.8,2x,e15.8)') (xobs(s,i),i=1,DIM),(Vobs(s,i),i=1,DIM)587End if588End if589End do590close(IO)591END IF592End if593594! Reset Warning level to previous value595IF (FirstRound) THEN596OutputLevelMask(2)=WarnActive597ENDIF598599FirstRound=.False.600Return6016021000 format('#date,time,',a1,'/',a1,'/',a4,',',a2,':',a2,':',a2)6031001 format('#lambda,',e15.8)604605CONTAINS606! Find the min and max indexes of values within bBox607SUBROUTINE MinMaxIndex(x,n,minx,maxx,MinIndex,MaxIndex)608IMPLICIT NONE609REAL(KIND=dp),INTENT(IN) :: x(:),minx,maxx610INTEGER,INTENT(IN) :: n611INTEGER,INTENT(OUT) :: MinIndex,MaxIndex612613! coordinates should be monotonically increasing or614! decreasing615IF ((x(2)>x(1)).AND.(x(n)>x(n-1))) THEN616MinIndex = MAXLOC(x, DIM=1,MASK=(x < minx))617MinIndex=MAX(MinIndex,1)618619MaxIndex = MINLOC(x, DIM=1,MASK=(x > maxx))620IF (MaxIndex.LE.1) MaxIndex=n621ELSE IF ((x(2)<x(1)).AND.(x(n)<x(n-1))) THEN622MaxIndex = MAXLOC(x, DIM=1,MASK=(x < minx))623IF (MaxIndex.LE.1) MaxIndex=n624625MinIndex = MINLOC(x, DIM=1,MASK=(x > maxx))626MinIndex=MAX(MinIndex,1)627ELSE628CALL FATAL(SolverName,'coordinate is neither monotonically increasing or decreasing')629ENDIF630631IF (MaxIndex.LT.MinIndex) THEN632WRITE(message,*) 'Error Min Max Indexes ',MinIndex,MaxIndex633CALL WARN(SolverName,message)634WRITE(message,*) 'Min values ',MINVAL(x),minx635CALL WARN(SolverName,message)636WRITE(message,*) 'Max values ',MAXVAL(x),maxx637CALL WARN(SolverName,message)638CALL FATAL(SolverName,'This is a fatal error')639ENDIF640641END SUBROUTINE MinMaxIndex642643!------------------------------------------------------------------------------644END SUBROUTINE AdjointSSA_CostDiscSolver645!------------------------------------------------------------------------------646! *****************************************************************************647648649