Path: blob/devel/elmerice/Utils/SSAMaterialModels.F90
3203 views
!/*****************************************************************************/1! *2! * Elmer, A Finite Element Software for Multiphysical Problems3! *4! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland5! *6! * This library is free software; you can redistribute it and/or7! * modify it under the terms of the GNU Lesser General Public8! * License as published by the Free Software Foundation; either9! * version 2.1 of the License, or (at your option) any later version.10! *11! * This library 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 the GNU14! * Lesser General Public License for more details.15! *16! * You should have received a copy of the GNU Lesser General Public17! * License along with this library (in file ../LGPL-2.1); if not, write18! * to the Free Software Foundation, Inc., 51 Franklin Street,19! * Fifth Floor, Boston, MA 02110-1301 USA20! *21! *****************************************************************************/22!23!/******************************************************************************24! *25! * Authors: fabien Gillet-Chaulet, Rupert Gladstone26! * Email: [email protected]27! * [email protected]28! * Web: http://elmerice.elmerfem.org29! *30! * Original Date: May 202231! *32! ******************************************************************************/33!--------------------------------------------------------------------------------34!> Module containing utility routines for the SSA35!--------------------------------------------------------------------------------36MODULE SSAMaterialModels3738USE DefUtils3940IMPLICIT NONE4142CONTAINS4344!--------------------------------------------------------------------------------45!> Return the effective friction coefficient46!--------------------------------------------------------------------------------47FUNCTION SSAEffectiveFriction(Element,n,Basis,ub,SEP,PartlyGrounded,h,rho,rhow,sealevel,SlipDer) RESULT(Slip)48IMPLICIT NONE49REAL(KIND=dp) :: Slip ! the effective friction coefficient50TYPE(Element_t), POINTER :: Element ! the current element51INTEGER :: n ! number of nodes52REAL(KIND=dp) :: Basis(:) ! basis functions53REAL(KIND=dp) :: ub ! the velocity for non-linear friction laws54LOGICAL :: SEP ! Sub-Element Parametrisation of the friction55LOGICAL :: PartlyGrounded ! is the GL within the current element?56REAL(KIND=dp) :: h ! for SEP: the ice thickness at current location57REAL(KIND=dp) :: rho,rhow,sealevel ! density, sea-water density, sea-level58REAL(KIND=dp),OPTIONAL :: SlipDer ! dSlip/du=dSlip/dv if ub=(u^2+v^2)^1/2 ! required to compute the Jacobian59606162INTEGER :: iFriction63INTEGER, PARAMETER :: LINEAR = 164INTEGER, PARAMETER :: WEERTMAN = 265INTEGER, PARAMETER :: BUDD = 566INTEGER, PARAMETER :: REG_COULOMB_GAG = 3 ! Schoof 2005 & Gagliardini 200767INTEGER, PARAMETER :: REG_COULOMB_JOU = 4 ! Joughin 20196869TYPE(ValueList_t), POINTER :: Material, Constants, pMaterial70TYPE(Variable_t), POINTER :: GMSol,BedrockSol,NSol71INTEGER, POINTER :: NodeIndexes(:)72CHARACTER(LEN=MAX_NAME_LEN) :: Friction73REAL(KIND=dp) :: Slip2, gravity, qq, hafq74REAL(KIND=dp) :: fm,fq,MinN,U075REAL(KIND=dp) :: alpha,beta,fB76INTEGER :: GLnIP7778REAL(KIND=dp),DIMENSION(n) :: NodalBeta, NodalGM, NodalBed, NodalLinVelo,NodalC,NodalN79REAL(KIND=dp) :: bedrock,Hf,fC,fN,LinVelo80LOGICAL :: Found81TYPE(Solver_t), POINTER :: pSolver => NULL()8283SAVE :: GLnIP, GMSol, BedRockSol, pSolver8485! Sub - element GL parameterisation86IF (SEP) THEN87! If we have adaptive rule then GLnIP keyword is not given.88IF(.NOT. ASSOCIATED(pSolver, CurrentModel % Solver ) ) THEN89pSolver => CurrentModel % Solver90GLnIP=ListGetInteger( pSolver % Values, &91'GL integration points number',Found)92IF(GLnIP > 0) THEN93GMSol => VariableGet( CurrentModel % Variables, 'GroundedMask',UnFoundFatal=.TRUE. )94END IF95BedrockSol => VariableGet( CurrentModel % Variables, 'bedrock',UnFoundFatal=.TRUE. )96END IF97IF(GLnIP > 0) THEN98CALL GetLocalSolution( NodalGM,UElement=Element,UVariable=GMSol)99END IF100CALL GetLocalSolution( NodalBed,UElement=Element,UVariable= BedrockSol)101END IF102103! Friction law104Material => GetMaterial(Element)105NodeIndexes => Element % NodeIndexes106107Friction = ListGetString(Material, 'SSA Friction Law',Found, UnFoundFatal=.TRUE.)108SELECT CASE(Friction)109CASE('linear')110iFriction = LINEAR111fm = 1.0_dp112CASE('weertman')113iFriction = WEERTMAN114CASE('budd')115iFriction = BUDD116CASE('coulomb')117iFriction = REG_COULOMB_GAG118CASE('regularized coulomb')119iFriction = REG_COULOMB_JOU120CASE DEFAULT121CALL FATAL("SSAEffectiveFriction",'Friction choice not recognised')122END SELECT123124! coefficient for all friction parameterisations125NodalBeta(1:n) = ListGetReal( &126Material, 'SSA Friction Parameter', n, NodeIndexes(1:n), Found,&127UnFoundFatal=.TRUE.)128129! for nonlinear powers of sliding velocity130SELECT CASE (iFriction)131CASE(REG_COULOMB_JOU,REG_COULOMB_GAG,WEERTMAN,BUDD)132fm = ListGetConstReal( Material, 'SSA Friction Exponent', Found , UnFoundFatal=.TRUE.)133NodalLinVelo(1:n) = ListGetReal( &134Material, 'SSA Friction Linear Velocity', n, NodeIndexes(1:n), Found,&135UnFoundFatal=.TRUE.)136CASE DEFAULT137END SELECT138139! where explicit dependence on effective pressure is present...140SELECT CASE (iFriction)141CASE(REG_COULOMB_GAG,BUDD)142NSol => VariableGet( CurrentModel % Variables, 'Effective Pressure', UnFoundFatal=.TRUE. )143CALL GetLocalSolution( NodalN,UElement=Element, UVariable=NSol)144MinN = ListGetConstReal( Material, 'SSA Min Effective Pressure', Found, UnFoundFatal=.TRUE.)145fN = SUM( NodalN(1:n) * Basis(1:n) )146! Effective pressure should be >0 (for the friction law)147fN = MAX(fN, MinN)148END SELECT149150! parameters unique to one sliding parameterisation151SELECT CASE (iFriction)152153CASE(BUDD)154Constants => GetConstants()155gravity = ListGetConstReal( Constants, 'Gravity Norm', UnFoundFatal=.TRUE. )156! calculate haf from N = rho_i g z*157qq = ListGetConstReal( Material, 'SSA Haf Exponent', Found, UnFoundFatal=.TRUE.)158hafq = ( fN / (gravity * rho) ) ** qq159160CASE(REG_COULOMB_GAG)161fq = ListGetConstReal( Material, 'SSA Friction Post-Peak', Found, UnFoundFatal=.TRUE. )162NodalC = 0.0_dp163NodalC(1:n) = ListGetReal( &164Material, 'SSA Friction Maximum Value', n, NodeIndexes(1:n), Found,&165UnFoundFatal=.TRUE.)166fC = SUM( NodalC(1:n) * Basis(1:n) )167168CASE(REG_COULOMB_JOU)169U0 = ListGetConstReal( Material, 'SSA Friction Threshold Velocity', Found, UnFoundFatal=.TRUE.)170171END SELECT172173Beta=SUM(Basis(1:n)*NodalBeta(1:n))174175IF (SEP) THEN176! Floating177IF (PartlyGrounded .OR. GLnIP==0) THEN178bedrock = SUM( NodalBed(1:n) * Basis(1:n) )179Hf = rhow * (sealevel-bedrock) / rho180if (h < Hf) beta = 0._dp181ELSE IF (ALL(NodalGM(1:n) < 0._dp)) THEN182beta = 0._dp183END IF184END IF185186Slip2=0.0_dp187IF (iFriction .NE. LINEAR) THEN188LinVelo = SUM( NodalLinVelo(1:n) * Basis(1:n) )189IF ((iFriction == WEERTMAN).AND.(fm==1.0_dp)) iFriction=LINEAR190Slip2=1.0_dp191IF (ub < LinVelo) then192ub = LinVelo193Slip2=0.0_dp194ENDIF195END IF196197SELECT CASE (iFriction)198199CASE(LINEAR)200Slip = beta201IF (PRESENT(SlipDer)) SlipDer = 0._dp202203CASE(WEERTMAN)204Slip = beta * ub**(fm-1.0_dp)205IF (PRESENT(SlipDer)) SlipDer = Slip2*Slip*(fm-1.0_dp)/(ub*ub)206207CASE(BUDD)208Slip = beta * hafq * ub**(fm-1.0_dp)209! TODO:210! IF (PRESENT(SlipDer)) SlipDer =211212CASE(REG_COULOMB_GAG)213IF (fq.NE.1.0_dp) THEN214alpha = (fq-1.0_dp)**(fq-1.0_dp) / fq**fq215ELSE216alpha = 1.0_dp217END IF218fB = alpha * (beta / (fC*fN))**(fq/fm)219Slip = beta * ub**(fm-1.0_dp) / (1.0_dp + fB * ub**fq)**fm220IF (PRESENT(SlipDer)) SlipDer = Slip2 * Slip * ((fm-1.0_dp) / (ub*ub) - &221fm*fq*fB*ub**(fq-2.0_dp)/(1.0_dp+fB*ub**fq))222223CASE(REG_COULOMB_JOU)224Slip = beta * ub**(fm-1.0_dp) / (ub + U0)**fm225IF (PRESENT(SlipDer)) SlipDer = Slip2 * Slip * ((fm-1.0_dp) / (ub*ub) - &226fm*ub**(-1.0_dp)/(ub+U0))227228END SELECT229230END FUNCTION SSAEffectiveFriction231232!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!233! Compute the element averaged friction234!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!235FUNCTION ComputeMeanFriction(Element,n,ElementNodes,STDOFs,NodalU,NodalV,NodalZs,NodalZb,MinH, &236NodalDensity,SEP,GLnIP,sealevel,rhow) RESULT(strbasemag)237REAL(KIND=dp) :: strbasemag238TYPE(Element_t), POINTER :: Element239INTEGER :: n240TYPE(Nodes_t) :: ElementNodes241INTEGER :: STDOFs242REAL(KIND=dp) :: NodalU(n),NodalV(n),NodalZs(n),NodalZb(n),NodalDensity(n)243REAL(KIND=dp) :: MinH244LOGICAL :: SEP245INTEGER :: GLnIP246REAL(KIND=dp) :: sealevel,rhow247248LOGICAL :: PartlyGroundedElement249TYPE(Variable_t),POINTER :: GMSol250REAL(KIND=dp) :: NodalGM(n)251TYPE(GaussIntegrationPoints_t) :: IP252REAL(KIND=dp) :: Basis(n), detJ253REAL(KIND=dp) :: h,ub,rho,Velo(2)254REAL(KIND=dp) :: area,tb255REAL(KIND=dp) :: Ceff256LOGICAL :: stat257INTEGER :: t258259strbasemag=0._dp260IF (SEP) THEN261GMSol => VariableGet( CurrentModel % Variables, 'GroundedMask',UnFoundFatal=.TRUE. )262CALL GetLocalSolution( NodalGM,UElement=Element,UVariable=GMSol)263PartlyGroundedElement=(ANY(NodalGM(1:n).GE.0._dp).AND.ANY(NodalGM(1:n) < 0._dp))264IF (PartlyGroundedElement .AND. GLnIP > 0 ) THEN265IP = GaussPoints( Element , np=GLnIP )266ELSE267IP = GaussPoints( Element )268ENDIF269ELSE270IP = GaussPoints( Element )271ENDIF272273area=0._dp274tb=0._dp275DO t=1,IP % n276stat = ElementInfo( Element, ElementNodes, IP % U(t), IP % V(t), &277IP % W(t), detJ, Basis )278279h = SUM( (NodalZs(1:n)-NodalZb(1:n)) * Basis(1:n) )280h=max(h,MinH)281282rho = SUM( NodalDensity(1:n) * Basis(1:n) )283284Velo = 0.0_dp285Velo(1) = SUM(NodalU(1:n) * Basis(1:n))286IF (STDOFs == 2) Velo(2) = SUM(NodalV(1:n) * Basis(1:n))287ub = SQRT(Velo(1)*Velo(1)+Velo(2)*Velo(2))288289Ceff=SSAEffectiveFriction(Element,n,Basis,ub,SEP,PartlyGroundedElement,h,rho,rhow,sealevel)290291area=area+detJ*IP % s(t)292tb=tb+Ceff*ub*detJ*IP % s(t)293END DO294295strbasemag=tb/area296297END FUNCTION ComputeMeanFriction298299!--------------------------------------------------------------------------------300!> Return the effective basal mass balance (to be called separately for each IP in301!> a partly grounded element)302!--------------------------------------------------------------------------------303FUNCTION SSAEffectiveBMB(Element,nn,Basis,SEM,BMB,hh,FIPcount,rho,rhow,sealevel,FAF) RESULT(BMBatIP)304305IMPLICIT NONE306307REAL(KIND=dp) :: BMBatIP ! the effective basal melt rate at integration point308309INTEGER,INTENT(IN) :: nn ! element number of nodes310REAL(KIND=dp), INTENT(IN) :: BMB(:) ! basal mass balance311LOGICAL, INTENT(IN) :: SEM ! Sub-Element Parametrisation (requires interpolation of floatation on IPs)312REAL(KIND=dp),INTENT(IN) :: hh ! the ice thickness at current location313REAL(KIND=dp),INTENT(IN) :: Basis(:)314TYPE(Element_t),POINTER,INTENT(IN) :: Element315316! optional arguments, depending on melt param317INTEGER,INTENT(INOUT),OPTIONAL :: FIPcount318REAL(KIND=dp),INTENT(IN),OPTIONAL :: rho,rhow,sealevel ! to calculate floatation for SEM3319REAL(KIND=dp),INTENT(IN),OPTIONAL :: FAF ! Floating area fraction for SEM1320321TYPE(ValueList_t), POINTER :: Material322TYPE(Variable_t), POINTER :: GMSol,BedrockSol323CHARACTER(LEN=MAX_NAME_LEN) :: MeltParam324325REAL(KIND=dp),DIMENSION(nn) :: NodalBeta, NodalGM, NodalBed, NodalLinVelo,NodalC326REAL(KIND=dp) :: bedrock,Hf327328LOGICAL :: Found329330! Sub - element GL parameterisation331IF (SEM) THEN332GMSol => VariableGet( CurrentModel % Variables, 'GroundedMask',UnFoundFatal=.TRUE. )333CALL GetLocalSolution( NodalGM,UElement=Element,UVariable=GMSol )334BedrockSol => VariableGet( CurrentModel % Variables, 'bedrock',UnFoundFatal=.TRUE. )335CALL GetLocalSolution( NodalBed,UElement=Element,UVariable= BedrockSol )336END IF337338Material => GetMaterial(Element)339MeltParam = ListGetString(Material, 'SSA Melt Param',Found, UnFoundFatal=.TRUE.)340341BMBatIP=SUM(Basis(1:nn)*BMB(1:nn))342343SELECT CASE(MeltParam)344345CASE('FMP','fmp')346347CASE('NMP','nmp')348BMBatIP = 0.0_dp349350CASE('SEM1','sem1')351! check element type is triangular (would need to modify352! CalcFloatingAreaFraction to allow other element types)353IF (element % type % ElementCode .NE. 303) THEN354CALL Fatal('SSAEffectiveBMB','Expecting element type 303!')355END IF356357IF (PRESENT(FAF)) THEN358BMBatIP = BMBatIP * FAF359ELSE360CALL Fatal('SSAEffectiveBMB','FAF (floating area fraction) not present!')361END IF362363CASE('SEM3','sem3')364bedrock = SUM( NodalBed(1:nn) * Basis(1:nn) )365Hf= rhow * (sealevel-bedrock) / rho366IF (hh > Hf) THEN367BMBatIP = 0.0_dp368ELSE369IF (PRESENT(FIPcount)) FIPcount = FIPcount + 1370END IF371372CASE DEFAULT373WRITE( Message, * ) 'SSA Melt Param not recognised:', MeltParam374CALL FATAL("SSAEffectiveBMB",Message)375376END SELECT377378END FUNCTION SSAEffectiveBMB379380!--------------------------------------------------------------------------------381!> Calculate the fractional floating area of a partly grounded element for SEM1.382!> For implementing SEP1 use (1-FAF) for grounded area fraction.383!> Written for element type 303.384!> To be called per element from ThicknessSolver for SEM1.385!> See Helene Seroussi TC papers from 2014 and 2018.386!> More notes here: https://www.overleaf.com/read/chpfpgzhwvjr387!--------------------------------------------------------------------------------388FUNCTION CalcFloatingAreaFraction(element,NodalGM,hhVar,sealevel,rho,rhow) RESULT(FAF)389390IMPLICIT NONE391392REAL(KIND=dp) :: FAF ! the area fraction of floating ice393394TYPE(Element_t),POINTER,INTENT(IN) :: Element395REAL(KIND=dp),INTENT(IN) :: NodalGM(:)396TYPE(Variable_t),POINTER,INTENT(IN) :: hhVar397REAL(KIND=dp), INTENT(IN) :: rho,rhow,sealevel ! to calculate floatation398399TYPE(Variable_t),POINTER :: bedVar400INTEGER,POINTER :: hhPerm(:),bedPerm(:)401REAL(KIND=dp),POINTER :: hh(:),bed(:)402403! The following real vars use Yu's terminology (see overleaf linked above)404REAL(KIND=dp) :: ss,tt,A1,A2,x1,x2,x3,x4,x5,y1,y2,y3,y4,y5405REAL(KIND=dp) :: B1,B2,B3,Zb1,Zb2,Zb3406407! NI refers to Node Index.408! NI2 is the sole node of its category (floating or GL, see Yu's notes);409! NI1 and NI3 are both in the other category.410! nn is the number of nodes for the current element (3 for triangles, which is assumed here)411INTEGER :: NumFLoatingNodes, nn, NI1, NI2, NI3412413bedVar => VariableGet( CurrentModel % Variables, 'bedrock', UnFoundFatal=.TRUE. )414bedPerm => bedVar % Perm415bed => bedVar % Values416417hhPerm => hhVar % Perm418hh => hhVar % Values419420nn = GetElementNOFNodes()421NumFLoatingNodes = -SUM(NodalGM(1:nn))422423IF (ANY(NodalGM(1:nn) > 0._dp)) THEN424CALL Fatal('CalcFloatingAreaFraction','Fully grounded nodes found!')425END IF426427IF (NumFLoatingNodes < 1) THEN428CALL Fatal('CalcFloatingAreaFraction','Not enough floating nodes!')429430ELSEIF (NumFLoatingNodes == 1) THEN431IF (NodalGM(1) == -1) THEN432NI2 = element % NodeIndexes(1)433NI1 = element % NodeIndexes(2)434NI3 = element % NodeIndexes(3)435ELSEIF (NodalGM(2) == -1) THEN436NI2 = element % NodeIndexes(2)437NI1 = element % NodeIndexes(1)438NI3 = element % NodeIndexes(3)439ELSEIF (NodalGM(3) == -1) THEN440NI2 = element % NodeIndexes(3)441NI1 = element % NodeIndexes(1)442NI3 = element % NodeIndexes(2)443END IF444445ELSEIF (NumFLoatingNodes == 2) THEN446IF (NodalGM(1) == 0) THEN447NI2 = element % NodeIndexes(1)448NI1 = element % NodeIndexes(2)449NI3 = element % NodeIndexes(3)450ELSEIF (NodalGM(2) == 0) THEN451NI2 = element % NodeIndexes(2)452NI1 = element % NodeIndexes(1)453NI3 = element % NodeIndexes(3)454ELSEIF (NodalGM(3) == 0) THEN455NI2 = element % NodeIndexes(3)456NI1 = element % NodeIndexes(1)457NI3 = element % NodeIndexes(2)458END IF459460ELSEIF (NumFLoatingNodes > 2) THEN461CALL Fatal('CalcFloatingAreaFraction','Too many floating nodes!')462463END IF464465x1 = CurrentModel % Mesh % Nodes % x(NI1)466x2 = CurrentModel % Mesh % Nodes % x(NI2)467x3 = CurrentModel % Mesh % Nodes % x(NI3)468469y1 = CurrentModel % Mesh % Nodes % y(NI1)470y2 = CurrentModel % Mesh % Nodes % y(NI2)471y3 = CurrentModel % Mesh % Nodes % y(NI3)472473B1 = bed(bedPerm(NI1))474B2 = bed(bedPerm(NI2))475B3 = bed(bedPerm(NI3))476477Zb1= sealevel - hh(hhPerm(NI1)) * rho/rhow478Zb2= sealevel - hh(hhPerm(NI2)) * rho/rhow479Zb3= sealevel - hh(hhPerm(NI3)) * rho/rhow480481ss = (Zb2-B2)/(B3-B2-Zb3+Zb2)482tt = (Zb1-B1)/(B2-B1-Zb2+Zb1)483484x4 = x1 + tt*(x2-x1)485x5 = x2 + ss*(x3-x2)486y4 = y1 + tt*(y2-y1)487y5 = y2 + ss*(y3-y2)488489A1 = 0.5_dp * ABS( x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2) )490A2 = 0.5_dp * ABS( x4*(y5-y2) + x5*(y2-y4) + x2*(y4-y5) )491492FAF = A2/A1493494IF (NumFLoatingNodes == 2) FAF = 1.0_dp - FAF ! Needed because FAF was grounded fraction495496END FUNCTION CalcFloatingAreaFraction497498END MODULE SSAMaterialModels499500501502