Process surface model output

Process surface model output

  • R program to process Southern Ocean model station concentrations

library('ncdf4')
library('yaml')
library('RColorBrewer')
project_tmpdir_obs = read_yaml('../_config_calc.yml')$project_tmpdir_obs
model_data_dir = read_yaml('../_config_calc.yml')$model_data_dir
username = Sys.info()['user']
project_tmpdir_obs = gsub('\\{\\{env\\[\'USER\'\\]\\}\\}', username, project_tmpdir_obs)
model_data_dir = gsub('\\{\\{env\\[\'USER\'\\]\\}\\}', username, model_data_dir)
# Options:
minnmon=2 # minimum number of months to use for a seasonal average 
meanwin=c(1998.9,2020.2) # window for calculating means
trendwin=c(2005.0,2020.2) # window for calculating trends
thisyear=2020 # latest year to process (figure titles assume synonymous with 'present')

# Select which sites to use:
stationlist=read.table('SO_CO2_stationlist_for_models.txt',header=T,stringsAsFactors=F) # use = 0 for do not use, 2 for SO, 1 was for reference but no longer used - use refcol below
print(stationlist)

# Specify lab codes
labs=c('NOAA','SIO_O2','SIO_CDK','CSIRO','SAWS','LSCE','TU','NIWA')
labids=c(1,4,426,2,36,11,8,15)

# Specify ObsPack file location (used as template for CAMSv20r1)
gvp60dir='obspack_co2_1_GLOBALVIEWplus_v6.0_2020-09-11/data/nc'
   station     lab method use      lat      lon masl
1      SPO    NOAA insitu   1 -89.9800 -24.8000 2810
2      SPO    NOAA  flask   0 -89.9800 -24.8000 2810
3      SPO  SIO_O2  flask   0 -89.9800 -24.8000 2810
4      SPO SIO_CDK  flask   0 -89.9800 -24.8000 2810
5      SPO   CSIRO  flask   0 -89.9800 -24.8000 2810
6      HBA    NOAA  flask   2 -75.6050 -26.2100   10
7      SYO    NOAA  flask   2 -69.0125  39.5900   14
8      SYO      TU insitu   2 -69.0125  39.5900   14
9      MAA   CSIRO  flask   2 -67.6170  62.8670   32
10     CYA   CSIRO  flask   2 -66.2830 110.5170   47
11     PSA    NOAA  flask   2 -64.9000 -64.0000   10
12     PSA  SIO_O2  flask   2 -64.9000 -64.0000   10
13     DRP    NOAA  flask   2 -59.0000 -64.6900   10
14     MQA   CSIRO  flask   2 -54.4830 158.9670    6
15     CRZ    NOAA  flask   2 -46.4337  51.8478  197
16     BHD    NIWA insitu   0 -41.4083 174.8710   85
17     CGO    NOAA  flask   0 -40.6830 144.6900  164
18     CGO  SIO_O2  flask   0 -40.6830 144.6900  104
19     CGO   CSIRO  flask   0 -40.6830 144.6900  164
20     AMS    LSCE insitu   0 -37.7983  77.5378   55
21     CPT    SAWS insitu   0 -34.3523  18.4891  230

Loop on models

bbsnames=c('CAMSv20r1','CT2017','CT2019B','CTE2018','CTE2020','MIROC','CarboScopeAdjSO','CarboScopeSC','CarboScope','TM5pCO2')
mclnames=c('CAMSv20r1','CT2017','CT2019B','CTE2018','CTE2020','MIROC','s99oc_ADJocI40S_v2020','s99oc_SOCCOM_v2020','s99oc_v2020','TM5-Flux-')

