Program Listing for File scale_wind_vert.cpp

Program Listing for File scale_wind_vert.cpp#

Return to documentation for file (src/modules/scale_wind_vert.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 "scale_wind_vert.hpp"
REGISTER_MODULE_CPP(scale_wind_vert);

scale_wind_vert::scale_wind_vert(config_file cfg)
        : module_base("scale_wind_vert", parallel::data, cfg)
// Assume we are running in point mode, this gets us past the check in core. However we can later enable domain mode if we need to. We can't
// do that in this ctor as global isn't defined yet and we don't

{


    depends("U_R");

    optional("snowdepthavg");

    //provides("U_CanTop"); // Possible output, but commented out for speed
    //provides("U_CanMid");
    provides("U_2m_above_srf");

    SPDLOG_DEBUG("Successfully instantiated module {}",this->ID);
}

scale_wind_vert::~scale_wind_vert()
{


}

void scale_wind_vert::point_scale(mesh_elem &face)
{

    // Get meteorological data for current face
    double U_R = (*face)["U_R"_s]; // Wind speed at reference height Z_R (m/s)

    // Get height info
    double Z_R = Atmosphere::Z_U_R; // Reference wind speed height [m] = 50.0 m

    double Z_CanTop = 0;

    if (!ignore_canopy && face->has_vegetation())
    {
        Z_CanTop = face->veg_attribute("CanopyHeight");
    }
    double Z_CanBot = Z_CanTop /
                      2.0; //global_param->parameters.get<double>("landcover." + std::to_string(LC) + ".TrunkHeight"); // TODO: HARDCODED until we get from obs
    //double Z_CanMid       = (Z_CanTop+Z_CanBot)/2.0; // Mid height of canopy
    double snowdepthavg = 0;
    if (has_optional("snowdepthavg"))
        snowdepthavg = (*face)["snowdepthavg"_s];

    // Snow depth check
    if (is_nan(snowdepthavg)) // If it is not defined
        snowdepthavg = 0.0;

    double Z_2m_above_srf = snowdepthavg + 2.0; // (m)

    // Initialize stuff
    double U_2m_above_srf; // Wind speed 2 meters above the surface (ground or snow)

    // Check if snowdepth is above U_R (avalanche gone crazy case)
    // This is outside of log_scale_wind()'s range, so make assumption that wind is constant above U_R
    if (Z_2m_above_srf >= Z_R)
    {
        (*face)["U_2m_above_srf"_s]= U_R;
        return;
    }


    // Call wind speed scaling functions to specific heights

    // If a Canopy exists
    if ( !ignore_canopy && (Z_CanTop > 0.0) && (Z_2m_above_srf <  Z_CanTop))
    {
        // Get Canopy/Surface info

        //assume we have LAI, otherwise it will cleanly bail if we don't
        double LAI = face->veg_attribute("LAI");
        const double alpha = LAI; // attenuation coefficient introduced by Inoue (1963) and increases with canopy density

        // If snowdepth is below the Canopy Top
        if (snowdepthavg < Z_CanTop)
        {
            // Scale Z_R to Z_CanTop
            double U_CanTop = Atmosphere::log_scale_wind(U_R, Z_R, Z_CanTop, snowdepthavg);

            // Scale Z_CanTop to Z_CanBot
            double U_CanBot = Atmosphere::exp_scale_wind(U_CanTop, Z_CanTop, Z_CanBot, alpha);

            // Scale Z_CanTop to Z_CanMid
            //double U_CanMid = Atmosphere::exp_scale_wind(U_CanTop, Z_CanTop, Z_CanMid, alpha);

            // snowdepth is below bottom of canopy, scale bottom of canopy wind to surface
            if (Z_2m_above_srf < Z_CanBot)
                U_2m_above_srf = Atmosphere::log_scale_wind(U_CanBot, Z_CanBot, Z_2m_above_srf,
                                                            snowdepthavg);
            else // snow depth +2 above canopy bottom
                U_2m_above_srf = Atmosphere::exp_scale_wind(U_CanTop, Z_CanTop, Z_2m_above_srf, alpha);
        } else
        {
            // Scale Z_R to snowdepth (which is above canopy height)
            U_2m_above_srf = Atmosphere::log_scale_wind(U_R, Z_R, Z_2m_above_srf, snowdepthavg);
        }

        // No Canopy exists
    } else
    {
        U_2m_above_srf = Atmosphere::log_scale_wind(U_R, Z_R, Z_2m_above_srf,
                                                    snowdepthavg); // (U_start,Height_start,Height_end)
    }

    // Check that U_2m_above_srf is not too small for turbulent parameterizations (should move check there)
    U_2m_above_srf = std::max(0.1, U_2m_above_srf);
    (*face)["U_2m_above_srf"_s]= U_2m_above_srf;

};

void scale_wind_vert::init(mesh& domain)
{
    //since we can optionally run this module in point or domain mode, we need to turn back on the domain parallel flag at this point
    if(!global_param->is_point_mode())
        _parallel_type =  parallel::domain;


#pragma omp parallel for
    for (size_t i = 0; i < domain->size_local_faces(); i++)
    {
      auto face = domain->face(i);
      // Exception throwing from OpenMP needs to be here

       auto& data = face->make_module_data<d>(ID);
       data.interp.init(interp_alg::tpspline,3,{{"reuse_LU","true"}});

    }


    ignore_canopy = cfg.get("ignore_canopy",false);

}

void scale_wind_vert::run(mesh_elem &face)
{
    point_scale(face);
}

void scale_wind_vert::run(mesh& domain)
{

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

        point_scale(face);

    }

    // Need to access U_2m_above_srf from neighbors
    domain->ghost_neighbors_communicate_variable("U_2m_above_srf"_s);

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

        std::vector<boost::tuple<double, double, double> > u;
        for (size_t j = 0; j < 3; j++)
        {
            auto neigh = face->neighbor(j);

             if (neigh != nullptr)
             {
                 u.push_back(boost::make_tuple(neigh->get_x(), neigh->get_y(), (*neigh)["U_2m_above_srf"_s]));
             }

        }

        auto query = boost::make_tuple(face->get_x(), face->get_y(), face->get_z());

        if(!u.empty())
        {
            double new_u =  face->get_module_data<d>(ID).interp(u, query);

            face->get_module_data<d>(ID).temp_u = std::max(0.1,new_u);
        }
        else
        {
            face->get_module_data<d>(ID).temp_u = std::max(0.1,(*face)["U_2m_above_srf"_s]);
        }

    }


#pragma omp parallel for
    for (size_t i = 0; i < domain->size_local_faces(); i++)
    {
        auto face = domain->face(i);
        // Exception throwing from OpenMP needs to be here
        (*face)["U_2m_above_srf"_s]=face->get_module_data<d>(ID).temp_u ;

    }


}