Program Listing for File fsm.cpp

Program Listing for File fsm.cpp#

Return to documentation for file (src/modules/fsm.cpp)

//
// Canadian Hydrological Model - The Canadian Hydrological Model (CHM) is a novel
// modular unstructured mesh based approach for hydrological modelling
// Copyright (C) 2018 Christopher Marsh
//
// This file is part of Canadian Hydrological Model.
//
// Canadian Hydrological Model is free software: you can redistribute it and/or
// modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Canadian Hydrological Model is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Canadian Hydrological Model.  If not, see
// <http://www.gnu.org/licenses/>.
//

#include "fsm.hpp"
REGISTER_MODULE_CPP(FSM);

FSM::FSM(config_file cfg)
    : module_base("FSM", parallel::data, cfg)
{
    depends("solar_el");
    depends("ilwr");
    depends("rh");
    depends("t");
    depends("p_snow");
    depends("p_rain");
    depends("U_2m_above_srf");

    depends("iswr_direct");
    depends("iswr_diffuse");

    optional("iswr_subcanopy");
    optional("rh_subcanopy");
    optional("ta_subcanopy");
    optional("ilwr_subcanopy");
    optional("iswr_subcanopy");
    optional("p_subcanopy");
    optional("drift_mass");

    // Optional avalanche variables
    optional("delta_avalanche_snowdepth");
    optional("delta_avalanche_mass");

    //diags
    provides("H"); // Sensible heat flux to the atmosphere (W/m^2)
    provides("E"); // Latent heat flux to the atmosphere (W/m^2)
    provides("ilwr_out"); // Outgoing LW radiation (W/m^2)
    provides("melt_rate"); // Surface melt rate (kg/m^2/s)
    provides("roff"); // Runoff from snow (kg/m^2/s)

    provides("snowdepthavg"); // Snow depth (m)
    provides("snowdepthavg_vert"); // Snow depth, slope corrected
    provides("swe"); // Total snow mass on ground (kg/m^2)
    provides("subl"); // Sublimation rate (kg/m^2/s)



    provides("sum_snowpack_subl");

    provides("snow_albedo");

    provides("Nsnow");

    provides("Tsoil[0]");
    provides("Tsoil[1]");
    provides("Tsoil[2]");
    provides("Tsoil[3]");


    provides("LWout");

    provides("Sliq[0]");
    provides("Sliq[1]");
    provides("Sliq[2]");

    provides("Tsnow[0]");
    provides("Tsnow[1]");
    provides("Tsnow[2]");

}