for(model in bbsnames){ 

# Specify cases and particulars
if(substr(model,1,3)=='CT2'){
	cases=c(1:5)
} else if(substr(model,1,3)=='CTE'){
	cases=c(1,3:5) # do not need BG
} else if(substr(model,1,10)=='CarboScope'){
	cases=c(1,3:5) # no BG
} else if(model=='MIROC'){
	mirocyears=seq(1996,2019)
	cases=c(1,3:5) # do not need BG
} else if(model=='CAMSv20r1'){
	modobspacktotfile='v20r1_obspack6.txt' # flat text files for all data
	modobspacklndfile='v20r1_obspack6_testland.txt'
	modobspackocefile='v20r1_obspack6_testocean.txt'
	modobspackfosfile='v20r1_obspack6_testffoss.txt'
	obsobspackdir=paste(project_tmpdir_obs,'/',gvp60dir,sep='') # read in actual data files from same GV+ version, then swap in from single CAMS file
	cases=c(1,3:5)
} else if(model=='TM5pCO2'){
	cases=c(6:9)
}

# Loop on cases
for(case in cases){

# CT, CTE, CAMS, MIROC, CarboScope (comps not used by CAMS or CarboScope)
if(case==1){ comps=c(1,1,1,1,1); subdir='' } # bg, ff, ocean, bio, and fires (all)
if(case==2){ comps=c(1,0,0,0,0); subdir='/BG' } # bg only
if(case==3){ comps=c(0,1,0,0,0); subdir='/FF' } # ff only 
if(case==4){ comps=c(0,0,1,0,0); subdir='/OCEAN' } # ocean only 
if(case==5){ comps=c(0,0,0,1,1); subdir='/LAND' } # bio, and fires only
# TM5pCO2
if(case==6){ run='mrf' } 
if(case==7){ run='m0f' } 
if(case==8){ run='mwf' } 
if(case==9){ run='mmf' } 
#if(case>5){ subdir=paste('/',toupper(run),sep='') }
if(case>5){ 
    subdir=''
    mclname=paste(mclnames[which(bbsnames==model)],run,sep='') 
} else {
    mclname=mclnames[which(bbsnames==model)]
}

# Specify model subdirectory
modobspackdir=paste(model_data_dir,'/',mclname,sep='')
 
print(paste(mclname,subdir,sep=''))

# Read in station output

# set up arrays
allsta=data.frame(cbind(rep(seq(1957,thisyear),each=12),rep(seq(1,12),times=thisyear-1957+1)))
colnames(allsta)=c('year','month')
yrfrac=allsta$year+(allsta$mon-0.5)/12
monseas=rep(1,nrow(allsta)) # DJF
monseas[allsta$month>2&allsta$month<6]=2 # MAM
monseas[allsta$month>5&allsta$month<9]=3 # JJA
monseas[allsta$month>8&allsta$month<12]=4 # SON
seasyear=trunc(yrfrac+1/12) # shift Dec to next year

allseas=data.frame(cbind(aggregate(monseas,by=list(seas=monseas,year=seasyear),mean)$x,aggregate(yrfrac,by=list(seas=monseas,year=seasyear),mean)$x))
allnmon=aggregate(!is.na(monseas),by=list(seas=monseas,year=seasyear),sum)$x # all 3 except first and last DJF
colnames(allseas)=c('seas','yrfrac')
alllat=NULL
alllon=NULL
allalt=NULL

allann=data.frame(cbind(aggregate(yrfrac,by=list(year=allsta$year),mean)$x))
allnmonann=aggregate(!is.na(monseas),by=list(year=allsta$year),sum)$x
colnames(allann)=c('yrfrac')

# loop over stations
for(i in c(1:nrow(stationlist))){

	staco2=NULL
	sta=stationlist$station[i]
	lab=stationlist$lab[i]
	meth=stationlist$method[i]
	use=stationlist$use[i]
	#print(c(sta,lab,meth,use))

	# match ObsPack file names
	if(sta=='DRP'){ type='shipboard' } else { type='surface' }
	if(lab=='NOAA'&meth=='insitu') { filt='allvalid' } else if(lab=='SAWS'){ filt='marine' } else if(lab=='NIWA'){ filt='baseline' } else { filt='representative' }
	if((model=='CT2019B'|model=='TM5pCO2'|model=='MIROC'|model=='CAMSv20r1'|model=='CTE2018'|model=='CTE2020')&sta=='SYO'&lab=='TU'){ filt='allvalid' } # rest = representative

	labid=labids[which(labs==lab)]

	if(substr(model,1,10)!='CarboScope'&model!='TM5pCO2'&model!='CAMSv20r1'&model!='CTE2018') stanc=nc_open(paste(modobspackdir,'/simulated-obs/co2_',tolower(sta),'_',type,'-',meth,'_',labid,'_',filt,'.nc',sep=''))

	if(model=='CT2017'|model=='CT2019B'){

		dummyvar='model_bg'
		stadat=data.frame(cbind(ncvar_get(stanc,'time_decimal'),t(ncvar_get(stanc,'time_components')),ncvar_get(stanc,dummyvar)*1E6)) ; colnames(stadat)=c('date','year','mon','day','hour','min','sec','co2')
		stadat$co2=(ncvar_get(stanc,'model_bg')*comps[1]+ncvar_get(stanc,'model_ff')*comps[2]+ncvar_get(stanc,'model_ocean')*comps[3]+ncvar_get(stanc,'model_bio')*comps[4]+ncvar_get(stanc,'model_fires')*comps[5])*1E6 # replace obs with model

	} else if(model=='CTE2018'){

		if(labid==4|labid==426){ # did not request SIO_O2 and SIO_CDK files - instead use NOAA files from same stations as it should not matter for model and not used in gradient calc
			stanc=nc_open(paste(modobspackdir,'/simulated-obs/co2_',tolower(sta),'_',type,'-',meth,'_1_',filt,'.nc',sep=''))
		} else {
			stanc=nc_open(paste(modobspackdir,'/simulated-obs/co2_',tolower(sta),'_',type,'-',meth,'_',labid,'_',filt,'.nc',sep=''))
		}
		stadat=data.frame(cbind(ncvar_get(stanc,'time_decimal'),t(ncvar_get(stanc,'time_components')),ncvar_get(stanc,'modelsamplesmean')*1E6)) ; colnames(stadat)=c('date','year','mon','day','hour','min','sec','co2')
		stadat$co2=(ncvar_get(stanc,'modelsamplesensemble')[1,]*comps[1]+ncvar_get(stanc,'modelsamplesensemble')[2,]*comps[2]+ncvar_get(stanc,'modelsamplesensemble')[4,]*comps[3]+
                    ncvar_get(stanc,'modelsamplesensemble')[3,]*comps[4]+ncvar_get(stanc,'modelsamplesensemble')[5,]*comps[5])*1E6 # 5 component concentrations: bg, foss, bio, oce, bb
		stadat$co2[stadat$co2>1E3]=NA # 1000 ppm
		stadat$co2[stadat$co2<(-1E3)]=NA # -1000 ppm
		stadat=stadat[ncvar_get(stanc,'time_decimal')<2018,] # files have zeros for 2018

	} else if(model=='CTE2020'){

		stadat=data.frame(cbind(ncvar_get(stanc,'time_decimal'),t(ncvar_get(stanc,'time_components')),ncvar_get(stanc,'modelsamplesmean')*1E6)) ; colnames(stadat)=c('date','year','mon','day','hour','min','sec','co2')
		stadat$co2=(ncvar_get(stanc,'modelsamplesensemble')[1,]*comps[1]+ncvar_get(stanc,'modelsamplesensemble')[2,]*comps[2]+ncvar_get(stanc,'modelsamplesensemble')[4,]*comps[3]+
                    ncvar_get(stanc,'modelsamplesensemble')[3,]*comps[4]+ncvar_get(stanc,'modelsamplesensemble')[5,]*comps[5])*1E6 # 5 component concentrations: bg, foss, bio, oce, bb
		stadat$co2[stadat$co2>1E3]=NA
		stadat$co2[stadat$co2<(-1E3)]=NA

	} else if(model=='CAMSv20r1'){

		# for CAMS, first read in obs
		stanc=nc_open(paste(obsobspackdir,'/co2_',tolower(sta),'_',type,'-',meth,'_',labid,'_',filt,'.nc',sep=''))
                stadat=data.frame(cbind(ncvar_get(stanc,'time_decimal'),t(ncvar_get(stanc,'time_components')),ncvar_get(stanc,'value')*1E6)) ; colnames(stadat)=c('date','year','mon','day','hour','min','sec','co2')
		system('rm cams_station_data_temp.txt')
		if(case==1){ 
			modobspackfile=paste(modobspackdir,'/simulated-obs',subdir,'/',modobspacktotfile,sep='')
		} else if(case==3){
			modobspackfile=paste(modobspackdir,'/simulated-obs',subdir,'/',modobspackfosfile,sep='')
		} else if(case==4){
			modobspackfile=paste(modobspackdir,'/simulated-obs',subdir,'/',modobspackocefile,sep='')
		} else if(case==5){
			modobspackfile=paste(modobspackdir,'/simulated-obs',subdir,'/',modobspacklndfile,sep='')
		}
		system(paste('grep co2_',tolower(sta),'_',type,'-',meth,'_',labid,'_',filt,' ',modobspackfile,' > cams_station_data_temp.txt',sep=''))
		camsin=read.table('cams_station_data_temp.txt',stringsAsFactors=F) # obspackid, posterior co2, flag (all 0s)
		ids=ncvar_get(stanc,'obspack_id')
		stadat$co2=rep(NA,nrow(stadat)) # remove old
                stadat$co2[is.element(ids,camsin[,1])]=camsin[,2][match(ids[is.element(ids,camsin[,1])],camsin[,1])]

    } else if(model=='MIROC'){

		tm=data.frame(t(ncvar_get(stanc,'time_components'))) ; colnames(tm)=c('year','mon','day','hour','min','sec')
		tmdatetime=as.POSIXlt(ISOdatetime(tm$year,tm$mon,tm$day,tm$hour,tm$min,tm$sec,tz='UTC'),tz='UTC')
		tmdectime=tmdatetime$year+1900+as.numeric(tmdatetime-trunc(tmdatetime,'year'),units='days')/as.numeric((ISOdatetime(tmdatetime$year+1901,1,1,0,0,0,tz='UTC')-trunc(tmdatetime,'year')),units='days')
		stadat=data.frame(cbind(tmdectime,t(ncvar_get(stanc,'time_components')),ncvar_get(stanc,'Observation'))) ; colnames(stadat)=c('date','year','mon','day','hour','min','sec','co2')
		stadat$co2=ncvar_get(stanc,'CO2FF')*comps[2]+ncvar_get(stanc,'CO2Ocn_apos')*comps[3]+ncvar_get(stanc,'CO2Bio_apos')*comps[4] # replace obs with model # 2 = FF, 3 = ocean, 4 = bio
		if(case==1){
			stadat$co2=stadat$co2-as.numeric(ncvar_get(stanc,'CO2bias_apos')) # 1391.02
		}

	} else if(substr(model,1,10)=='CarboScope'){

		# encode CarboScope file names
		if(meth=='insitu'){ let1='H' }else if(meth=='flask'){ let1='F' } # First big letter: F = Flask (discrete sampling events), H = Continuous (original hourly, half-hourly, etc.)
		if(type=='surface'){ # Second small letter: Platform / station class
			let2='b' 
			if(sta=='HBA'|sta=='SYO'|sta=='MAA'|sta=='PSA'|sta=='MQA'|sta=='CRZ'|sta=='CGO'|sta=='AMS') let2='r'
			if(sta=='CYA'|sta=='BHD'|sta=='CPT') let2='s'
		}else if(type=='shipboard'){ 
			let2='o' 
		} 
		# Capital letters (or numbers): STATION CODE 
		if(lab=='NOAA'){ # First small letter after station code: Measuring institution
			labcode='c' 
		} else if(lab=='CSIRO'){ 
			labcode='a'
		} else if(lab=='SIO_CDK'){ 
			labcode='k'
		} else if(lab=='SIO_O2'){ 
			labcode='s'
		} else if(lab=='LSCE'){ 
			labcode='l'
		} else if(lab=='TU'|lab=='NIWA'|lab=='SAWS'){ 
			labcode='x'
		} 
		filename=paste(let1,let2,sta,labcode,'o.co2.ts',sep='') # Second small letter after station code (if any): Source data base # o	ObsPack (GLOBALVIEW+)
		stadatin=read.table(paste(modobspackdir,'/simulated-obs',subdir,'/',filename,sep=''))
		names(stadatin)=c('date','year','mon','day','hour','min','sec','lat','lon','hgt','obs','sigma','co2')
		if(filename=='FbSPOko.co2.ts') stadatin=stadatin[stadatin$year>1956,] # first 10 values repeated
		stadat=stadatin[,c(1:7,13)]

	} else if(model=='TM5pCO2'){

		stanc=nc_open(paste(modobspackdir,'/simulated-obs/co2_',tolower(sta),'_',type,'-',meth,'_',labid,'_',filt,'.nc',sep=''))
        stadat=data.frame(cbind(ncvar_get(stanc,'time_decimal'),t(ncvar_get(stanc,'time_components')),ncvar_get(stanc,'model_ocean')*1E6)) ; colnames(stadat)=c('date','year','mon','day','hour','min','sec','co2') 

	}

	# calculate monthly means
	staco2=aggregate(stadat$co2,by=list(year=stadat$year,month=stadat$mon),mean,na.rm=T) ; staco2=staco2[order(staco2$year+staco2$mon/12),]
	names(staco2)=c('year','month','co2')

	# accumulate model results
	if(!is.null(staco2)){
		oldnames=colnames(allsta)
		allsta=merge(allsta,staco2,by=c('year','month'),all=T)
		colnames(allsta)=c(oldnames,paste(sta,'_',lab,'_',meth,sep=''))
		# aggregate by season
		seasco2=aggregate(allsta[,ncol(allsta)],by=list(seas=monseas,year=seasyear),mean,na.rm=T)$x # with na.rm=T so returns value even if only 1 month present (< 2 filtered below)
		annco2=aggregate(allsta[,ncol(allsta)],by=list(year=allsta$year),mean)$x # with na.rm=T so only returns value if all 3 months present
		oldnames=colnames(allseas)
		allseas=cbind(allseas,seasco2)
		colnames(allseas)=c(oldnames,paste(sta,'_',lab,'_',meth,sep=''))
		alllat=c(alllat,stationlist$lat[i])
		alllon=c(alllon,stationlist$lon[i])
		allalt=c(allalt,stationlist$masl[i])
		oldnames=colnames(allann)
        allann=cbind(allann,annco2)
        colnames(allann)=c(oldnames,paste(sta,'_',lab,'_',meth,sep=''))
	}
	if(substr(model,1,10)!='CarboScope'&model!='TM5pCO2') nc_close(stanc)

	if(sta=='SPO'&lab=='NOAA'&meth=='insitu'){
		# write out NOAA in situ SPO record for subtraction in process_aircraft_models.ipynb
		sel=stadat$year>=trunc(meanwin[1])&stadat$year<=trunc(meanwin[2])
        write(paste(names(stadat),collapse=' '),paste('../data/simulated-obs/',mclname,subdir,'/SPO_NOAA_in_situ.txt',sep=''))
		write(t(stadat[sel,]),paste('../data/simulated-obs/',mclname,subdir,'/SPO_NOAA_in_situ.txt',sep=''),append=T,ncol=ncol(stadat))
	}

} # loop on station record

        
# filter and write out
allseas=allseas[allnmon>1,] # since starting on Jan and ending on Dec, this will keep the first JF, but exclude the last D
allann[allnmonann<10,3:ncol(allann)]=NA # do not average if more than 2 months missing
sel=allsta$year>=trunc(meanwin[1])&allsta$year<=trunc(meanwin[2])
write(paste(names(allsta),collapse=' '),paste('../data/simulated-obs/',mclname,subdir,'/SO_CO2_monthly.txt',sep=''))
write(t(allsta[sel,]),paste('../data/simulated-obs/',mclname,subdir,'/SO_CO2_monthly.txt',sep=''),append=T,ncol=ncol(allsta))

# Calc diffs for all stations using SPO NOAA in situ as the reference
alldiffsmon=allsta # monthly resolution
refcol=which(names(allsta)=='SPO_NOAA_insitu')
for(i in c(3:ncol(allsta))){
	alldiffsmon[,i]=allsta[,i]-allsta[,refcol]
}
alldiffsann=aggregate(alldiffsmon[,3:ncol(alldiffsmon)],by=list(year=alldiffsmon$year),mean,na.rm=T) # allows incomplete years
nann=aggregate(!is.na(alldiffsmon[,3:ncol(alldiffsmon)]),by=list(year=alldiffsmon$year),sum) # counts # of months present
alldiffsann[nann<8]=NA # need at least 8 months to count
alldiffsmonclim=aggregate(alldiffsmon[alldiffsmon$year>=meanwin[1]&alldiffsmon$year<=meanwin[2],3:ncol(alldiffsmon)],by=list(month=alldiffsmon$month[alldiffsmon$year>=meanwin[1]&alldiffsmon$year<=meanwin[2]]),mean,na.rm=T) # allows missing months
alldiffsmonclimcomp=apply(alldiffsmonclim[,which(stationlist$use==2)+1],1,mean,na.rm=T)

alldiffs=allseas # 4-season resolution
refcol=which(names(allseas)=='SPO_NOAA_insitu')
for(i in c(3:ncol(allseas))){
	alldiffs[,i]=allseas[,i]-allseas[,refcol]
}

alldiffsnew=data.frame(cbind(aggregate(monseas,by=list(seas=monseas,year=seasyear),mean)$x,aggregate(yrfrac,by=list(seas=monseas,year=seasyear),mean)$x,aggregate(alldiffsmon[,3:ncol(alldiffsmon)],
    by=list(seas=monseas,year=seasyear),mean,na.rm=T)[,3:ncol(alldiffsmon)]))
allnmonnew=data.frame(cbind(aggregate(monseas,by=list(seas=monseas,year=seasyear),mean)$x,aggregate(yrfrac,by=list(seas=monseas,year=seasyear),mean)$x,aggregate(!is.na(alldiffsmon[,3:ncol(alldiffsmon)]),
    by=list(seas=monseas,year=seasyear),sum)[,3:ncol(alldiffsmon)]))
alldiffsnew[,3:ncol(alldiffsnew)][allnmonnew[,3:ncol(alldiffsnew)]<minnmon]=NA ## allow seasons with one missing month, but not two
colnames(alldiffsnew)[1:2]=c('seas','yrfrac')
alldiffs=alldiffsnew

# Calc long term mean and sd of diffs, from seasonal differences
meandiff=apply(alldiffs[alldiffs$yrfrac>=meanwin[1]&alldiffs$yrfrac<=meanwin[2],],2,mean,na.rm=T)
sddiff=apply(alldiffs[alldiffs$yrfrac>=meanwin[1]&alldiffs$yrfrac<=meanwin[2],],2,sd,na.rm=T)
sumdiff=apply(!is.na(alldiffs[alldiffs$yrfrac>=meanwin[1]&alldiffs$yrfrac<=meanwin[2],]),2,sum,na.rm=T)
meandiffseas=NULL
sddiffseas=NULL
for(seas in c(1:4)){
	meandiffseas=rbind(meandiffseas,apply(alldiffs[alldiffs$yrfrac>=meanwin[1]&alldiffs$yrfrac<=meanwin[2]&alldiffs$seas==seas,],2,mean,na.rm=T))
	if(sum(!is.na(alldiffs[alldiffs$yrfrac>=meanwin[1]&alldiffs$yrfrac<=meanwin[2]&alldiffs$seas==seas,'SPO_NOAA_insitu']))>2){ # need more than 2 years to calc SD
		sddiffseas=rbind(sddiffseas,apply(alldiffs[alldiffs$yrfrac>=meanwin[1]&alldiffs$yrfrac<=meanwin[2]&alldiffs$seas==seas,],2,sd,na.rm=T))
	} else {
		sddiffseas=rbind(sddiffseas,rep(0.01,ncol(alldiffs))) # will not show up on plot and all sta given equal weights in poly fit
	}
}
meandiffseas=data.frame(meandiffseas)
sddiffseas=data.frame(sddiffseas)
sddiffseas$seas=meandiffseas$seas

# Make composite difference:
# monthly:
alldiffscompmon=cbind(alldiffsmon[,1:2],apply(alldiffsmon[,which(stationlist$use==2)+2],1,mean,na.rm=T)) # if one station missing, still calculate
names(alldiffscompmon)=c('year','month','comp')
alldiffscompmonclim=aggregate(alldiffscompmon,by=list(month=alldiffscompmon$month),mean,na.rm=T)
alldiffscompmonclim=alldiffscompmonclim[,3:4] # month and comp
# annual:
alldiffscompann=data.frame(cbind(alldiffsann[,1],apply(alldiffsann[,which(stationlist$use==2)+1],1,mean,na.rm=T))) # if one station missing, still calculate
names(alldiffscompann)=c('year','comp')
# by season:
alldiffscomp=cbind(alldiffs[,1:2],apply(alldiffs[,which(stationlist$use==2)+2],1,mean,na.rm=T)) # if one station missing, still calculate
names(alldiffscomp)=c('seas','yrfrac','comp')


# Make plots:

cols=rep(brewer.pal(12,'Paired'),4)
pchs=rep(16,length(cols))
pchs[which(stationlist$lab=='SIO_CDK')]=15
pchs[which(stationlist$lab=='CSIRO')]=17
pchs[which(stationlist$lab=='SIO_O2')]=18
pchs[which(stationlist$lab=='TU')]=0
pchs[which(stationlist$lab=='LSCE')]=1
pchs[which(stationlist$lab=='SAWS')]=2
pchs[which(stationlist$lab=='NIWA')]=5
numsta=ncol(allsta)-2
bgs=cols # these are fill colors for pch 21-25
cols[which(stationlist$method=='underway')]=rgb(0,0,0) # these are edge colors for pch 21-25

compflag=paste(tolower(substr(stationlist$station[stationlist$use==2],1,1)),collapse='')
refflag='s'
ylm=c(-0.75,0.75) # ppm for plot y-axes

# Plot lat grad of diffs

seasname=c('DJF','MAM','JJA','SON')
for(seas in c(1,3)){

png(paste('../data/simulated-obs/',mclname,subdir,'/so_station_co2diff_gradient_',meanwin[1],'-',meanwin[2],'_',seasname[seas],'.png',sep=''),height=1200,width=1200,pointsize=30)
par(mar=c(5,5,4,2)+0.1)

plot(as.numeric(stationlist$lat),meandiffseas[meandiffseas$seas==seas,3:ncol(meandiffseas)],type='n',xlim=c(-90,max(as.numeric(stationlist$lat))),ylim=ylm,
     main=substitute(paste('Southern Ocean Seasonal ',CO[2],' Gradient - ',v),list(v=seasname[seas])),ylab=expression(paste(Delta,CO[2],' (ppm)')),xlab='Latitude (degrees N)',cex.main=1.2,cex.axis=1.2,cex.lab=1.2)
abline(h=0)
mtext(paste(stationlist$station[refcol-2],' ',stationlist$lab[refcol-2],' ',stationlist$method[refcol-2],' Subtracted, ',meanwin[1],'-',meanwin[2],' Average and SD',sep=''),3,0.3)
segwd=0.2
stasel=c(1:numsta)[c(1:numsta)+2!=refcol&!is.na(meandiffseas[meandiffseas$seas==seas,3:length(meandiff)])]
y=as.numeric(meandiffseas[meandiffseas$seas==seas,3:length(meandiff)][stasel])
x=stationlist$lat[stasel]
w=as.numeric(1/sddiffseas[sddiffseas$seas==seas,3:length(meandiff)][stasel]^2)
new=data.frame(x=seq(-90,ceiling(max(x)),1))
lines(new$x,predict.lm(lm(y ~ poly(x,3),weights=w),new),lwd=2)
for(i in stasel){
	segments(stationlist$lat[i],meandiffseas[meandiffseas$seas==seas,i+2]-sddiffseas[sddiffseas$seas==seas,i+2],stationlist$lat[i],meandiffseas[meandiffseas$seas==seas,i+2]+sddiffseas[sddiffseas$seas==seas,i+2],col=cols[i])
	segments(stationlist$lat[i]-segwd,meandiffseas[meandiffseas$seas==seas,i+2]-sddiffseas[sddiffseas$seas==seas,i+2],stationlist$lat[i]+segwd,meandiffseas[meandiffseas$seas==seas,i+2]-sddiffseas[sddiffseas$seas==seas,i+2],col=cols[i])
	segments(stationlist$lat[i]-segwd,meandiffseas[meandiffseas$seas==seas,i+2]+sddiffseas[sddiffseas$seas==seas,i+2],stationlist$lat[i]+segwd,meandiffseas[meandiffseas$seas==seas,i+2]+sddiffseas[sddiffseas$seas==seas,i+2],col=cols[i])
        points(stationlist$lat[i],meandiffseas[meandiffseas$seas==seas,i+2],pch=pchs[i],col=cols[i],cex=1.5,lwd=2)
	if(any(!is.na(meandiffseas[meandiffseas$seas==seas,i+2]))) text(stationlist$lat[i],ylm[1],stationlist$sta[i],col=cols[i],srt=90,pos=3,offset=0,adj=c(0.5,0))
}
labsel=stationlist$lab[stasel]; pchsel=pchs[stasel]; pchsel=pchsel[!duplicated(labsel)]; labsel=labsel[!duplicated(labsel)]
legend('topleft',labsel,pch=pchsel,cex=1.0,col='gray30',ncol=2)

dev.off()

} # loop on season


# Plot composite seasonal cycle

png(paste('../data/simulated-obs/',mclname,subdir,'/so_station_composite_co2diff_seascycle_',compflag,'-',refflag,'_',meanwin[1],'-',meanwin[2],'.png',sep=''),height=1200,width=1800,pointsize=30)
par(mar=c(5,5,4,2)+0.1)

plot(seq(0.5,11.5),alldiffsmonclim[c(7:12,1:6),3],type='n',xlim=c(0,12),ylim=ylm,main=expression(paste('Southern Ocean Climatological Monthly Mean ',CO[2])),ylab=expression(paste(Delta,CO[2],' (ppm)')),xlab='Month',cex.main=1.2,cex.lab=1.2,axes=F)
box()
axis(2,cex.axis=1.2)
axis(1,at=c(0:12),labels=F,cex.axis=1.5)
axis(1,seq(0.5,11.5),labels=c('J','A','S','O','N','D','J','F','M','A','M','J'),cex.axis=1.3,tick=F)
mtext(paste(stationlist$station[refcol-2],' ',stationlist$lab[refcol-2],' ',stationlist$method[refcol-2],' Subtracted, ',meanwin[1],'-',meanwin[2],sep=''),3,0.3)
abline(h=0)
for(i in c(1:numsta)[stationlist$use==2]){
        points(seq(0.5,11.5),alldiffsmonclim[c(7:12,1:6),i+1],type='b',pch=pchs[i],col=cols[i],cex=1.5,lwd=2)
}
lines(seq(0.5,11.5),alldiffsmonclimcomp[c(7:12,1:6)],col='grey10',lwd=10)

legend('topleft',names(alldiffsmonclim)[c(1:numsta)[stationlist$use==2]+1],col=cols[c(1:numsta)[stationlist$use==2]],pch=pchs[c(1:numsta)[stationlist$use==2]],cex=1,pt.cex=1,lwd=2,ncol=3)

dev.off()

} # loop on case

if(substr(model,1,4)=='CAMS') system('rm cams*.txt')

} # loop on model
[1] "CAMSv20r1"
[1] "CAMSv20r1/FF"
[1] "CAMSv20r1/OCEAN"
[1] "CAMSv20r1/LAND"
[1] "CT2017"
[1] "CT2017/BG"
[1] "CT2017/FF"
[1] "CT2017/OCEAN"
[1] "CT2017/LAND"
[1] "CT2019B"
[1] "CT2019B/BG"
[1] "CT2019B/FF"
[1] "CT2019B/OCEAN"
[1] "CT2019B/LAND"
[1] "CTE2018"
[1] "CTE2018/FF"
[1] "CTE2018/OCEAN"
[1] "CTE2018/LAND"
[1] "CTE2020"
[1] "CTE2020/FF"
[1] "CTE2020/OCEAN"
[1] "CTE2020/LAND"
[1] "MIROC"
[1] "MIROC/FF"
[1] "MIROC/OCEAN"
[1] "MIROC/LAND"
[1] "s99oc_ADJocI40S_v2020"
[1] "s99oc_ADJocI40S_v2020/FF"
[1] "s99oc_ADJocI40S_v2020/OCEAN"
[1] "s99oc_ADJocI40S_v2020/LAND"
[1] "s99oc_SOCCOM_v2020"
[1] "s99oc_SOCCOM_v2020/FF"
[1] "s99oc_SOCCOM_v2020/OCEAN"
[1] "s99oc_SOCCOM_v2020/LAND"
[1] "s99oc_v2020"
[1] "s99oc_v2020/FF"
[1] "s99oc_v2020/OCEAN"
[1] "s99oc_v2020/LAND"
[1] "TM5-Flux-mrf"
[1] "TM5-Flux-m0f"
[1] "TM5-Flux-mwf"
[1] "TM5-Flux-mmf"