// $Header$ -*-C-*-

// Purpose: ncap2 netCDF Arithmetic Processor Demonstration/Test Script
// Adopted from an ncap (=ncap1) Demonstration/Test Script deprecated in 201803

/* Format of valid ncap2 script:
   Syntax is C-like, and C++ comments also valid
   Mathematical expressions use forward algebraic notation
   Statements in scripts are terminated with semi-colons
   Command-line definitions should omit semi-colons, e.g., -s "foo=bar"
   Whitespace (blank lines, tabs) is ignored
   Nested files allowed with #include file syntax */
//#include ncap.in2

/* Usage:
   ncap2 -O -D 1 -v -S ${HOME}/nco/data/ncap.in ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -O -D 1 -d lat,0 -d lon,0 -d lev,0 -s "a9=three_dmn_var" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -O -D 1 -v -s "upk=pck*pck@scale_factor+pck@add_offset" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -O -D 1 -v -s "upk=pck" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -O -D 1 -v -s "pck=pack(three_dmn_var)" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -O -D 1 -s "prs_mdp[time,lat,lon,lev]=P0*hyam+hybm*PS" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncks -H -m ${HOME}/nco/data/foo.nc | m */

/* ncap2 is a handy attribute editor:
   Define new attribute:
   ncap2 -O -v -s "one@new=2*att_var@double_att;one@test=two@long_name;" in.nc foo.nc

   Re-define existing attribute in terms of itself:
   ncap2 -O -v -s "att_var@double_att=att_var@double_att^2.0" in.nc foo.nc

   Computation is performed at highest precision of RHS expression:
   ncap2 -O -v -s "one@new=att_var@short_att * att_var@double_att - att_var@double_att" in.nc foo.nc 

   Use math functions (default result is of type double):
   ncap2 -O -v -s "one@new=cos(1.0e-4) - 1" in.nc foo.nc
   Result is now of type float:
   ncap2 -O -v -s "one@new=cos(1.0e-4f)^2+sin(1.0e-4f)^2" in.nc foo.nc

   Assign string to attribute (use \" to escape quote from shell):
   ncap2 -O -v -s "one@new=\"hello world\"" in.nc foo.nc

   Add strings, and use C escape characters:
   ncap2 -O -v -s "one@new=\"Hello\t\"+\"World\n\"" in.nc foo.nc

   Set an attribute equal to a 0-dimensional variable:
   ncap2 -O -v -s "one@new=one" in.c foo.nc

   Set an attribute equal to a 1-dimensional variable:
   ncap2 -O -v -s "one@new=mss_val" in.nc foo.nc 

/* The ncap2 operators works with netCDF variables and attributes
   Multiply an existing co-ordinate variable by 20:
   ncap2 -O -v -s "lat=20*lat" in.nc foo.nc

   Average variables of mixed types (result is of type double):
   ncap2 -O -v -s "average=(three_dmn_rec_var+three_dmn_var_dbl+three_dmn_var_int)/3" in.nc foo.nc

   Take log (to base e) of absolute value of variable:
   ncap2 -O -v -s "abslog=log(abs(three_dmn_var_dbl))" in.nc foo.nc

   The available maths functions are: 
   acos(), asin(), atan(), cos(), exp(), erf(), erfc(), gamma(), log(), log10(), sin(), sqrt(), tan();
   If argument precision is "less" than type float then result is type float
   If argument is type double then result is also double
   This also applies to pow() function, e.g, pow(var1,3.5) or var1^3.5, e.g.,
   ncap2 -O -v -s "modulus=pow(sin(three_dmn_rec_var),2)+cos(three_dmn_rec_var)^2 - 1" in.nc foo.nc */

/* Modulus operator % can also be used with attributes and variables
   Attributes are converted to variable's type prior to operation
   Result of modulus is of type float:
   ncap2 -O -v -s "mod=three_dmn_rec_var % 4.0" in.nc foo.nc

   Result of modulus is of type int:
   ncap2 -O -v -s "testa=three_dmn_var_int % 1.0f" in.nc foo.nc

   Unary +/- signs work intuitively with attributes and variables:
   ncap2 -O -v -s "sign=-three_dmn_rec_var" in.nc foo.nc */