void FSM::init(mesh& domain)
{
    fsm_layers_config(0.5f, 1.5f, CANOPY_LAYER_COUNT, SNOW_LAYER_COUNT, SOIL_LAYER_COUNT);

    fsm_soilprops_config(7.63f, 2.3e6f, 0.11f, 0.41f, 0.26f, 0.27f);

    fsm_allocate();

    constants_e0_ = fsm_constants_e0();
    constants_eps_ = fsm_constants_eps();
    parameters_rgr0_ = fsm_parameters_rgr0();

    #pragma omp parallel for
    for (size_t i = 0; i < domain->size_local_faces(); i++)
    {
        auto face = domain->face(i);
        auto& d = face->make_module_data<data>(ID);

        d.veg.alb0 = 0.2;
        d.veg.vegh = 0;
        d.veg.VAI = 0;


        d.diag.snd = 0;
        d.diag.snw = 0;
        d.diag.sum_snowpack_subl = 0;

        for (int layer = 0; layer < SNOW_LAYER_COUNT; ++layer)
        {
            d.state.Rgrn[layer] = parameters_rgr0_;
        }

        // the other d.* are init in the stat struct

        // IC soil temp workaround
        // Todo: make this FSM an IC instead of using param

        float st1 = 269.0; //level 1 is the surface temperature of the force restore
        float st2 = 269.0; //lvl 2 represents the deep soil temperature of the FR approach
        if(face->has_parameter("soilT_1"_s))
        {
            st1 = face->parameter("soilT_1"_s);
        }
        if(face->has_parameter("soilT_2"_s))
        {
            st2 = face->parameter("soilT_2"_s);
        }

        float ds = (st2 - st1) / 4.0;

        d.state.Tsoil[0] = st1;

        d.state.Tsoil[1] = st1 + ds;
        d.state.Tsoil[2] = st1 + 2*ds;

        d.state.Tsoil[3] = st2;


    }
}
void FSM::run(mesh_elem& face)
{
    if(is_water(face))
    {
        set_all_nan_on_skip(face);
        return;
    }


    // met data
    float zT = 2; // m
    float zU = 2;
    float Ps = mio::Atmosphere::stdAirPressure(face->get_z()); // Pa

    float dt = (float)global_param->dt();


    float rh = -9999;
    if(has_optional("rh_subcanopy")) {
        rh = (*face)["rh_subcanopy"_s];
    } else {
        rh = (*face)["rh"_s];
    }

    float Rf = 0; // rainfall rate
    float Sf = 0;  // snowfall rate
    if(has_optional("p_subcanopy"))
    {
        Rf = (*face)["p_rain_subcanopy"_s]  / dt; // rainfall rate
        Sf = (*face)["p_snow_subcanopy"_s] / dt; // snowfall rate
    } else {
        Rf = (*face)["p_rain"_s]  / dt; // rainfall rate
        Sf = (*face)["p_snow"_s] / dt; // snowfall rate
    }

    // drop if v. small mass value
    float p_cutoff = 0.1f/dt; //mm/dt
    if( Rf < p_cutoff && Rf > -p_cutoff)
        Rf = 0;
    if( Sf < p_cutoff && Sf > -p_cutoff)
        Sf = 0;

    auto& d = face->get_module_data<data>(ID);

    float elev = (float)(*face)["solar_el"_s] * M_PI / 180.0;
    float ilwr = -9999;
    if(has_optional("ilwr_subcanopy")) {
        ilwr = (float)(*face)["ilwr_subcanopy"_s];
    } else {
        ilwr = (float)(*face)["ilwr"_s];
    }


    float Sdiff = 0.0f;
    float Sdir = 0.0f;
    if(has_optional("iswr_subcanopy"))
    {
        // iswr is direct + diff -> which is fed to canopy so iswr_subcanopy is also direct + diff
        Sdiff = (float)(*face)["iswr_diffuse"_s];
        // need to split this up
        Sdir  = (float)(*face)["iswr_subcanopy"_s] - (float)(*face)["iswr_diffuse"_s];
    }
    else
    {
        Sdiff = (float)(*face)["iswr_diffuse"_s];
        Sdir = (float)(*face)["iswr_direct"_s];
    }

    // clamp to positive
    Sdiff = std::max(0.0f, Sdiff);
    Sdir  = std::max(0.0f, Sdir);


    float t = -9999;
    if(has_optional("ta_subcanopy")) {
        t = (float)(*face)["ta_subcanopy"_s];
    } else {
        t = (float)(*face)["t"_s];
    }
    t += 273.15f;

    float tc = t - 273.15f;
    float Qs = constants_eps_ * (constants_e0_ / Ps) *
               exp((float)17.5043 * tc / ((float)241.3 + tc));
    float Qa = (rh/ (float)100.0) * Qs; // specific humidity

    float U = (float)(*face)["U_2m_above_srf"_s];

    //blowing snow
    float trans = 0.0f;
    if(has_optional("drift_mass"))
    {
        double mass = (*face)["drift_mass"_s]; //kg/m^2  (mm)

        mass = is_nan(mass) ? 0 : mass;

        trans = mass / dt;
    }

    float rhod = 300.0f;
    // If snow avalanche variables are available
    if(has_optional("delta_avalanche_mass"))
    {
        double delta_avalanche_swe = (*face)["delta_avalanche_mass"_s];

        // delta_avalanche_swe is m^3 of swe. So ----> m^3 / m^2 * 1000 kg/m^3 = kg/m^2
        delta_avalanche_swe = delta_avalanche_swe / face->get_area() * 1000.0;
        trans = trans + delta_avalanche_swe/dt;

        if(delta_avalanche_swe >0)
            rhod = 500;

    }

    // FSM has removal as positive and deposition as negative
    trans = -trans;

    d.state.Tsoil[0] = 263;
    d.state.Tsoil[1] = 263.1;
    d.state.Tsoil[2] = 263.2;
    d.state.Tsoil[3] = 263.3;

    // the tiny snowpack -> snowfree cycles can do weird things to this temp, so reinit crazy cases
    if (d.state.Tsrf < 230.0f)
        d.state.Tsrf = 263.0f;

    fsm2_timestep(
        // Driving variables
        &dt, &elev, &zT, &zU,
        &ilwr, &Ps, &Qa, &Rf, &Sdiff, &Sdir, &Sf, &t, &trans, &U,

        // Vegetation characteristics
         &d.veg.alb0, &d.veg.vegh, &d.veg.VAI, &rhod,

        // State variables
        &d.state.albs, &d.state.Tsrf, d.state.Dsnw, &d.state.Nsnow, d.state.Qcan,
        d.state.Rgrn, d.state.Sice, d.state.Sliq, d.state.Sveg, d.state.Tcan, d.state.Tsnow,
        d.state.Tsoil, d.state.Tveg, d.state.Vsmc,

        // Diagnostics
        &d.diag.H, &d.diag.LE, &d.diag.LWout, &d.diag.LWsub, &d.diag.Melt,
        &d.diag.Roff, &d.diag.snd, &d.diag.snw, &d.diag.subl, &d.diag.svg,
        &d.diag.SWout, &d.diag.SWsub, &d.diag.Usub,  d.diag.Wflx
    );

    // ensure no stale values are cached from FSM through a snow -> snow_free cycle
    if (d.state.Nsnow == 0 || d.diag.snd <= 0.0f)
    {
        d.diag.snd = 0.0f;
        d.diag.snw = 0.0f;

        // in the case of tiny snowcover -> snow free cyles, this can end up crazy low,
        // so reinit to cold soil interface
        d.state.Tsrf = 263.0f;
    }

    (*face)["H"_s] = d.diag.H;
    (*face)["E"_s] = d.diag.LE;
    (*face)["ilwr_out"_s] = d.diag.LWout;
    (*face)["LWout"_s] = d.diag.LWout;
    //LWsub not used
    (*face)["melt_rate"_s] = d.diag.Melt;
    (*face)["roff"_s] = d.diag.Roff;
    (*face)["snowdepthavg"_s] = d.diag.snd;
    (*face)["swe"_s] = d.diag.snw;

    (*face)["snowdepthavg_vert"_s] = d.diag.snd/std::max(0.001,cos(face->slope()));

    (*face)["subl"_s] = d.diag.subl;
    // svg not used

    d.diag.sum_snowpack_subl += d.diag.subl * global_param->dt();

    (*face)["sum_snowpack_subl"_s] = d.diag.sum_snowpack_subl;
    (*face)["snow_albedo"] = d.state.albs;

    (*face)["Tsoil[0]"_s] = d.state.Tsoil[0];
    (*face)["Tsoil[1]"_s] = d.state.Tsoil[1];
    (*face)["Tsoil[2]"_s] = d.state.Tsoil[2];
    (*face)["Tsoil[3]"_s] = d.state.Tsoil[3];

    (*face)["Nsnow"_s] = d.state.Nsnow;


    (*face)["Sliq[0]"_s] = d.state.Sliq[0];
    (*face)["Sliq[1]"_s] = d.state.Sliq[1];
    (*face)["Sliq[2]"_s] = d.state.Sliq[2];

    (*face)["Tsnow[0]"_s] = d.state.Tsnow[0];
    (*face)["Tsnow[1]"_s] = d.state.Tsnow[1];
    (*face)["Tsnow[2]"_s] = d.state.Tsnow[2];
}

