How do I...? {#howto}
===

[TOC]

@section editing_config Creating and using configuration flags and parameters

- Edit `src/pism_config.cdl`. Each flag or parameter is stored as a NetCDF attribute and should have a corresponding "_doc" attribute describing its meaning.

~~~
pism_config:standard_gravity = 9.81;
pism_config:standard_gravity_doc = "m s-2; acceleration due to gravity on Earth geoid";
~~~

- One can access these parameters using the PISMConfig class. IceModel
  has an instance of this class as a data member 'config', so no
  additional code is necessary to initialize the configuration
  database.

To use a parameter, do

~~~
double g = config.get_double("standard_gravity");
~~~

To use a flag, do

~~~
bool compute_age = config.get_boolean("do_age");
~~~

@note
- It is a good idea to avoid calling `config.get_double()` and
  `config.get_boolean()` from within loops: looking up a parameter by its
  name is slow.
- Please see [this page](@ref config) for a list of flags and
  parameters currently used in PISM.

@section variables Creating and using additional variables

@subsection icemodelvecs Creating IceModelVec instances

PISM uses the following classes to manage 2D and 3D fields, their I/O and
metadata:

- IceModelVec2S -- scalar 2D fields
- IceModelVec2V -- vector 2D fields (such as horizontal velocities;
  corresponds to 2 NetCDF variables)
- IceModelVec2Int -- 2D masks, such as the grounded/floating mask
- IceModelVec2T -- 2D time-dependent fields (used to read and store forcing data)
- IceModelVec3 -- scalar 3D fields (within the ice)

Please see the documentation of these classes for more info.  The base class
IceModelVec is a virtual class, so code should use the above derived classes.
Only the derived classes have create() methods, in particular.

To create a scalar field, for example, one needs to create an instance of one
of the classes listed above and then call "create" to allocate it.

~~~
// land ice thickness
ice_thickness.create(grid, "thk", WITH_GHOSTS, 2);
ice_thickness.set_attrs("model_state", "land ice thickness",
                        "m", "land_ice_thickness");
ice_thickness.metadata().set_double("valid_min", 0.0);
~~~

Here `grid` is an IceGrid instance, "thk" is the name of the NetCDF
variable, WITH_GHOSTS means that storage for "ghost" ("halo") points
will be allocated, and "2" is the number of ghosts (in other words:
needed stencil width).

The IceModelVec destructor takes care of undoing all that's done by the
create() call.  Therefore you don't need to explicitly de-allocate variables
unless you dynamically created the IceModelVec (or derived) instance using the
C++ "`new`" operator.  (In which case "`delete`" should be used.)

The IceModelVec::set_attrs() call sets commonly used NetCDF variable
attributes seen in PISM output files:
- **pism_intent**  -- the only important case is "model_state", see below
- **long_name** -- the (descriptive) long name used for plotting, etc
  (a free-form string)
- **units** -- units used *in the code*. Does not have to match units
  in a file
- **standard_name** -- CF standard name, if defined, or an empty string.

The third call above `ice_thickness.metadata()` allows accessing
variable metadata and adding arbitrary named attributes. See
VariableMetadata for details.

The CF convention covers some attribute semantics, including
"valid_min" in this example.

PISM will automatically convert units from ones present in an input
file into internal units defines by the set_attrs() call (see above).

If you want PISM to save data in units other than internal ones, first
set these "glaciological" units:
~~~
ice_thickness.metadata().set_string("glaciological_units", "km");
~~~
Then set IceModelVec::write_in_glaciological_units to `true`:
~~~
ice_thickness.write_in_glaciological_units = true;
~~~

@subsection reading_data Reading data from a file

There are at least three cases of "reading data from a file":
- reading a field stored in an input file on a grid matching the one
  used by the current run (restarting a run) and
- reading a field stored in an input file on a different grid,
  interpolating onto the current grid (bootstrapping).
- reading a field stored in a file **other** than the input file using
  interpolation (assuming that grids are compatible but not identical)

This snippet from PAYearlyCycle::init() covers the first two cases (at least for
surface, atmosphere and ocean models; %i.e. for all the classes derived from PISMComponent)

~~~
bool do_regrid = false;
int start = -1;

find_pism_input(precip_filename, do_regrid, start);

// read precipitation rate from file
verbPrintf(2, grid.com,
           "    reading mean annual ice-equivalent precipitation rate 'precipitation'\n"
           "      from %s ... \n",
           precip_filename.c_str());
if (do_regrid) {
  precipitation.regrid(precip_filename, CRITICAL); // fails if not found!
} else {
  precipitation.read(precip_filename, start); // fails if not found!
}
~~~

Here
- "start" is an index of a record *within a file* that we need to
  read. PISM almost always reads the last record.

Please see PISMComponent::find_pism_input() to see how to compute the "start"
index "by hand".

The snippet below is an example of case 3 (for 2D fields, reading the last record).

~~~
variable.regrid(filename, CRITICAL); // fails if not found!
~~~

@subsection writing_data Writing data to a file

To write a field stored in an IceModelVec to an already "prepared" file, just call

~~~
precip.write(filename);
~~~

The file referred to by "filename" here has to have the time "time"
dimension created, that is, it must be prepared (other dimensions are
created automatically, if it is necessary). No action is needed to be
able to write to an output ("-o") file, a snapshot file or the like;
IceModel has already prepared it.

If you do need to "prepare" a file, do:

~~~
PIO nc(grid.com, grid.config.get_string("output_format"));

std::string time_name = config.get_string("time_dimension_name");
nc.open(filename, PISM_WRITE); // append == false
nc.def_time(time_name, grid.time->calendar(),
            grid.time->CF_units_string());
nc.append_time(time_name, grid.time->current());
nc.close();
~~~

When a file is opened with the `PISM_WRITE` mode, PISM checks if this
file is present and moves it aside if it is; to append to an existing file, use
~~~
nc.open(filename, PISM_WRITE, true); // append == true
~~~
A newly-created file is "empty" and contains no records. The nc.append_time()
call creates a record corresponding to a particular model year.

@subsection icemodelvec_in_icemodel Creating IceModelVec instances that are data members of IceModel or a derived class

To add a new variable to IceModel, allocate it in the createVecs() method.

If `pism_intent` is set to `model_state` and a variable is added to the
"variables" dictionary (see PISMVars), IceModel will automatically read this
variable from a file it is re-starting from and will always write it to an
output, snapshot and backup files.

~~~
variables.add(ice_thickness);
~~~

@subsection reading_scalar_forcing Reading scalar forcing data

PISM uses instances of the Timeseries class to read scalar forcing data; please
see PA_delta_T for an example.

The following snippet illustrates creating a Timeseries object and
reading data from a file.

~~~
std::string offset_name	= "delta_T";

offset = new Timeseries(&grid, offset_name, config.get_string("time_dimension_name"));
offset->set_string("units", "Kelvin");
offset->set_dimension_units(grid.time->units_string(), "");

verbPrintf(2, g.com,
           "  reading %s data from forcing file %s...\n",
           offset->short_name.c_str(), filename.c_str());

PIO nc(g.com, "netcdf3", grid.get_unit_system());
nc.open(filename, PISM_NOWRITE);
{
  offset->read(nc, grid.time);
}
nc.close();

// use offset

delete offset; // when done
~~~

To use `offset`, call
~~~
double data = (*offset)(time);
~~~
to get the value corresponding to the time `time`, in this case in seconds. The
value returned will be computed using linear interpolation.

@subsection reading_2d_forcing Reading 2D forcing fields

PISM uses instances of the IceModelVec2T class to read 2D forcing fields that
vary in time; please see PSDirectForcing for an example.

The following snippet from PSDirectForcing::init() illustrates creating an
IceModelVec2T object and reading data from a file.

~~~
IceModelVec2T temperature;
temperature.set_n_records((unsigned int) config.get_double("climate_forcing_buffer_size"));
temperature.create(grid, "artm", WITHOUT_GHOSTS);
temperature.set_attrs("climate_forcing",
                      "temperature of the ice at the ice surface but below firn processes",
                      "Kelvin", "");
temperature.init(filename);
~~~

@section using_vars Using fields managed by IceModel in a surface model to implement a parameterization

Please see PA_SeaRISE_Greenland::init() and PA_SeaRISE_Greenland::update() for an example.

@section writing_components Managing I/O in a PISMComponent derived class

A PISM component needs to implement the following I/O methods:

- init(). It is not an I/O method per se, but most PISM components
  read their input fields there; see PA_SeaRISE_Greenland::init().
- add_vars_to_output(), which adds variable names to the list of
  fields that need to be written. See
  PSTemperatureIndex::add_vars_to_output_impl() for an example.
- define_variables(), which defines variables. (See
  PSTemperatureIndex::define_variables().)
- write_variables(), which writes data; see
  PSTemperatureIndex::write_variables().

Why are all these methods needed? In PISM we separate defining and
writing NetCDF variables because defining all the NetCDF variables
before writing data is a lot faster than defining a variable, writing
it, defining the second variable, etc. (See
[The NetCDF Users' Guide](http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Parts-of-a-NetCDF-Classic-File.html#Parts-of-a-NetCDF-Classic-File) for a technical explanation.)

Within IceModel the following steps are done to write 2D and 3D fields to an
output file:

- Assemble the list of variables to be written (see
  IceModel::set_output_size()); calls add_vars_to_output()
- Create a NetCDF file
- Define all the variables in the file (see IceModel::write_variables());
  calls define_variables()
- Write all the variables to the file (same method); calls write_variables().

@section diagnostics Adding a new "diagnostic" quantity to an atmosphere model

To add a new "diagnostic" quantity (i.e. a 2D or 3D field that needs
to be saved to an output file but is not permanently stored in memory
and is computed on demand), do the following.

#### Create the class implementing the diagnostic

Assuming that PA_foo is the atmosphere model we're working with and "bar" is
the name of the new quantity, we need to add this code

~~~
class PA_foo_bar : public PISMDiag<PA_foo>
{
public:
  PA_foo_bar(PA_foo *m, IceGrid &g, PISMVars &my_vars);
  virtual IceModelVec::Ptr compute();
};
~~~
to a header (`.hh`) file and implement it in a `.cc` file.

See `IceModel_diagnostics.cc` for examples. Generally speaking, in every class
implementing a "diagnostic" quantity
- the constructor sets metadata
- you have access to a data member "var" of an atmosphere model as
  "model->var"; you might need to add

~~~
friend class PA_foo_bar;
~~~

to the definition of PA_foo if you need to access a private data member (see
the definition of IceModel for one example).
- when working with atmosphere models and other classes derived from
  PISMComponent, you can access the config database as
  "model->config".
- the PISMDiagnostic::compute() method allocates memory and performs
  the computation.
- to use a field managed by IceModel, use "variables":

~~~
const IceModelVec2S *surface = variables.get_2d_scalar("surface_altitude");
~~~

- the **caller** of the PISMDiagnostic::compute() method has to
  de-allocate the field allocated by PISMDiagnostic::compute()

Note that in almost every (current) implementation of
`PISMDiagnostic::compute()` you see

~~~
IceModelVec::Ptr ...::compute() {
  const PetscScalar fillval = -0.01;

  <...>

  // 1
  IceModelVec2S::Ptr result(new IceModelVec2S);
  result->create(grid, "hardav", WITHOUT_GHOSTS);
  result->metadata(0) = m_vars[0];

  <...>

  // 2
  return result;
}
~~~

The block marked "1" allocates a 2D field and copies metadata stored
in `m_vars[0]`, while the block marked "2" returns a pointer to a 2D
field, which gets cast to a pointer to a "generic" field.

This allows us to have the same interface for both 2D and 3D diagnostics.

#### "Register" the new diagnostic.

To make the new diagnostic field available (i.e. to be able to use the new
`PA_foo_bar` class), implement `PA_foo::get_diagnostics_impl()`.

~~~
void PA_foo::get_diagnostics_impl(map<string, PISMDiagnostic*> &dict) {
  dict["bar"] = new PA_foo_bar(this, grid, *variables);
}
~~~

Note that if you are implementing this method in a "modifier" of a surface
model, you need to remember to call
~~~
input_model->get_diagnostics(dict);
~~~
@note PISM's handling of scalar diagnostic quantities is different and should
be improved.
