Objectives
By no means is this an exhaustive how to use pic and cdo but may offer a start point for new users.
PIC
So PIC is PNNL’s Linux machine, it has 520 Intel Haswell-based nodes in quad form. Each node features a dual-socket Intel Haswell E5-2670v3 CPU (12-cores-per-socket, running at 2.3 GHz) with 64 GB of 2133 MHz ECC memory, an FDR Infiniband network card, and 480 GB local solid-state drive storage. For PIC help check out this confluence page. Tim Carlson at PIC support is a great resource when people at JGRCI are unable to answer your questions. When I have questions I usually talk to Robert, Caleb, or Pralit. For pic help google linux or unix commands. PIC is a useful resource but is not the silver bullet answer to computing problems, there are things that will be better to run on your local machine even if it is really slow.
I use PIC to run
- GCAM (single GCAM runs and parallel GCAM runs).
- Big R scripts (runs that would take several hours on my local machine).
- To processes netcdf files that are too large to download to my local machine or that I will want to process with CDO.
- Making maps.
What I do not use pic for
- To do interactive analyses.
- To make intermediate plots (the only time I use pic to make plots is if I am confident that the plots are not going to change and you will need to write them to a directory on pic and transfer them to your local machine in order to look at it).
When you first log on to pic you are going to end up at the /people/usrnameXXX directory. I use this location for scratch work/to save intermediate files because data storage is limited and temporary, I think things get deleted every so often. For the files/projects you want to keep around make a directory in /pic/projects/GCAM/. Note that if you want to make that directory public and you have a local windows machine do not use WINscp to make directories/move the files. It will cause problems that can only be resolved by contacting pic support.
If you are running something big on pic like a script that will process all a large set of netcdf files you are going to have to submit an sbatch file see here which will submit the batch to the pic que. Here is an example of an sbatch script called run.zsh
#!/bin/zsh
#SBATCH -t 15:0:0
#SBATCH -n 1
#SBATCH -c 1
#SBATCH -N 1
#SBATCH -J ESM_processing
#SBATCH -A IHESD
## This configuration gives us:
## a time limit of 15 hours
## 1 task
## 1 cpu per task
## on a single node
## names the job 'GCAM'
## on account
#First make sure the module commands are available.
source /etc/profile.d/modules.sh
#Set up your environment you wish to run in with module commands.
module load R/3.4.3
#Next unlimit system resources, and set any other environment variables you need.
unlimit
Rscript ./B2.get-annual-ocean-flux.R
To run sbatch scripts you are going to need hours on a pic account. Talk to pic support and who you are working on the project with to figure out hours and such. You can check your account allocation with.
[dorh012@constance03 ESM_processing_code]$ module load sbank
[dorh012@constance03 ESM_processing_code]$ sbank balance statement
You can also run a few code on the log in node. But do not abuse this because you run the risk of getting in trouble with pic if you are trying to do to much on the log in node.
Things that are okay to do on the log in node
- Move files / make directories
- Test out R scripts (when I do this I process dummy data or a single file)
- Look some netcdf files
This that are not okay to do on the log in node
- Process lots of files
- Run GCAM or Hector a bunch of times
- Open really large data files (I tried to unzip and open a large file from a collaborator on the pic log in node and it flooded the node and started to use 90% of pic’s computing power, needless to say the pic team was not happy with me).
How to load R on pic.
When developing R code to use on pic I usually work in R studio on my local machine, then copy and paste it into the interactive R module on pic.
To see what is aviable on pic use module avail
module avail R
Launch R version 3.4.3 on pic.
module load R/3.4.3
R
CDO
CDO is a collection of command line data operators for processing netcdfs (see website for information)[https://code.mpimet.mpg.de/projects/cdo/embedded/index.html]. CDO is great to process the cmip output data where there is inconsistent data structures (different relative start dates, different spatial projections etc) because it will perform operations based on information included in the meta data structure.
The CDO on pic lives at
/share/apps/netcdf/4.3.2/gcc/4.4.7/bin/cdo
How you would run cdo in command line on pic. (you can chain operators together)
cdo operator infile outfile
For example the global mean of a single file using cdo. The results will be stored in test.nc.
[dorh012@constance03 ~]$ /share/apps/netcdf/4.3.2/gcc/4.4.7/bin/cdo fldmean /pic/projects/GCAM/CMIP5-CHartin/CMIP5_RCP45/tas/tas_Amon_CESM1-BGC_rcp45_r1i1p1_200601-210012.nc ./test.nc
Warning (cdfScanVarAttributes) : NetCDF: Variable not found - areacella
cdo fldmean: Processed 63037440 values from 1 variable over 1140 timesteps ( 1.02s )
So with one line of code I was able to calculate the weighted global mean temperature for a netcdf. However if you want to process more than 10 files the command line option gets kind of clunky and you will not be able to generate new file names using cdo so paring cdo and R together is very powerful.
CDO + R
Examples of cdo + R processing code can be on GitHub at https://github.com/kdorheim/CDOexamples. But you are going to want to use the function system2 to execute the cdo code, it will look like the following format.
system2('path/to/cdo/exe', args = c('operator', 'path/to/inifile.nc', './test.nc'), stdout = TRUE, stderr = TRUE)
Here is what you would run in interactive R to repeat our earlier example in R.
CDO_EXE <- '/share/apps/netcdf/4.3.2/gcc/4.4.7/bin/cdo'
input_nc <- "/pic/projects/GCAM/CMIP5-CHartin/CMIP5_RCP45/tas/tas_Amon_CESM1-BGC_rcp45_r1i1p1_200601-210012.nc"
output_nc <- "./test.nc"
system2(CDO_EXE, args = c("fldmean", input_nc, output_nc), stdout = TRUE, stderr = TRUE)
Then to extract the results from the new netcdf you are going to use the package ncdf4. I recommend looking into that package documentation because it if a pretty valuable tool.
How to extract the results from a netcdf in R.
# For the input netcdf file.
nc_in <- nc_open(input_nc)
tas <- ncvar_get(nc_in, 'tas')
time <- ncvar_get(nc, 'time')
# For the output netcdf.
nc <- nc_open(output_nc)
tas <- ncvar_get(nc, 'tas')
time <- ncvar_get(nc, 'time')
What is the difference in these two netcdf files (dimension)?
How I like to structure my CDO + R code
I typically use two scripts (and I want to stress that this is just my preference on how to set it up there is going to be lots of different ways to doing it).
My first script locates and sorts the netcdfs I want to process, this is because of how CMIP5 data was stored on pic. It stores information about the cmip files to process in a csv file.
In my second script I import my Csv file containing the netcdf files to process, define my function that contains the system2 cdo call, and then use an apply family command to execute my processing function on the list of the netcdfs to process. I save the final results in a csv file that is easy to download onto my local machine.
CDO & R tips
- Write tests into your function and try to give informative error messages, otherwise you can spend a lot of time trying to debug your code.
- CDO generates lots of intermediate netcdf files, at some point you should clean these up but during development it can be useful to keep the intermediate netcdf files and check them.
Practice Exercise
What is the difference between the global average temperature for a single year calculated by cdo fldgen vs mean in R? Is this a surprise, why or why not?
Hint - if you used ncvar_get() to import the girded monthly data into R the data is going to be an array with three dimensions, [lon, lat, time].
LS0tCnRpdGxlOiAiSW50cm8gdG8gUElDICYgbmV0Y2RmIHByb2Nlc3NpbmciCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBPYmplY3RpdmVzCgpCeSBubyBtZWFucyBpcyB0aGlzIGFuIGV4aGF1c3RpdmUgaG93IHRvIHVzZSBwaWMgYW5kIGNkbyBidXQgbWF5IG9mZmVyIGEgc3RhcnQgcG9pbnQgZm9yIG5ldyB1c2Vycy4gIAoKIyMjIFBJQwoKU28gUElDIGlzIFBOTkwncyBMaW51eCBtYWNoaW5lLCBpdCBoYXMgNTIwIEludGVsIEhhc3dlbGwtYmFzZWQgbm9kZXMgaW4gcXVhZCBmb3JtLiBFYWNoIG5vZGUgZmVhdHVyZXMgYSBkdWFsLXNvY2tldCBJbnRlbCBIYXN3ZWxsIEU1LTI2NzB2MyBDUFUgKDEyLWNvcmVzLXBlci1zb2NrZXQsIHJ1bm5pbmcgYXQgMi4zIEdIeikgd2l0aCA2NCBHQiBvZiAyMTMzIE1IeiBFQ0MgbWVtb3J5LCBhbiBGRFIgSW5maW5pYmFuZCBuZXR3b3JrIGNhcmQsIGFuZCA0ODAgR0IgbG9jYWwgc29saWQtc3RhdGUgZHJpdmUgc3RvcmFnZS4gRm9yIFBJQyBoZWxwIGNoZWNrIG91dCB0aGlzIFtjb25mbHVlbmNlIHBhZ2VdKGh0dHBzOi8vY29uZmx1ZW5jZS5wbm5sLmdvdi9jb25mbHVlbmNlL2Rpc3BsYXkvUkMvUmVzZWFyY2grQ29tcHV0aW5nK0tub3dsZWRnZSkuIFRpbSBDYXJsc29uIGF0IFBJQyBzdXBwb3J0IGlzIGEgZ3JlYXQgcmVzb3VyY2Ugd2hlbiBwZW9wbGUgYXQgSkdSQ0kgYXJlIHVuYWJsZSB0byBhbnN3ZXIgeW91ciBxdWVzdGlvbnMuIFdoZW4gSSBoYXZlIHF1ZXN0aW9ucyBJIHVzdWFsbHkgdGFsayB0byBSb2JlcnQsIENhbGViLCBvciBQcmFsaXQuIEZvciBwaWMgaGVscCBnb29nbGUgbGludXggb3IgdW5peCBjb21tYW5kcy4gUElDIGlzIGEgdXNlZnVsIHJlc291cmNlIGJ1dCBpcyBub3QgdGhlIHNpbHZlciBidWxsZXQgYW5zd2VyIHRvIGNvbXB1dGluZyBwcm9ibGVtcywgdGhlcmUgYXJlIHRoaW5ncyB0aGF0IHdpbGwgYmUgYmV0dGVyIHRvIHJ1biBvbiB5b3VyIGxvY2FsIG1hY2hpbmUgZXZlbiBpZiBpdCBpcyByZWFsbHkgc2xvdy4KCkkgdXNlIFBJQyB0byBydW4gCgoxLiBHQ0FNIChzaW5nbGUgR0NBTSBydW5zIGFuZCBwYXJhbGxlbCBHQ0FNIHJ1bnMpLgoyLiBCaWcgUiBzY3JpcHRzIChydW5zIHRoYXQgd291bGQgdGFrZSBzZXZlcmFsIGhvdXJzIG9uIG15IGxvY2FsIG1hY2hpbmUpLgozLiBUbyBwcm9jZXNzZXMgbmV0Y2RmIGZpbGVzIHRoYXQgYXJlIHRvbyBsYXJnZSB0byBkb3dubG9hZCB0byBteSBsb2NhbCBtYWNoaW5lIG9yIHRoYXQgSSB3aWxsIHdhbnQgdG8gcHJvY2VzcyB3aXRoIENETy4gCjQuIE1ha2luZyBtYXBzLgoKPGJyPgoKV2hhdCBJIGRvIG5vdCB1c2UgcGljIGZvciAKCjEuIFRvIGRvIGludGVyYWN0aXZlIGFuYWx5c2VzLiAKMi4gVG8gbWFrZSBpbnRlcm1lZGlhdGUgcGxvdHMgKHRoZSBvbmx5IHRpbWUgSSB1c2UgcGljIHRvIG1ha2UgcGxvdHMgaXMgaWYgSSBhbSBjb25maWRlbnQgdGhhdCB0aGUgcGxvdHMgYXJlIG5vdCBnb2luZyB0byBjaGFuZ2UgYW5kIHlvdSB3aWxsIG5lZWQgdG8gd3JpdGUgdGhlbSB0byBhIGRpcmVjdG9yeSBvbiBwaWMgYW5kIHRyYW5zZmVyIHRoZW0gdG8geW91ciBsb2NhbCBtYWNoaW5lIGluIG9yZGVyIHRvIGxvb2sgYXQgaXQpLgoKCldoZW4geW91IGZpcnN0IGxvZyBvbiB0byBwaWMgeW91IGFyZSBnb2luZyB0byBlbmQgdXAgYXQgdGhlIGAvcGVvcGxlL3Vzcm5hbWVYWFhgIGRpcmVjdG9yeS4gSSB1c2UgdGhpcyBsb2NhdGlvbiBmb3Igc2NyYXRjaCB3b3JrL3RvIHNhdmUgaW50ZXJtZWRpYXRlIGZpbGVzIGJlY2F1c2UgZGF0YSBzdG9yYWdlIGlzIGxpbWl0ZWQgYW5kIHRlbXBvcmFyeSwgSSB0aGluayB0aGluZ3MgZ2V0IGRlbGV0ZWQgZXZlcnkgc28gb2Z0ZW4uIEZvciB0aGUgZmlsZXMvcHJvamVjdHMgeW91IHdhbnQgdG8ga2VlcCBhcm91bmQgbWFrZSBhIGRpcmVjdG9yeSBpbiBgL3BpYy9wcm9qZWN0cy9HQ0FNL2AuIE5vdGUgdGhhdCBpZiB5b3Ugd2FudCB0byBtYWtlIHRoYXQgZGlyZWN0b3J5IHB1YmxpYyBhbmQgeW91IGhhdmUgYSBsb2NhbCB3aW5kb3dzIG1hY2hpbmUgZG8gbm90IHVzZSBXSU5zY3AgdG8gbWFrZSBkaXJlY3Rvcmllcy9tb3ZlIHRoZSBmaWxlcy4gSXQgd2lsbCBjYXVzZSBwcm9ibGVtcyB0aGF0IGNhbiBvbmx5IGJlIHJlc29sdmVkIGJ5IGNvbnRhY3RpbmcgcGljIHN1cHBvcnQuIAoKPGJyPgoKSWYgeW91IGFyZSBydW5uaW5nIHNvbWV0aGluZyBiaWcgb24gcGljIGxpa2UgYSBzY3JpcHQgdGhhdCB3aWxsIHByb2Nlc3MgYWxsIGEgbGFyZ2Ugc2V0IG9mIG5ldGNkZiBmaWxlcyB5b3UgYXJlIGdvaW5nIHRvIGhhdmUgdG8gc3VibWl0IGFuIHNiYXRjaCBmaWxlIFtzZWUgaGVyZV0oaHR0cHM6Ly9jb25mbHVlbmNlLnBubmwuZ292L2NvbmZsdWVuY2UvZGlzcGxheS9SQy9DcmVhdGluZythK0pvYitTY3JpcHQpIHdoaWNoIHdpbGwgc3VibWl0IHRoZSBiYXRjaCB0byB0aGUgcGljIHF1ZS4gSGVyZSBpcyBhbiBleGFtcGxlIG9mIGFuIHNiYXRjaCBzY3JpcHQgY2FsbGVkIHJ1bi56c2ggCgpgYGAKIyEvYmluL3pzaAojU0JBVENIIC10IDE1OjA6MAojU0JBVENIIC1uIDEKI1NCQVRDSCAtYyAxCiNTQkFUQ0ggLU4gMQojU0JBVENIIC1KIEVTTV9wcm9jZXNzaW5nCiNTQkFUQ0ggLUEgSUhFU0QgCgojIyBUaGlzIGNvbmZpZ3VyYXRpb24gZ2l2ZXMgdXM6CiMjIGEgdGltZSBsaW1pdCBvZiAxNSBob3VycwojIyAxIHRhc2sKIyMgMSBjcHUgcGVyIHRhc2sKIyMgb24gYSBzaW5nbGUgbm9kZQojIyBuYW1lcyB0aGUgam9iICdHQ0FNJwojIyBvbiBhY2NvdW50IAoKI0ZpcnN0IG1ha2Ugc3VyZSB0aGUgbW9kdWxlIGNvbW1hbmRzIGFyZSBhdmFpbGFibGUuCnNvdXJjZSAvZXRjL3Byb2ZpbGUuZC9tb2R1bGVzLnNoIAoKI1NldCB1cCB5b3VyIGVudmlyb25tZW50IHlvdSB3aXNoIHRvIHJ1biBpbiB3aXRoIG1vZHVsZSBjb21tYW5kcy4KbW9kdWxlIGxvYWQgUi8zLjQuMwoKI05leHQgdW5saW1pdCBzeXN0ZW0gcmVzb3VyY2VzLCBhbmQgc2V0IGFueSBvdGhlciBlbnZpcm9ubWVudCB2YXJpYWJsZXMgeW91IG5lZWQuCnVubGltaXQKClJzY3JpcHQgLi9CMi5nZXQtYW5udWFsLW9jZWFuLWZsdXguUgpgYGAKClRvIHJ1biBzYmF0Y2ggc2NyaXB0cyB5b3UgYXJlIGdvaW5nIHRvIG5lZWQgaG91cnMgb24gYSBwaWMgYWNjb3VudC4gVGFsayB0byBwaWMgc3VwcG9ydCBhbmQgd2hvIHlvdSBhcmUgd29ya2luZyBvbiB0aGUgcHJvamVjdCB3aXRoIHRvIGZpZ3VyZSBvdXQgaG91cnMgYW5kIHN1Y2guIFlvdSBjYW4gY2hlY2sgeW91ciBhY2NvdW50IGFsbG9jYXRpb24gd2l0aC4gCgpgYGAKW2RvcmgwMTJAY29uc3RhbmNlMDMgRVNNX3Byb2Nlc3NpbmdfY29kZV0kIG1vZHVsZSBsb2FkIHNiYW5rCltkb3JoMDEyQGNvbnN0YW5jZTAzIEVTTV9wcm9jZXNzaW5nX2NvZGVdJCBzYmFuayBiYWxhbmNlIHN0YXRlbWVudApgYGAKCllvdSBjYW4gYWxzbyBydW4gYSBmZXcgY29kZSBvbiB0aGUgbG9nIGluIG5vZGUuIEJ1dCBkbyBub3QgYWJ1c2UgdGhpcyBiZWNhdXNlIHlvdSBydW4gdGhlIHJpc2sgb2YgZ2V0dGluZyBpbiB0cm91YmxlIHdpdGggcGljIGlmIHlvdSBhcmUgdHJ5aW5nIHRvIGRvIHRvIG11Y2ggb24gdGhlIGxvZyBpbiBub2RlLgoKVGhpbmdzIHRoYXQgYXJlIG9rYXkgdG8gZG8gb24gdGhlIGxvZyBpbiBub2RlIAoKKiBNb3ZlIGZpbGVzIC8gbWFrZSBkaXJlY3RvcmllcyAKKiBUZXN0IG91dCBSIHNjcmlwdHMgKHdoZW4gSSBkbyB0aGlzIEkgcHJvY2VzcyBkdW1teSBkYXRhIG9yIGEgc2luZ2xlIGZpbGUpCiogTG9vayBzb21lIG5ldGNkZiBmaWxlcyAKCgpUaGlzIHRoYXQgYXJlIG5vdCBva2F5IHRvIGRvIG9uIHRoZSBsb2cgaW4gbm9kZQoKKiBQcm9jZXNzIGxvdHMgb2YgZmlsZXMgCiogUnVuIEdDQU0gb3IgSGVjdG9yIGEgYnVuY2ggb2YgdGltZXMgCiogT3BlbiByZWFsbHkgbGFyZ2UgZGF0YSBmaWxlcyAoSSB0cmllZCB0byB1bnppcCBhbmQgb3BlbiBhIGxhcmdlIGZpbGUgZnJvbSBhIGNvbGxhYm9yYXRvciBvbiB0aGUgcGljIGxvZyBpbiBub2RlIGFuZCBpdCBmbG9vZGVkIHRoZSBub2RlIGFuZCBzdGFydGVkIHRvIHVzZSA5MCUgb2YgcGljJ3MgY29tcHV0aW5nIHBvd2VyLCBuZWVkbGVzcyB0byBzYXkgdGhlIHBpYyB0ZWFtIHdhcyBub3QgaGFwcHkgd2l0aCBtZSkuCgoKIyMjIyBIb3cgdG8gbG9hZCBSIG9uIHBpYy4gCgpXaGVuIGRldmVsb3BpbmcgUiBjb2RlIHRvIHVzZSBvbiBwaWMgSSB1c3VhbGx5IHdvcmsgaW4gUiBzdHVkaW8gb24gbXkgbG9jYWwgbWFjaGluZSwgdGhlbiBjb3B5IGFuZCBwYXN0ZSBpdCBpbnRvIHRoZSBpbnRlcmFjdGl2ZSBSIG1vZHVsZSBvbiBwaWMuCgpUbyBzZWUgd2hhdCBpcyBhdmlhYmxlIG9uIHBpYyB1c2UgYG1vZHVsZSBhdmFpbGAKCmBgYAptb2R1bGUgYXZhaWwgUgpgYGAKCkxhdW5jaCBSIHZlcnNpb24gMy40LjMgb24gcGljLiAKCmBgYAptb2R1bGUgbG9hZCBSLzMuNC4zIApSCmBgYCAKCgojIyMgQ0RPIAoKQ0RPIGlzIGEgY29sbGVjdGlvbiBvZiBjb21tYW5kIGxpbmUgZGF0YSBvcGVyYXRvcnMgZm9yIHByb2Nlc3NpbmcgbmV0Y2RmcyAoc2VlIHdlYnNpdGUgZm9yIGluZm9ybWF0aW9uKVtodHRwczovL2NvZGUubXBpbWV0Lm1wZy5kZS9wcm9qZWN0cy9jZG8vZW1iZWRkZWQvaW5kZXguaHRtbF0uIENETyBpcyBncmVhdCB0byBwcm9jZXNzIHRoZSBjbWlwIG91dHB1dCBkYXRhIHdoZXJlIHRoZXJlIGlzIGluY29uc2lzdGVudCBkYXRhIHN0cnVjdHVyZXMgKGRpZmZlcmVudCByZWxhdGl2ZSBzdGFydCBkYXRlcywgZGlmZmVyZW50IHNwYXRpYWwgcHJvamVjdGlvbnMgZXRjKSBiZWNhdXNlIGl0IHdpbGwgcGVyZm9ybSBvcGVyYXRpb25zIGJhc2VkIG9uIGluZm9ybWF0aW9uIGluY2x1ZGVkIGluIHRoZSBtZXRhIGRhdGEgc3RydWN0dXJlLiAKClRoZSBDRE8gb24gcGljIGxpdmVzIGF0IApgYGAKL3NoYXJlL2FwcHMvbmV0Y2RmLzQuMy4yL2djYy80LjQuNy9iaW4vY2RvCmBgYAoKSG93IHlvdSB3b3VsZCBydW4gY2RvIGluIGNvbW1hbmQgbGluZSBvbiBwaWMuICh5b3UgY2FuIGNoYWluIG9wZXJhdG9ycyB0b2dldGhlcikKCmBgYApjZG8gb3BlcmF0b3IgaW5maWxlIG91dGZpbGUKYGBgCgpGb3IgZXhhbXBsZSB0aGUgZ2xvYmFsIG1lYW4gb2YgYSBzaW5nbGUgZmlsZSB1c2luZyBjZG8uIFRoZSByZXN1bHRzIHdpbGwgYmUgc3RvcmVkIGluIHRlc3QubmMuIAoKYGBgCltkb3JoMDEyQGNvbnN0YW5jZTAzIH5dJCAvc2hhcmUvYXBwcy9uZXRjZGYvNC4zLjIvZ2NjLzQuNC43L2Jpbi9jZG8gZmxkbWVhbiAvcGljL3Byb2plY3RzL0dDQU0vQ01JUDUtQ0hhcnRpbi9DTUlQNV9SQ1A0NS90YXMvdGFzX0Ftb25fQ0VTTTEtQkdDX3JjcDQ1X3IxaTFwMV8yMDA2MDEtMjEwMDEyLm5jIC4vdGVzdC5uYwpXYXJuaW5nIChjZGZTY2FuVmFyQXR0cmlidXRlcykgOiBOZXRDREY6IFZhcmlhYmxlIG5vdCBmb3VuZCAtIGFyZWFjZWxsYQpjZG8gZmxkbWVhbjogUHJvY2Vzc2VkIDYzMDM3NDQwIHZhbHVlcyBmcm9tIDEgdmFyaWFibGUgb3ZlciAxMTQwIHRpbWVzdGVwcyAoIDEuMDJzICkKYGBgCgpTbyB3aXRoIG9uZSBsaW5lIG9mIGNvZGUgSSB3YXMgYWJsZSB0byBjYWxjdWxhdGUgdGhlIHdlaWdodGVkIGdsb2JhbCBtZWFuIHRlbXBlcmF0dXJlIGZvciBhIG5ldGNkZi4gSG93ZXZlciBpZiB5b3Ugd2FudCB0byBwcm9jZXNzIG1vcmUgdGhhbiAxMCBmaWxlcyB0aGUgY29tbWFuZCBsaW5lIG9wdGlvbiBnZXRzIGtpbmQgb2YgY2x1bmt5IGFuZCB5b3Ugd2lsbCBub3QgYmUgYWJsZSB0byBnZW5lcmF0ZSBuZXcgZmlsZSBuYW1lcyB1c2luZyBjZG8gc28gcGFyaW5nIGNkbyBhbmQgUiB0b2dldGhlciBpcyB2ZXJ5IHBvd2VyZnVsLiAKCgojIyMjIENETyArIFIgCgpFeGFtcGxlcyBvZiBjZG8gKyBSIHByb2Nlc3NpbmcgY29kZSBjYW4gYmUgb24gR2l0SHViIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9rZG9yaGVpbS9DRE9leGFtcGxlcy4gQnV0IHlvdSBhcmUgZ29pbmcgdG8gd2FudCB0byB1c2UgdGhlIGZ1bmN0aW9uIHN5c3RlbTIgdG8gZXhlY3V0ZSB0aGUgY2RvIGNvZGUsIGl0IHdpbGwgbG9vayBsaWtlIHRoZSBmb2xsb3dpbmcgZm9ybWF0LiAKCmBgYApzeXN0ZW0yKCdwYXRoL3RvL2Nkby9leGUnLCBhcmdzID0gYygnb3BlcmF0b3InLCAncGF0aC90by9pbmlmaWxlLm5jJywgJy4vdGVzdC5uYycpLCBzdGRvdXQgPSBUUlVFLCBzdGRlcnIgPSBUUlVFKQpgYGAKCkhlcmUgaXMgd2hhdCB5b3Ugd291bGQgcnVuIGluIGludGVyYWN0aXZlIFIgdG8gcmVwZWF0IG91ciBlYXJsaWVyIGV4YW1wbGUgaW4gUi4KCmBgYHtyLCBldmFsPUZBTFNFfQpDRE9fRVhFICAgPC0gICAnL3NoYXJlL2FwcHMvbmV0Y2RmLzQuMy4yL2djYy80LjQuNy9iaW4vY2RvJwppbnB1dF9uYyAgPC0gIi9waWMvcHJvamVjdHMvR0NBTS9DTUlQNS1DSGFydGluL0NNSVA1X1JDUDQ1L3Rhcy90YXNfQW1vbl9DRVNNMS1CR0NfcmNwNDVfcjFpMXAxXzIwMDYwMS0yMTAwMTIubmMiCm91dHB1dF9uYyA8LSAiLi90ZXN0Lm5jIiAKc3lzdGVtMihDRE9fRVhFLCBhcmdzID0gYygiZmxkbWVhbiIsIGlucHV0X25jLCBvdXRwdXRfbmMpLCBzdGRvdXQgPSBUUlVFLCBzdGRlcnIgPSBUUlVFKQpgYGAKCgpUaGVuIHRvIGV4dHJhY3QgdGhlIHJlc3VsdHMgZnJvbSB0aGUgbmV3IG5ldGNkZiB5b3UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgcGFja2FnZSBgbmNkZjRgLiBJIHJlY29tbWVuZCBsb29raW5nIGludG8gdGhhdCBwYWNrYWdlIGRvY3VtZW50YXRpb24gYmVjYXVzZSBpdCBpZiBhIHByZXR0eSB2YWx1YWJsZSB0b29sLgoKSG93IHRvIGV4dHJhY3QgdGhlIHJlc3VsdHMgZnJvbSBhIG5ldGNkZiBpbiBSLgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgRm9yIHRoZSBpbnB1dCBuZXRjZGYgZmlsZS4KbmNfaW4gPC0gbmNfb3BlbihpbnB1dF9uYykKdGFzICAgPC0gbmN2YXJfZ2V0KG5jX2luLCAndGFzJykKdGltZSAgPC0gbmN2YXJfZ2V0KG5jLCAndGltZScpCmBgYAoKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgRm9yIHRoZSBvdXRwdXQgbmV0Y2RmLiAKbmMgICA8LSBuY19vcGVuKG91dHB1dF9uYykKdGFzICA8LSBuY3Zhcl9nZXQobmMsICd0YXMnKQp0aW1lIDwtIG5jdmFyX2dldChuYywgJ3RpbWUnKQpgYGAKCldoYXQgaXMgdGhlIGRpZmZlcmVuY2UgaW4gdGhlc2UgdHdvIG5ldGNkZiBmaWxlcyAoZGltZW5zaW9uKT8gCgo8YnI+CgpIb3cgSSBsaWtlIHRvIHN0cnVjdHVyZSBteSBDRE8gKyBSIGNvZGUKCkkgdHlwaWNhbGx5IHVzZSB0d28gc2NyaXB0cyAoYW5kIEkgd2FudCB0byBzdHJlc3MgdGhhdCB0aGlzIGlzIGp1c3QgbXkgcHJlZmVyZW5jZSBvbiBob3cgdG8gc2V0IGl0IHVwIHRoZXJlIGlzIGdvaW5nIHRvIGJlIGxvdHMgb2YgZGlmZmVyZW50IHdheXMgdG8gZG9pbmcgaXQpLiAKCk15IGZpcnN0IHNjcmlwdCBsb2NhdGVzIGFuZCBzb3J0cyB0aGUgbmV0Y2RmcyBJIHdhbnQgdG8gcHJvY2VzcywgdGhpcyBpcyBiZWNhdXNlIG9mIGhvdyBDTUlQNSBkYXRhIHdhcyBzdG9yZWQgb24gcGljLiBJdCBzdG9yZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNtaXAgZmlsZXMgdG8gcHJvY2VzcyBpbiBhIGNzdiBmaWxlLiAKCkluIG15IHNlY29uZCBzY3JpcHQgSSBpbXBvcnQgbXkgQ3N2IGZpbGUgY29udGFpbmluZyB0aGUgbmV0Y2RmIGZpbGVzIHRvIHByb2Nlc3MsIGRlZmluZSBteSBmdW5jdGlvbiB0aGF0IGNvbnRhaW5zIHRoZSBgc3lzdGVtMmAgY2RvIGNhbGwsIGFuZCB0aGVuIHVzZSBhbiBgYXBwbHlgIGZhbWlseSBjb21tYW5kIHRvIGV4ZWN1dGUgbXkgcHJvY2Vzc2luZyBmdW5jdGlvbiBvbiB0aGUgbGlzdCBvZiB0aGUgbmV0Y2RmcyB0byBwcm9jZXNzLiBJIHNhdmUgdGhlIGZpbmFsIHJlc3VsdHMgaW4gYSBjc3YgZmlsZSB0aGF0IGlzIGVhc3kgdG8gZG93bmxvYWQgb250byBteSBsb2NhbCBtYWNoaW5lLiAKCioqQ0RPICYgUiB0aXBzKioKCiogV3JpdGUgdGVzdHMgaW50byB5b3VyIGZ1bmN0aW9uIGFuZCB0cnkgdG8gZ2l2ZSBpbmZvcm1hdGl2ZSBlcnJvciBtZXNzYWdlcywgb3RoZXJ3aXNlIHlvdSBjYW4gc3BlbmQgYSBsb3Qgb2YgdGltZSB0cnlpbmcgdG8gZGVidWcgeW91ciBjb2RlLiAKKiBDRE8gZ2VuZXJhdGVzIGxvdHMgb2YgaW50ZXJtZWRpYXRlIG5ldGNkZiBmaWxlcywgYXQgc29tZSBwb2ludCB5b3Ugc2hvdWxkIGNsZWFuIHRoZXNlIHVwIGJ1dCBkdXJpbmcgZGV2ZWxvcG1lbnQgaXQgY2FuIGJlIHVzZWZ1bCB0byBrZWVwIHRoZSBpbnRlcm1lZGlhdGUgbmV0Y2RmIGZpbGVzIGFuZCBjaGVjayB0aGVtLiAKCgojIyBQcmFjdGljZSBFeGVyY2lzZSAKCldoYXQgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZ2xvYmFsIGF2ZXJhZ2UgdGVtcGVyYXR1cmUgZm9yIGEgc2luZ2xlIHllYXIgY2FsY3VsYXRlZCBieSBjZG8gYGZsZGdlbmAgdnMgYG1lYW5gIGluIFI/IElzIHRoaXMgYSBzdXJwcmlzZSwgd2h5IG9yIHdoeSBub3Q/IAoKSGludCAtIGlmIHlvdSB1c2VkIG5jdmFyX2dldCgpIHRvIGltcG9ydCB0aGUgZ2lyZGVkIG1vbnRobHkgZGF0YSBpbnRvIFIgdGhlIGRhdGEgaXMgZ29pbmcgdG8gYmUgYW4gYXJyYXkgd2l0aCB0aHJlZSBkaW1lbnNpb25zLCBgW2xvbiwgbGF0LCB0aW1lXWAuIAoKCg==