/* Packing and unpacking:
   Manual unpacking is relatively straightforward but works only with -D 3
   because automatic unpacking is now default on ncap2. It is now broken
   because ncap_var_write() tries to write bogus attributes automatically
   because pck_ram is set in pck_dsk_inq()
   ncap2 -D 3 -O -v -s "upk=pck*pck@scale_factor+pck@add_offset" -s "upk_arr=pck_arr*pck_arr@scale_factor+pck_arr@add_offset" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   Automatic unpacking works for scalars, not for arrays
   ncap2 -D 1 -O -v -s "upk=pck" -s "upk_arr=pck_arr" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   Automatic packing does not yet work
   ncap2 -D 1 -O -v -s "pck=pack(ORO)" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -D 1 -O -v -s "pck=pack(upk)" -s "pck_arr=pack(upk_arr)" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -D 1 -O -v -s "pck=pack(pck)" -s "pck_arr=pack(pck_arr)" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncap2 -D 1 -O -v -s "upk=unpack(pack(unpack(pack(pck))))" -s "upk_arr=unpack(pack(unpack(pack(pck_arr))))" ${HOME}/nco/data/in.nc ${HOME}/nco/data/foo.nc
   ncks -H -m ${HOME}/nco/data/foo.nc | m
   ncks -H -v pck,pck_arr -m ${HOME}/nco/data/foo.nc | m
   ncks -H -v upk,upk_arr -m ${HOME}/nco/data/foo.nc | m
 */

// Charlie's Tests:
// p=hyam*PO+hybm*PS
a2=(1*(three_dmn_var-three_dmn_var+1)-1)^1;
a3[lat,lon,lev]=one;
a4[lat,lon,lev]=1.0f-one;
a5=time;
a6=time+one_dmn_rec_var;
prs_mdp[time,lat,lon,lev]=P0*hyam+hybm*PS; // Works because prs_mdp in LHS cast
//prs_mdp=P0*hyam+hybm*PS; // Fails because prs_mdp dimension ordering is ambiguous
//prs_mdp=(four_dmn_rec_var-four_dmn_rec_var+1)*P0*hyam+(four_dmn_rec_var-four_dmn_rec_var+1)*hybm*PS; // Works because prs_mdp is typecast on RHS
a7=erf(1.0);
a8=erfc(1.0);
a9[time,lat,lev]=3.141d;
// Henry's tests:
one@one=10+30;
one@two=sin(3.141/2);
one@three=cos(3.1415926)+one@one+one@two;
// Redefine attributes 
one@one=23/4;
one@two=one@one+one@two;
one@eight=25.0%4.99;
one@nine=1.e10;
one@ten=val_half_half@_FillValue%1000;
/* Standard netCDF postfix operators are used to typecast attributes/numbers
   floats and doubles must include decimal point or exponent to be recognized */
one@byte=10b;
one@short=10s;
one@float=100.e2f;
one@double=2e3;
/* Type conversion follows C rules: expression is converted to highest type
   Following expression is of type float: */
one@add=one@byte+one@short/one@float;
// Can create 0 dimensional variables (scalars) 
nine=10000e2f;
one=10;
two=4;
val_half_half@_FillValue=21;
// Can use modulus operator with attributes and variables 
twenty=four_dmn_rec_var % 8; 
twentyone=sin(twenty)^2+cos(twenty)^2;
twentytwo=10*9;
twentythree=1.0e9%2;
twentyfour=two_dmn_var@units;
twentyfive=three_dmn_var_dbl/4;
twentysix=pck;
/* Below multiplication is of individual elements in variables AND NOT
   a matrix multiplication. Resulting matrix is of type double. */
twentyseven=three_dmn_var_int * three_dmn_var_dbl;
// Function atostr() evaluates an attribute and stores the result as a string 
twenty@one=atostr(1.e10);
twenty@two=atostr(1/7.0);
// Function atostr() accepts an optional C-format
twenty@three=atostr(1/7.0,"\t%15f\n");
// Add two strings together
twenty@four="Hello"+"\t World\n";
// Put a 0 or 1 dimensional variable into an attribute
twenty@five=fl_nm;
twenty@six=mss_val;
twenty@seven=one;
//Put an attribute/number  into a variable
thirty=1.e19f;
thirtyone=one@one;
// With a string ONLY the first char is put into variable
thirtytwo="Hello world";
// use varibles defined in output
thirtythree=twentyfive*4.001;
// Use UNARY -/+ and brackets
three=(-two_dmn_var+5)^3;
testa=pow(sin(four_dmn_rec_var),2)+cos(four_dmn_rec_var)^2;
/* Conversion functions are available for variables AND attributes
   Standard C type conversion rules are used
   Converting "down" produces interesting results!
   byte(),char(),short(), int(), float(), and double() */
// Convert float to byte
ex_byte=byte(two_dmn_var);
// Convert int to short
ex_short=short(three_dmn_var_int/5);
// Convert short to float
ex_float=float(three_dmn_var_sht);
// Convert float to double
ex_double=double(weight);
// Convert attribute to double
bnd_var@att_double=double(bnd_var@float_att); 
// Convert double attribute to int
bnd_var@att_int=double(4*bnd_var@float_att); 