void FSM::checkpoint(mesh& domain,  netcdf& chkpt)
{
    chkpt.create_variable1D("fsm:snw", domain->size_local_faces());
    chkpt.create_variable1D("fsm:snd", domain->size_local_faces());
    chkpt.create_variable1D("fsm:sum_snowpack_subl", domain->size_local_faces());

    chkpt.create_variable1D("fsm:albs", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsrf", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Dsnw[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Dsnw[1]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Dsnw[2]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Dsnw[3]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Dsnw[4]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Dsnw[5]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Nsnow", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Qcan[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Qcan[1]", domain->size_local_faces());


//    chkpt.create_variable1D("fsm:Rgrn", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sice[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sice[1]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sice[2]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sice[3]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sice[4]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sice[5]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Sliq[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sliq[1]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sliq[2]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sliq[3]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sliq[4]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sliq[5]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Sveg[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Sveg[1]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Tcan[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tcan[1]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Tsnow[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsnow[1]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsnow[2]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsnow[3]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsnow[4]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsnow[5]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Tsoil[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsoil[1]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsoil[2]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tsoil[3]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Tveg[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Tveg[1]", domain->size_local_faces());

    chkpt.create_variable1D("fsm:Vsmc[0]", domain->size_local_faces());
    chkpt.create_variable1D("fsm:Vsmc[1]", domain->size_local_faces());

    //netcdf puts are not threadsafe.
    for (size_t i = 0; i < domain->size_local_faces(); i++)
    {
        auto face = domain->face(i);
        auto& d = face->get_module_data<data>(ID);

        chkpt.put_var1D("fsm:snd", i, d.diag.snd);
        chkpt.put_var1D("fsm:snw", i, d.diag.snw);
        chkpt.put_var1D("fsm:sum_snowpack_subl", i, d.diag.sum_snowpack_subl);

        chkpt.put_var1D("fsm:albs", i, d.state.albs);
        chkpt.put_var1D("fsm:Tsrf", i, d.state.Tsrf);

        chkpt.put_var1D("fsm:Dsnw[0]", i, d.state.Dsnw[0]);
        chkpt.put_var1D("fsm:Dsnw[1]", i, d.state.Dsnw[1]);
        chkpt.put_var1D("fsm:Dsnw[2]", i, d.state.Dsnw[2]);
        chkpt.put_var1D("fsm:Dsnw[3]", i, d.state.Dsnw[3]);
        chkpt.put_var1D("fsm:Dsnw[4]", i, d.state.Dsnw[4]);
        chkpt.put_var1D("fsm:Dsnw[5]", i, d.state.Dsnw[5]);

        chkpt.put_var1D("fsm:Nsnow", i, d.state.Nsnow);

        chkpt.put_var1D("fsm:Qcan[0]", i, d.state.Qcan[0]);
        chkpt.put_var1D("fsm:Qcan[1]", i, d.state.Qcan[1]);

//        chkpt.put_var1D("fsm:Rgrn", i, d.state.Rgrn);
        chkpt.put_var1D("fsm:Sice[0]", i, d.state.Sice[0]);
        chkpt.put_var1D("fsm:Sice[1]", i, d.state.Sice[1]);
        chkpt.put_var1D("fsm:Sice[2]", i, d.state.Sice[2]);
        chkpt.put_var1D("fsm:Sice[3]", i, d.state.Sice[3]);
        chkpt.put_var1D("fsm:Sice[4]", i, d.state.Sice[4]);
        chkpt.put_var1D("fsm:Sice[5]", i, d.state.Sice[5]);

        chkpt.put_var1D("fsm:Sliq[0]", i, d.state.Sliq[0]);
        chkpt.put_var1D("fsm:Sliq[1]", i, d.state.Sliq[1]);
        chkpt.put_var1D("fsm:Sliq[2]", i, d.state.Sliq[2]);
        chkpt.put_var1D("fsm:Sliq[3]", i, d.state.Sliq[3]);
        chkpt.put_var1D("fsm:Sliq[4]", i, d.state.Sliq[4]);
        chkpt.put_var1D("fsm:Sliq[5]", i, d.state.Sliq[5]);

        chkpt.put_var1D("fsm:Sveg[0]", i, d.state.Sveg[0]);
        chkpt.put_var1D("fsm:Sveg[1]", i, d.state.Sveg[1]);

        chkpt.put_var1D("fsm:Tcan[0]", i, d.state.Tcan[0]);
        chkpt.put_var1D("fsm:Tcan[1]", i, d.state.Tcan[1]);

        chkpt.put_var1D("fsm:Tsnow[0]", i, d.state.Tsnow[0]);
        chkpt.put_var1D("fsm:Tsnow[1]", i, d.state.Tsnow[1]);
        chkpt.put_var1D("fsm:Tsnow[2]", i, d.state.Tsnow[2]);
        chkpt.put_var1D("fsm:Tsnow[3]", i, d.state.Tsnow[3]);
        chkpt.put_var1D("fsm:Tsnow[4]", i, d.state.Tsnow[4]);
        chkpt.put_var1D("fsm:Tsnow[5]", i, d.state.Tsnow[5]);

        chkpt.put_var1D("fsm:Tsoil[0]", i, d.state.Tsoil[0]);
        chkpt.put_var1D("fsm:Tsoil[1]", i, d.state.Tsoil[1]);
        chkpt.put_var1D("fsm:Tsoil[2]", i, d.state.Tsoil[2]);
        chkpt.put_var1D("fsm:Tsoil[3]", i, d.state.Tsoil[3]);

        chkpt.put_var1D("fsm:Tveg[0]", i, d.state.Tveg[0]);
        chkpt.put_var1D("fsm:Tveg[1]", i, d.state.Tveg[1]);

        chkpt.put_var1D("fsm:Vsmc[0]", i, d.state.Vsmc[0]);
        chkpt.put_var1D("fsm:Vsmc[1]", i, d.state.Vsmc[1]);

    }
}

void FSM::load_checkpoint(mesh& domain, netcdf& chkpt)
{
    for (size_t i = 0; i < domain->size_local_faces(); i++)
    {
        auto face = domain->face(i);
        auto& d = face->get_module_data<data>(ID);

        d.diag.snd = chkpt.get_var1D("fsm:snd", i);
        d.diag.snw = chkpt.get_var1D("fsm:snw", i);
        d.diag.sum_snowpack_subl =  chkpt.get_var1D("fsm:sum_snowpack_subl", i);

        d.state.albs = chkpt.get_var1D("fsm:albs", i);
        d.state.Tsrf = chkpt.get_var1D("fsm:Tsrf", i);

        d.state.Dsnw[0] = chkpt.get_var1D("fsm:Dsnw[0]", i);
        d.state.Dsnw[1] = chkpt.get_var1D("fsm:Dsnw[1]", i);
        d.state.Dsnw[2] = chkpt.get_var1D("fsm:Dsnw[2]", i);
        d.state.Dsnw[3] = chkpt.get_var1D("fsm:Dsnw[3]", i);
        d.state.Dsnw[4] = chkpt.get_var1D("fsm:Dsnw[4]", i);
        d.state.Dsnw[5] = chkpt.get_var1D("fsm:Dsnw[5]", i);

        d.state.Nsnow = chkpt.get_var1D("fsm:Nsnow", i);

        d.state.Qcan[0] = chkpt.get_var1D("fsm:Qcan[0]", i);
        d.state.Qcan[1] = chkpt.get_var1D("fsm:Qcan[1]", i);

        d.state.Sice[0] = chkpt.get_var1D("fsm:Sice[0]", i);
        d.state.Sice[1] = chkpt.get_var1D("fsm:Sice[1]", i);
        d.state.Sice[2] = chkpt.get_var1D("fsm:Sice[2]", i);
        d.state.Sice[3] = chkpt.get_var1D("fsm:Sice[3]", i);
        d.state.Sice[4] = chkpt.get_var1D("fsm:Sice[4]", i);
        d.state.Sice[5] = chkpt.get_var1D("fsm:Sice[5]", i);

        d.state.Sliq[0] = chkpt.get_var1D("fsm:Sliq[0]", i);
        d.state.Sliq[1] = chkpt.get_var1D("fsm:Sliq[1]", i);
        d.state.Sliq[2] = chkpt.get_var1D("fsm:Sliq[2]", i);
        d.state.Sliq[3] = chkpt.get_var1D("fsm:Sliq[3]", i);
        d.state.Sliq[4] = chkpt.get_var1D("fsm:Sliq[4]", i);
        d.state.Sliq[5] = chkpt.get_var1D("fsm:Sliq[5]", i);

        d.state.Sveg[0] = chkpt.get_var1D("fsm:Sveg[0]", i);
        d.state.Sveg[1] = chkpt.get_var1D("fsm:Sveg[1]", i);

        d.state.Tcan[0] = chkpt.get_var1D("fsm:Tcan[0]", i);
        d.state.Tcan[1] = chkpt.get_var1D("fsm:Tcan[1]", i);

        d.state.Tsnow[0] = chkpt.get_var1D("fsm:Tsnow[0]", i);
        d.state.Tsnow[1] = chkpt.get_var1D("fsm:Tsnow[1]", i);
        d.state.Tsnow[2] = chkpt.get_var1D("fsm:Tsnow[2]", i);
        d.state.Tsnow[3] = chkpt.get_var1D("fsm:Tsnow[3]", i);
        d.state.Tsnow[4] = chkpt.get_var1D("fsm:Tsnow[4]", i);
        d.state.Tsnow[5] = chkpt.get_var1D("fsm:Tsnow[5]", i);

        d.state.Tsoil[0] = chkpt.get_var1D("fsm:Tsoil[0]", i);
        d.state.Tsoil[1] = chkpt.get_var1D("fsm:Tsoil[1]", i);
        d.state.Tsoil[2] = chkpt.get_var1D("fsm:Tsoil[2]", i);
        d.state.Tsoil[3] = chkpt.get_var1D("fsm:Tsoil[3]", i);

        d.state.Tveg[0] = chkpt.get_var1D("fsm:Tveg[0]", i);
        d.state.Tveg[1] = chkpt.get_var1D("fsm:Tveg[1]", i);

        d.state.Vsmc[0] = chkpt.get_var1D("fsm:Vsmc[0]", i);
        d.state.Vsmc[1] = chkpt.get_var1D("fsm:Vsmc[1]", i);

        (*face)["swe"_s] = d.diag.snw;
        (*face)["snowdepthavg"_s] = d.diag.snd;
        (*face)["snowdepthavg_vert"_s] = d.diag.snd/std::max(0.001,cos(face->slope()));
        (*face)["sum_snowpack_subl"_s] = d.diag.sum_snowpack_subl;

        (*face)["H"_s] = d.diag.H;
        (*face)["E"_s] = d.diag.LE;
        (*face)["subl"_s] = d.diag.subl;

        (*face)["snow_albedo"] = d.state.albs;
    }
}

FSM::~FSM()
{


}