/*
LICENSE: see isogeometric_application/LICENSE.txt
*/

//
//   Project Name:        Kratos
//   Last modified by:    $Author: hbui $
//   Date:                $Date: Jan 9, 2013 $
//   Revision:            $Revision: 1.1 $
//
//

// System includes
#include <string>

// External includes
#include <boost/foreach.hpp>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/python/operators.hpp>

// Project includes
#include "includes/define.h"
#include "includes/model_part.h"
#include "custom_utilities/iga_define.h"
#include "custom_utilities/bspline_utils.h"
#include "custom_utilities/isogeometric_post_utility.h"
#include "custom_utilities/bezier_classical_post_utility.h"
#include "custom_utilities/bezier_post_utility.h"
#include "custom_utilities/isogeometric_test_utils.h"
#include "custom_utilities/bezier_test_utils.h"
#include "custom_utilities/isogeometric_merge_utility.h"
#include "custom_python/add_utilities_to_python.h"

#ifdef ISOGEOMETRIC_USE_HDF5
#include "custom_utilities/hdf5_post_utility.h"
#endif

#ifdef ISOGEOMETRIC_USE_GISMO
#include "custom_utilities/gismo/gismo_mesh.h"
#endif

namespace Kratos
{

namespace Python
{

using namespace boost::python;

int BSplineUtils_FindSpan(
    BSplineUtils& dummy,
    const int rN,
    const int rP,
    const double rXi,
    const Vector& rU
)
{
    return dummy.FindSpan(rN, rP, rXi, rU);
}

void BSplineUtils_BasisFuns(
    BSplineUtils& dummy,
    Vector& rS,
    const int rI,
    const double rXi,
    const int rP,
    const Vector& rU
)
{
    dummy.BasisFuns(rS, rI, rXi, rP, rU);
}

void BezierUtils_Bernstein(
    BezierUtils& dummy,
    BezierUtils::ValuesContainerType& rS,
    const int p,
    const double x
)
{
    dummy.bernstein(rS, p, x);
}

void BezierUtils_Bernstein_der(
    BezierUtils& dummy,
    BezierUtils::ValuesContainerType& rS,
    BezierUtils::ValuesContainerType& rD,
    const int p,
    const double x
)
{
    dummy.bernstein(rS, rD, p, x);
}

void BezierUtils_DumpShapeFunctionsIntegrationPointsValuesAndLocalGradients(
    BezierUtils& dummy,
    ModelPart::Pointer pModelPart,
    std::string FileName
)
{
    dummy.DumpShapeFunctionsIntegrationPointsValuesAndLocalGradients(pModelPart, FileName);
}

template<class T>
void BezierUtils_ComputeCentroid(
    BezierUtils& dummy,
    typename T::Pointer& pElem,
    typename T::GeometryType::PointType& P
)
{
    dummy.ComputeCentroid<T>(pElem, P);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeGlobalCoordinates1(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X
)
{
    dummy.ProbeGlobalCoordinates(pElement->GetGeometry(), X, 0.0, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeGlobalCoordinates2(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y
)
{
    dummy.ProbeGlobalCoordinates(pElement->GetGeometry(), X, Y, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeGlobalCoordinates3(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y, double Z
)
{
    dummy.ProbeGlobalCoordinates(pElement->GetGeometry(), X, Y, Z);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionValues1(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X
)
{
    dummy.ProbeShapeFunctionValues(pElement->GetGeometry(), X, 0.0, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionValues2(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y
)
{
    dummy.ProbeShapeFunctionValues(pElement->GetGeometry(), X, Y, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionValues3(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y, double Z
)
{
    dummy.ProbeShapeFunctionValues(pElement->GetGeometry(), X, Y, Z);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionDerivatives1(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X
)
{
    dummy.ProbeShapeFunctionDerivatives(pElement->GetGeometry(), X, 0.0, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionDerivatives2(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y
)
{
    dummy.ProbeShapeFunctionDerivatives(pElement->GetGeometry(), X, Y, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionDerivatives3(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y, double Z
)
{
    dummy.ProbeShapeFunctionDerivatives(pElement->GetGeometry(), X, Y, Z);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeJacobian1(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X
)
{
    dummy.ProbeJacobian(pElement->GetGeometry(), X, 0.0, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeJacobian2(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y
)
{
    dummy.ProbeJacobian(pElement->GetGeometry(), X, Y, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeJacobian3(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y, double Z
)
{
    dummy.ProbeJacobian(pElement->GetGeometry(), X, Y, Z);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives1(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X
)
{
    dummy.ProbeShapeFunctionSecondDerivatives(pElement->GetGeometry(), X, 0.0, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives2(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y
)
{
    dummy.ProbeShapeFunctionSecondDerivatives(pElement->GetGeometry(), X, Y, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives3(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y, double Z
)
{
    dummy.ProbeShapeFunctionSecondDerivatives(pElement->GetGeometry(), X, Y, Z);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives1(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X
)
{
    dummy.ProbeShapeFunctionThirdDerivatives(pElement->GetGeometry(), X, 0.0, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives2(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y
)
{
    dummy.ProbeShapeFunctionThirdDerivatives(pElement->GetGeometry(), X, Y, 0.0);
}

template<typename TEntityType>
void IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives3(
    IsogeometricTestUtils& dummy,
    typename TEntityType::Pointer pElement,
    double X, double Y, double Z
)
{
    dummy.ProbeShapeFunctionThirdDerivatives(pElement->GetGeometry(), X, Y, Z);
}

//////////////////////////////////////////////////////////

ModelPart::ElementsContainerType IsogeometricPostUtility_TransferElements(IsogeometricPostUtility& rDummy, ModelPart::ElementsContainerType& pElements,
    ModelPart& r_other_model_part, const std::string& sample_element_name, Properties::Pointer pProperties, const bool& retain_prop_id)
{
    std::size_t last_element_id = IsogeometricPostUtility::GetLastElementId(r_other_model_part);
    if (!KratosComponents<Element>::Has(sample_element_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_element_name, "is not registered to the Kratos kernel")
    Element const& r_clone_element = KratosComponents<Element>::Get(sample_element_name);
    ModelPart::ElementsContainerType pNewElements = IsogeometricPostUtility::CreateEntities(pElements, r_other_model_part, r_clone_element, last_element_id, pProperties, retain_prop_id);

    for(ModelPart::ElementsContainerType::ptr_iterator it = pNewElements.ptr_begin(); it != pNewElements.ptr_end(); ++it)
        r_other_model_part.Elements().push_back(*it);

    std::cout << "Transfer mesh completed, "
              << pNewElements.size() << " elements of type " << sample_element_name
              << " was added to model_part" << r_other_model_part.Name() << std::endl;

    return pNewElements;
}

ModelPart::ConditionsContainerType IsogeometricPostUtility_TransferConditions(IsogeometricPostUtility& rDummy, ModelPart::ConditionsContainerType& pConditions,
    ModelPart& r_other_model_part, const std::string& sample_condition_name, Properties::Pointer pProperties, const bool& retain_prop_id)
{
    std::size_t last_condition_id = IsogeometricPostUtility::GetLastConditionId(r_other_model_part);
    if (!KratosComponents<Condition>::Has(sample_condition_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_condition_name, "is not registered to the Kratos kernel")
    Condition const& r_clone_condition = KratosComponents<Condition>::Get(sample_condition_name);
    ModelPart::ConditionsContainerType pNewConditions = IsogeometricPostUtility::CreateEntities(pConditions, r_other_model_part, r_clone_condition, last_condition_id, pProperties, retain_prop_id);

    for(ModelPart::ConditionsContainerType::ptr_iterator it = pNewConditions.ptr_begin(); it != pNewConditions.ptr_end(); ++it)
        r_other_model_part.Conditions().push_back(*it);

    std::cout << "Transfer mesh completed, "
              << pNewConditions.size() << " conditions of type " << sample_condition_name
              << " was added to model_part" << r_other_model_part.Name() << std::endl;

    return pNewConditions;
}

ModelPart::ElementsContainerType IsogeometricPostUtility_FindElements(IsogeometricPostUtility& rDummy,
    ModelPart::ElementsContainerType& pElements, const std::string& sample_element_name)
{
    if (!KratosComponents<Element>::Has(sample_element_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_element_name, "is not registered to the Kratos kernel")
    Element const& r_clone_element = KratosComponents<Element>::Get(sample_element_name);
    return IsogeometricPostUtility::FindEntities(pElements, r_clone_element);
}

ModelPart::ConditionsContainerType IsogeometricPostUtility_FindConditions(IsogeometricPostUtility& rDummy,
    ModelPart::ConditionsContainerType& pConditions, const std::string& sample_condition_name)
{
    if (!KratosComponents<Condition>::Has(sample_condition_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_condition_name, "is not registered to the Kratos kernel")
    Condition const& r_clone_condition = KratosComponents<Condition>::Get(sample_condition_name);
    return IsogeometricPostUtility::FindEntities(pConditions, r_clone_condition);
}

template<typename TCoordinatesType, typename TPatchType>
boost::python::list IsogeometricPostUtility_CreateConditionsByTriangulation(IsogeometricPostUtility& rDummy,
    const boost::python::list& list_physical_points,
    const Vector& center, const Vector& normal, const Vector& t1, const Vector& t2,
    const boost::python::list& list_local_points, const std::size_t& nrefine,
    typename TPatchType::Pointer pPatch, ModelPart& r_model_part,
    const std::string& sample_condition_name,
    const std::size_t& last_node_id, const std::size_t& last_condition_id,
    Properties::Pointer pProperties)
{
    if (!KratosComponents<Condition>::Has(sample_condition_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_condition_name, "is not registered to the Kratos kernel")
    Condition const& r_clone_condition = KratosComponents<Condition>::Get(sample_condition_name);

    typedef boost::python::stl_input_iterator<array_1d<double, 3> > iterator_value_type;

    std::vector<TCoordinatesType> physical_points;
    BOOST_FOREACH(const iterator_value_type::value_type& p,
                std::make_pair(iterator_value_type(list_physical_points), // begin
                iterator_value_type() ) ) // end
    {
        physical_points.push_back(p);
    }

    std::vector<TCoordinatesType> local_points;
    BOOST_FOREACH(const iterator_value_type::value_type& p,
                std::make_pair(iterator_value_type(list_local_points), // begin
                iterator_value_type() ) ) // end
    {
        local_points.push_back(p);
    }

    std::size_t offset = last_node_id + 1;
    std::pair<std::vector<TCoordinatesType>, std::vector<std::vector<std::size_t> > >
    points_and_connectivities = IsogeometricPostUtility::GenerateTriangleGrid(physical_points, center, normal, t1, t2, local_points, offset, nrefine);

    boost::python::list new_nodes;
    boost::python::list new_local_points;
    std::size_t starting_node_id = last_node_id + 1;
    for (std::size_t i = 0; i < points_and_connectivities.first.size(); ++i)
    {
        new_local_points.append(points_and_connectivities.first[i]);
        ModelPart::NodeType::Pointer pNewNode = IsogeometricPostUtility::CreateNode(points_and_connectivities.first[i], *pPatch, r_model_part, starting_node_id++);
        new_nodes.append(pNewNode);
        // std::cout << "node " << pNewNode->Id() << " (" << pNewNode->X0() << " " << pNewNode->Y0() << " " << pNewNode->Z0() << ") is created at " << points_and_connectivities.first[i] << std::endl;
    }

    // std::cout << "connectivities:" << std::endl;
    // for (std::size_t i = 0; i < points_and_connectivities.second.size(); ++i)
    // {
    //     std::cout << " ";
    //     for (std::size_t j = 0; j < points_and_connectivities.second[i].size(); ++j)
    //         std::cout << " " << points_and_connectivities.second[i][j];
    //     std::cout << std::endl;
    // }

    std::size_t last_condition_id_new = last_condition_id;
    const std::string NodeKey = std::string("Node");
    ModelPart::ConditionsContainerType pNewConditions = IsogeometricPostUtility::CreateEntities<std::vector<std::vector<std::size_t> >, Condition, ModelPart::ConditionsContainerType>(
        points_and_connectivities.second, r_model_part, r_clone_condition, last_condition_id_new, pProperties, NodeKey);

    boost::python::list output;
    output.append(new_local_points);
    output.append(new_nodes);
    output.append(pNewConditions);
    return output;
}

template<typename TCoordinatesType, typename TPatchType>
boost::python::list IsogeometricPostUtility_CreateConditionsByQuadrilateralization(IsogeometricPostUtility& rDummy,
    const TCoordinatesType& p1, const TCoordinatesType& p2,
    const TCoordinatesType& p3, const TCoordinatesType& p4,
    const std::size_t& num_div_1, const std::size_t& num_div_2,
    typename TPatchType::Pointer pPatch, ModelPart& r_model_part,
    const std::string& sample_condition_name,
    const std::size_t& last_node_id, const std::size_t& last_condition_id,
    Properties::Pointer pProperties)
{
    if (!KratosComponents<Condition>::Has(sample_condition_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_condition_name, "is not registered to the Kratos kernel")
    Condition const& r_clone_condition = KratosComponents<Condition>::Get(sample_condition_name);

    std::size_t starting_node_id = last_node_id + 1;
    std::pair<std::vector<TCoordinatesType>, std::vector<std::vector<std::size_t> > >
    points_and_connectivities = IsogeometricPostUtility::GenerateQuadGrid(p1, p2, p3, p4, starting_node_id, num_div_1, num_div_2);

    boost::python::list new_nodes;
    boost::python::list new_local_points;
    for (std::size_t i = 0; i < points_and_connectivities.first.size(); ++i)
    {
        new_local_points.append(points_and_connectivities.first[i]);
        ModelPart::NodeType::Pointer pNewNode = IsogeometricPostUtility::CreateNode(points_and_connectivities.first[i], *pPatch, r_model_part, starting_node_id++);
        new_nodes.append(pNewNode);
        // std::cout << "node " << pNewNode->Id() << " (" << pNewNode->X0() << " " << pNewNode->Y0() << " " << pNewNode->Z0() << ") is created at " << points_and_connectivities.first[i] << std::endl;
    }

    // std::cout << "connectivities:" << std::endl;
    // for (std::size_t i = 0; i < points_and_connectivities.second.size(); ++i)
    // {
    //     std::cout << " ";
    //     for (std::size_t j = 0; j < points_and_connectivities.second[i].size(); ++j)
    //         std::cout << " " << points_and_connectivities.second[i][j];
    //     std::cout << std::endl;
    // }

    std::size_t last_condition_id_new = last_condition_id;
    const std::string NodeKey = std::string("Node");
    ModelPart::ConditionsContainerType pNewConditions = IsogeometricPostUtility::CreateEntities<std::vector<std::vector<std::size_t> >, Condition, ModelPart::ConditionsContainerType>(
        points_and_connectivities.second, r_model_part, r_clone_condition, last_condition_id_new, pProperties, NodeKey);

    boost::python::list output;
    output.append(new_local_points);
    output.append(new_nodes);
    output.append(pNewConditions);
    return output;
}

template<class TPatchType>
void IsogeometricPostUtility_TransferValuesToNodes(IsogeometricPostUtility& rDummy, Element::GeometryType::PointType& rNode, const TPatchType& rPatch)
{
    rDummy.TransferValuesToNodes(rNode, rPatch);
}

template<class TEntityType, typename TVariableType, class TPatchType>
void IsogeometricPostUtility_TransferValuesToGaussPoints(IsogeometricPostUtility& rDummy, TEntityType& rElement,
    const TVariableType& rVariable, const TPatchType& rPatch, const ProcessInfo& rProcessInfo)
{
    rDummy.TransferValuesToGaussPoints(rElement, rVariable, rPatch, rProcessInfo);
}

//////////////////////////////////////////////////////////

boost::python::list BezierClassicalPostUtility_GenerateConditions(BezierClassicalPostUtility& dummy,
        ModelPart& rModelPart,
        Condition& rCondition,
        const std::string& sample_condition_name,
        const std::size_t& starting_node_id,
        const std::size_t& starting_condition_id)
{
    if (!KratosComponents<Condition>::Has(sample_condition_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_condition_name, "is not registered to the Kratos kernel")
    Condition const& r_clone_condition = KratosComponents<Condition>::Get(sample_condition_name);
    std::size_t NodeCounter = starting_node_id;
    std::size_t NodeCounter_old = NodeCounter;
    std::size_t ConditionCounter = starting_condition_id;
    const std::string NodeKey = std::string("Node");
    std::vector<std::size_t> node_ids;
    std::vector<std::size_t> element_ids;
    dummy.GenerateForOneEntity<Condition, ModelPart::ConditionsContainerType, 2>(rModelPart, rCondition,
            r_clone_condition, NodeCounter_old, NodeCounter, ConditionCounter, NodeKey, false,
            node_ids, element_ids, true);

    boost::python::list list_nodes;
    boost::python::list list_elements;
    for (std::size_t i = 0; i < node_ids.size(); ++i) list_nodes.append(node_ids[i]);
    for (std::size_t i = 0; i < element_ids.size(); ++i) list_elements.append(element_ids[i]);
    boost::python::list Output;
    Output.append(list_nodes);
    Output.append(list_elements);
    return Output;
}

boost::python::list BezierClassicalPostUtility_GenerateConditionsWithNodalVariables(BezierClassicalPostUtility& dummy,
        ModelPart& rModelPart,
        Condition& rCondition,
        const std::string& sample_condition_name,
        const std::size_t& starting_node_id,
        const std::size_t& starting_condition_id)
{
    if (!KratosComponents<Condition>::Has(sample_condition_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_condition_name, "is not registered to the Kratos kernel")
    Condition const& r_clone_condition = KratosComponents<Condition>::Get(sample_condition_name);
    std::size_t NodeCounter = starting_node_id;
    std::size_t NodeCounter_old = NodeCounter;
    std::size_t ConditionCounter = starting_condition_id;
    const std::string NodeKey = std::string("Node");
    std::vector<std::size_t> node_ids;
    std::vector<std::size_t> element_ids;
    dummy.GenerateForOneEntity<Condition, ModelPart::ConditionsContainerType, 2>(rModelPart, rCondition,
            r_clone_condition, NodeCounter_old, NodeCounter, ConditionCounter, NodeKey, true,
            node_ids, element_ids, true);

    boost::python::list list_nodes;
    boost::python::list list_elements;
    for (std::size_t i = 0; i < node_ids.size(); ++i) list_nodes.append(node_ids[i]);
    for (std::size_t i = 0; i < element_ids.size(); ++i) list_elements.append(element_ids[i]);
    boost::python::list Output;
    Output.append(list_nodes);
    Output.append(list_elements);
    return Output;
}

boost::python::list BezierClassicalPostUtility_GenerateElements(BezierClassicalPostUtility& dummy,
        ModelPart& rModelPart,
        Element& rElement,
        const std::string& sample_element_name,
        const std::size_t& starting_node_id,
        const std::size_t& starting_element_id)
{
    if (!KratosComponents<Element>::Has(sample_element_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_element_name, "is not registered to the Kratos kernel")
    Element const& r_clone_element = KratosComponents<Element>::Get(sample_element_name);
    std::size_t NodeCounter = starting_node_id;
    std::size_t NodeCounter_old = NodeCounter;
    std::size_t ElementCounter = starting_element_id;
    const std::string NodeKey = std::string("Node");
    std::vector<std::size_t> node_ids;
    std::vector<std::size_t> element_ids;
    dummy.GenerateForOneEntity<Element, ModelPart::ElementsContainerType, 2>(rModelPart, rElement,
            r_clone_element, NodeCounter_old, NodeCounter, ElementCounter, NodeKey, false,
            node_ids, element_ids, true);

    boost::python::list list_nodes;
    boost::python::list list_elements;
    for (std::size_t i = 0; i < node_ids.size(); ++i) list_nodes.append(node_ids[i]);
    for (std::size_t i = 0; i < element_ids.size(); ++i) list_elements.append(element_ids[i]);
    boost::python::list Output;
    Output.append(list_nodes);
    Output.append(list_elements);
    return Output;
}

boost::python::list BezierClassicalPostUtility_GenerateElementsWithNodalVariables(BezierClassicalPostUtility& dummy,
        ModelPart& rModelPart,
        Element& rElement,
        const std::string& sample_element_name,
        const std::size_t& starting_node_id,
        const std::size_t& starting_element_id)
{
    if (!KratosComponents<Element>::Has(sample_element_name))
        KRATOS_THROW_ERROR(std::logic_error, sample_element_name, "is not registered to the Kratos kernel")
    Element const& r_clone_element = KratosComponents<Element>::Get(sample_element_name);
    std::size_t NodeCounter = starting_node_id;
    std::size_t NodeCounter_old = NodeCounter;
    std::size_t ElementCounter = starting_element_id;
    const std::string NodeKey = std::string("Node");
    std::vector<std::size_t> node_ids;
    std::vector<std::size_t> element_ids;
    dummy.GenerateForOneEntity<Element, ModelPart::ElementsContainerType, 2>(rModelPart, rElement,
            r_clone_element, NodeCounter_old, NodeCounter, ElementCounter, NodeKey, true,
            node_ids, element_ids, true);

    boost::python::list list_nodes;
    boost::python::list list_elements;
    for (std::size_t i = 0; i < node_ids.size(); ++i) list_nodes.append(node_ids[i]);
    for (std::size_t i = 0; i < element_ids.size(); ++i) list_elements.append(element_ids[i]);
    boost::python::list Output;
    Output.append(list_nodes);
    Output.append(list_elements);
    return Output;
}

void BezierClassicalPostUtility_GenerateModelPart2WithCondition(BezierClassicalPostUtility& dummy, ModelPart::Pointer pModelPartPost)
{
    dummy.GenerateModelPart2(pModelPartPost, true);
}

void BezierClassicalPostUtility_GenerateModelPart2(BezierClassicalPostUtility& dummy, ModelPart::Pointer pModelPartPost, const bool& generate_for_condition)
{
    dummy.GenerateModelPart2(pModelPartPost, generate_for_condition);
}

//////////////////////////////////////////////////////////

template<typename TVariableType>
void BezierPostUtility_TransferVariablesToNodes_ModelPart(BezierPostUtility& rDummy,
    const TVariableType& rThisVariable,
    ModelPart& r_model_part,
    BezierPostUtility::LinearSolverType::Pointer pSolver)
{
    rDummy.TransferVariablesToNodes(rThisVariable, r_model_part, pSolver);
}

template<typename TVariableType>
void BezierPostUtility_TransferVariablesToNodes_Elements(BezierPostUtility& rDummy,
    const TVariableType& rThisVariable,
    ModelPart& r_model_part, BezierPostUtility::ElementsArrayType& ElementsArray,
    BezierPostUtility::LinearSolverType::Pointer pSolver)
{
    rDummy.TransferVariablesToNodes(rThisVariable, r_model_part, ElementsArray, pSolver);
}

//////////////////////////////////////////////////////////

void IsogeometricApplication_AddBackendUtilitiesToPython()
{
    enum_<PostElementType>("PostElementType")
    .value("Triangle", _TRIANGLE_)
    .value("Quadrilateral", _QUADRILATERAL_)
    .value("Tetrahedra", _TETRAHEDRA_)
    .value("Hexahedra", _HEXAHEDRA_)
    ;

    class_<IsogeometricEcho, boost::noncopyable>("IsogeometricEcho", init<>())
    .def("SetEchoLevel", &IsogeometricEcho::SetEchoLevel)
    // .def("GetEchoLevel", IsogeometricEcho_GetEchoLevel)
    ;

    class_<BSplineUtils, BSplineUtils::Pointer, boost::noncopyable>("BSplineUtils", init<>())
    .def("FindSpan", BSplineUtils_FindSpan)
    .def("BasisFuns", BSplineUtils_BasisFuns)
    .def("test_ComputeBsplinesKnotInsertionCoefficients1DLocal", &BSplineUtils::test_ComputeBsplinesKnotInsertionCoefficients1DLocal)
    ;

    class_<BezierUtils, BezierUtils::Pointer, boost::noncopyable>("BezierUtils", init<>())
    .def("Bernstein", BezierUtils_Bernstein)
    .def("BernsteinDerivative", BezierUtils_Bernstein_der)
    .def("DumpShapeFunctionsIntegrationPointsValuesAndLocalGradients", BezierUtils_DumpShapeFunctionsIntegrationPointsValuesAndLocalGradients)
    .def("ComputeCentroid", BezierUtils_ComputeCentroid<Element>)
    .def("ComputeCentroid", BezierUtils_ComputeCentroid<Condition>)
//    .def("compute_extended_knot_vector", &BezierUtils::compute_extended_knot_vector)
//    .def("bezier_extraction_tsplines_1d", &BezierUtils::bezier_extraction_tsplines_1d)
    ;

    class_<IsogeometricPostUtility, IsogeometricPostUtility::Pointer, boost::noncopyable>("IsogeometricPostUtility", init<>())
    .def("TransferElements", &IsogeometricPostUtility_TransferElements)
    .def("TransferConditions", &IsogeometricPostUtility_TransferConditions)
    .def("TransferValuesToNodes", &IsogeometricPostUtility_TransferValuesToNodes<Patch<2> >)
    .def("TransferValuesToNodes", &IsogeometricPostUtility_TransferValuesToNodes<Patch<2> >)
    .def("TransferValuesToGaussPoints", &IsogeometricPostUtility_TransferValuesToGaussPoints<Element, Variable<double>, Patch<2> >)
    .def("TransferValuesToGaussPoints", &IsogeometricPostUtility_TransferValuesToGaussPoints<Element, Variable<array_1d<double, 3> >, Patch<2> >)
    .def("TransferValuesToGaussPoints", &IsogeometricPostUtility_TransferValuesToGaussPoints<Element, Variable<Vector>, Patch<2> >)
    .def("TransferValuesToGaussPoints", &IsogeometricPostUtility_TransferValuesToGaussPoints<Element, Variable<double>, Patch<3> >)
    .def("TransferValuesToGaussPoints", &IsogeometricPostUtility_TransferValuesToGaussPoints<Element, Variable<array_1d<double, 3> >, Patch<3> >)
    .def("TransferValuesToGaussPoints", &IsogeometricPostUtility_TransferValuesToGaussPoints<Element, Variable<Vector>, Patch<3> >)
    .def("FindElements", &IsogeometricPostUtility_FindElements)
    .def("FindConditions", &IsogeometricPostUtility_FindConditions)
    .def("CreateConditions", &IsogeometricPostUtility_CreateConditionsByTriangulation<array_1d<double, 3>, Patch<3> >)
    .def("CreateConditions", &IsogeometricPostUtility_CreateConditionsByQuadrilateralization<Vector, Patch<3> >)
    .def("CreateConditions", &IsogeometricPostUtility_CreateConditionsByQuadrilateralization<array_1d<double, 3>, Patch<3> >)
    ;

    class_<BezierClassicalPostUtility, BezierClassicalPostUtility::Pointer, boost::noncopyable>("BezierClassicalPostUtility", init<ModelPart::Pointer>())
    .def("GenerateConditions", &BezierClassicalPostUtility_GenerateConditions)
    .def("GenerateConditionsWithNodalVariables", &BezierClassicalPostUtility_GenerateConditionsWithNodalVariables)
    .def("GenerateElements", &BezierClassicalPostUtility_GenerateElements)
    .def("GenerateElementsWithNodalVariables", &BezierClassicalPostUtility_GenerateElementsWithNodalVariables)
    .def("GenerateModelPart", &BezierClassicalPostUtility::GenerateModelPart)
    .def("GenerateModelPart2", &BezierClassicalPostUtility_GenerateModelPart2WithCondition)
    .def("GenerateModelPart2", &BezierClassicalPostUtility_GenerateModelPart2)
    .def("GenerateModelPart2AutoCollapse", &BezierClassicalPostUtility::GenerateModelPart2AutoCollapse)
    .def("TransferNodalResults", &BezierClassicalPostUtility::TransferNodalResults<Variable<double> >)
    .def("TransferNodalResults", &BezierClassicalPostUtility::TransferNodalResults<Variable<Vector> >)
    .def("TransferNodalResults", &BezierClassicalPostUtility::TransferNodalResults<Variable<array_1d<double, 3> > >)
    .def("TransferIntegrationPointResults", &BezierClassicalPostUtility::TransferIntegrationPointResults<Variable<double> >)
    .def("TransferIntegrationPointResults", &BezierClassicalPostUtility::TransferIntegrationPointResults<Variable<Vector> >)
    .def("SynchronizeActivation", &BezierClassicalPostUtility::SynchronizeActivation)
    .def("TransferElementalData", &BezierClassicalPostUtility::TransferElementalData<Variable<bool> >)
    .def("TransferConditionalData", &BezierClassicalPostUtility::TransferConditionalData<Variable<bool> >)
    .def("TransferVariablesToNodes", &BezierClassicalPostUtility::TransferVariablesToNodes<Variable<double> >)
    .def("TransferVariablesToNodes", &BezierClassicalPostUtility::TransferVariablesToNodes<Variable<Vector> >)
    .def("GlobalNodalRenumbering", &BezierClassicalPostUtility::GlobalNodalRenumbering)
    ;

    class_<BezierPostUtility, BezierPostUtility::Pointer, boost::noncopyable>("BezierPostUtility", init<>())
    .def("TransferNodalResults", &BezierPostUtility::TransferNodalResults<Variable<double> >)
    .def("TransferNodalResults", &BezierPostUtility::TransferNodalResults<Variable<Vector> >)
    .def("TransferNodalResults", &BezierPostUtility::TransferNodalResults<Variable<array_1d<double, 3> > >)
    .def("TransferIntegrationPointResults", &BezierPostUtility::TransferIntegrationPointResults<Variable<double> >)
    .def("TransferIntegrationPointResults", &BezierPostUtility::TransferIntegrationPointResults<Variable<Vector> >)
    .def("TransferIntegrationPointResults", &BezierPostUtility::TransferIntegrationPointResults<Variable<array_1d<double, 3>> >)
    .def("TransferVariablesToNodes", &BezierPostUtility_TransferVariablesToNodes_ModelPart<Variable<double> >)
    .def("TransferVariablesToNodes", &BezierPostUtility_TransferVariablesToNodes_ModelPart<Variable<Vector> >)
    .def("TransferVariablesToNodes", &BezierPostUtility_TransferVariablesToNodes_ModelPart<Variable<array_1d<double, 3>> >)
    .def("TransferVariablesToNodes", &BezierPostUtility_TransferVariablesToNodes_Elements<Variable<double> >)
    .def("TransferVariablesToNodes", &BezierPostUtility_TransferVariablesToNodes_Elements<Variable<Vector> >)
    .def("TransferVariablesToNodes", &BezierPostUtility_TransferVariablesToNodes_Elements<Variable<array_1d<double, 3>> >)
    ;

    #ifdef ISOGEOMETRIC_USE_HDF5
    class_<HDF5PostUtility, HDF5PostUtility::Pointer, boost::noncopyable>("HDF5PostUtility", init<const std::string>())
    .def(init<const std::string, const std::string>())
    .def("WriteNodes", &HDF5PostUtility::WriteNodes)
    .def("WriteNodalResults", &HDF5PostUtility::WriteNodalResults<double>)
    .def("WriteNodalResults", &HDF5PostUtility::WriteNodalResults<array_1d<double, 3> >)
    .def("WriteNodalResults", &HDF5PostUtility::WriteNodalResults<Vector>)
    .def("WriteElementalData", &HDF5PostUtility::WriteElementalData<bool>)
    .def("ReadNodalResults", &HDF5PostUtility::ReadNodalResults<double>)
    .def("ReadNodalResults", &HDF5PostUtility::ReadNodalResults<array_1d<double, 3> >)
    .def("ReadNodalResults", &HDF5PostUtility::ReadNodalResults<Vector>)
    .def("ReadElementalData", &HDF5PostUtility::ReadElementalData<bool>)
    ;
    #endif

    class_<IsogeometricTestUtils, IsogeometricTestUtils::Pointer, boost::noncopyable>("IsogeometricTestUtils", init<>())
    .def("Test1", &IsogeometricTestUtils::Test1)
    .def("Test2", &IsogeometricTestUtils::Test2)
    .def("ProbeGlobalCoordinates", &IsogeometricTestUtils_ProbeGlobalCoordinates1<Element>)
    .def("ProbeGlobalCoordinates", &IsogeometricTestUtils_ProbeGlobalCoordinates1<Condition>)
    .def("ProbeGlobalCoordinates", &IsogeometricTestUtils_ProbeGlobalCoordinates2<Element>)
    .def("ProbeGlobalCoordinates", &IsogeometricTestUtils_ProbeGlobalCoordinates2<Condition>)
    .def("ProbeGlobalCoordinates", &IsogeometricTestUtils_ProbeGlobalCoordinates3<Element>)
    .def("ProbeGlobalCoordinates", &IsogeometricTestUtils_ProbeGlobalCoordinates3<Condition>)
    .def("ProbeShapeFunctionValues", &IsogeometricTestUtils_ProbeShapeFunctionValues1<Element>)
    .def("ProbeShapeFunctionValues", &IsogeometricTestUtils_ProbeShapeFunctionValues1<Condition>)
    .def("ProbeShapeFunctionValues", &IsogeometricTestUtils_ProbeShapeFunctionValues2<Element>)
    .def("ProbeShapeFunctionValues", &IsogeometricTestUtils_ProbeShapeFunctionValues2<Condition>)
    .def("ProbeShapeFunctionValues", &IsogeometricTestUtils_ProbeShapeFunctionValues3<Element>)
    .def("ProbeShapeFunctionValues", &IsogeometricTestUtils_ProbeShapeFunctionValues3<Condition>)
    .def("ProbeShapeFunctionDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionDerivatives1<Element>)
    .def("ProbeShapeFunctionDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionDerivatives1<Condition>)
    .def("ProbeShapeFunctionDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionDerivatives2<Element>)
    .def("ProbeShapeFunctionDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionDerivatives2<Condition>)
    .def("ProbeShapeFunctionDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionDerivatives3<Element>)
    .def("ProbeShapeFunctionDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionDerivatives3<Condition>)
    .def("ProbeShapeFunctionSecondDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives1<Element>)
    .def("ProbeShapeFunctionSecondDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives1<Condition>)
    .def("ProbeShapeFunctionSecondDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives2<Element>)
    .def("ProbeShapeFunctionSecondDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives2<Condition>)
    .def("ProbeShapeFunctionSecondDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives3<Element>)
    .def("ProbeShapeFunctionSecondDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionSecondDerivatives3<Condition>)
    .def("ProbeShapeFunctionThirdDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives1<Element>)
    .def("ProbeShapeFunctionThirdDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives1<Condition>)
    .def("ProbeShapeFunctionThirdDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives2<Element>)
    .def("ProbeShapeFunctionThirdDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives2<Condition>)
    .def("ProbeShapeFunctionThirdDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives3<Element>)
    .def("ProbeShapeFunctionThirdDerivatives", &IsogeometricTestUtils_ProbeShapeFunctionThirdDerivatives3<Condition>)
    .def("ProbeJacobian", &IsogeometricTestUtils_ProbeJacobian1<Element>)
    .def("ProbeJacobian", &IsogeometricTestUtils_ProbeJacobian1<Condition>)
    .def("ProbeJacobian", &IsogeometricTestUtils_ProbeJacobian2<Element>)
    .def("ProbeJacobian", &IsogeometricTestUtils_ProbeJacobian2<Condition>)
    .def("ProbeJacobian", &IsogeometricTestUtils_ProbeJacobian3<Element>)
    .def("ProbeJacobian", &IsogeometricTestUtils_ProbeJacobian3<Condition>)
    .def("DumpNodalValues", &IsogeometricTestUtils::DumpNodalValues<double>)
    .def("DumpNodalValues", &IsogeometricTestUtils::DumpNodalValues<array_1d<double, 3> >)
    .def("DumpNodalValues", &IsogeometricTestUtils::DumpNodalValues<Vector>)
    ;

    class_<IsogeometricMergeUtility, IsogeometricMergeUtility::Pointer, boost::noncopyable>(
        "IsogeometricMergeUtility", init<>())
    .def("Add", &IsogeometricMergeUtility::Add)
    .def("Export", &IsogeometricMergeUtility::Export)
    .def("DumpNodalVariablesList", &IsogeometricMergeUtility::DumpNodalVariablesList)
    ;

    /////////////////////////////////////////////////////////////////
    ///////////////////////GISMO/////////////////////////////////////
    /////////////////////////////////////////////////////////////////

    #ifdef ISOGEOMETRIC_USE_GISMO
    class_<GismoMesh, GismoMesh::Pointer, boost::noncopyable>
    ("GismoMesh", init<std::string>())
    .def("SetEchoLevel", &GismoMesh::SetEchoLevel)
    .def("ReadMesh", &GismoMesh::ReadMesh)
    .def(self_ns::str(self))
    ;
    #endif

}

}  // namespace Python.

} // Namespace Kratos


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE matrix
#include <boost/test/unit_test.hpp>

#include "../include/matrix.hpp"

BOOST_AUTO_TEST_CASE(matrix_)
{
    auto m = matrix{{{ 1,0,0
                     , 0,1,0
                     , 0,0,1 }}};

    BOOST_CHECK_EQUAL(m.value(0,0), 1);
    BOOST_CHECK_EQUAL(m.value(0,1), 0);
    BOOST_CHECK_EQUAL(m.value(0,2), 0);

    BOOST_CHECK_EQUAL(m.value(1,0), 0);
    BOOST_CHECK_EQUAL(m.value(1,1), 1);
    BOOST_CHECK_EQUAL(m.value(1,2), 0);

    BOOST_CHECK_EQUAL(m.value(2,0), 0);
    BOOST_CHECK_EQUAL(m.value(2,1), 0);
    BOOST_CHECK_EQUAL(m.value(2,2), 1);
}

BOOST_AUTO_TEST_CASE(addition)
{
    auto m0 = matrix{{{ 1,2,3
                      , 4,5,6
                      , 7,8,9 }}};
    auto m1 = matrix{{{ 9,8,7
                      , 6,5,4
                      , 3,2,1 }}};

    auto exp = m0 + m1;
    matrix m = exp;

    for (int i = 0; i != 3; ++i)
    {
        for (int j = 0; j != 3; ++j)
        {
            BOOST_CHECK_EQUAL(exp.value(i,j), 10);
            BOOST_CHECK_EQUAL(m.value(i,j), 10);
        }
    }

    auto exp2 = m0 + m1;
    auto exp3 = exp + exp2;

    for (int i = 0; i != 3; ++i)
    {
        for (int j = 0; j != 3; ++j)
        {
            BOOST_CHECK_EQUAL(exp3.value(i,j), 20);
        }
    }
}


//==================================================================================================
/*!
  @file

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_REMQUO_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_REMQUO_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-arithmetic

    This function object  computes the remainder (rem) and a part of the quotient (quo) upon
    division of @c x by @c y. By design, the value of the remainder is the same as that
    computed by the @ref rem standard function. The value of the computed quotient has
    the sign of @c x/y and agrees with the actual quotient in at least the low
    order 3 bits.

    @par Header <boost/simd/function/remquo.hpp>

    @par semantic:

    If T is the common type to @c x and @c y

    @code
      std::pair< T, as_integer_t<T> > p = remquo(x, y);
    @endcode

    computes the two values.

    @par Note

      - This function mimics a standard C library one that was mainly written in its time to
        help computation of periodic trigonometric functions: three bits of @c quo allowing
        to know the 'octant'

      - This implementation differs from std::remquo as the quotient is not returned as a
        pointer, and his type is not int but the signed integer type associated to the floating
        one, to allow proper SIMD implementation.

      - also note that the double implementation of std::remquo is flawed in GNU C
        Library until version 2.21 (2.22 has been corrected).

    @par Example:

      @snippet remquo.cpp remquo

    @par Possible output:

      @snippet remquo.txt remquo

  **/
  std::pair<IEEEValue, as_integer_t<IEEEValue>> remquo(IEEEValue const& x, IEEEValue const& y);
} }
#endif

#include <boost/simd/function/scalar/remquo.hpp>
#include <boost/simd/function/simd/remquo.hpp>

#endif


/* Test program to test find functions of triagular matrices
 *
 * author: Gunter Winkler ( guwi17 at gmx dot de )
 */

// ublas headers

#include <boost/numeric/ublas/experimental/sparse_view.hpp>

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <boost/numeric/ublas/io.hpp>

#include <boost/numeric/ublas/traits/c_array.hpp>

// other boost headers

// headers for testcase

#define BOOST_TEST_MODULE SparseMatrixErasureTest
#include <boost/test/included/unit_test.hpp>

// standard and system headers

#include <iostream>
#include <string>

namespace ublas = boost::numeric::ublas;

    /*
      sparse input matrix:

      1 2 0 0
      0 3 9 0
      0 1 4 0
    */

    static const std::string inputMatrix = "[3,4]((1,2,0,0),(0,3,9,0),(0,1,4,0))\n";

    const unsigned int NNZ  = 6;
    const unsigned int IB   = 1;
    const double VA[]       = { 1.0, 2.0, 3.0, 9.0, 1.0, 4.0 };
    const unsigned int IA[] = { 1, 3, 5, 7 };
    const unsigned int JA[] = { 1, 2, 2, 3, 2, 3 };

BOOST_AUTO_TEST_CASE( test_construction_and_basic_operations )
{

    typedef ublas::matrix<double> DENSE_MATRIX;

    // prepare data

    DENSE_MATRIX A;

    std::istringstream iss(inputMatrix);
    iss >> A;

    std::cout << A << std::endl;

    std::cout << ( ublas::make_compressed_matrix_view<ublas::row_major,IB>(3,4,NNZ,IA,JA,VA) ) << std::endl;

    typedef ublas::compressed_matrix_view<ublas::row_major, IB, unsigned int [4], unsigned int [NNZ], double[NNZ]> COMPMATVIEW;

    COMPMATVIEW viewA(3,4,NNZ,IA,JA,VA);

    std::cout << viewA << std::endl;

}

BOOST_AUTO_TEST_CASE( test_construction_from_pointers )
{

    std::cout << ( ublas::make_compressed_matrix_view<ublas::column_major,IB>(4,3,NNZ
                                                                              , ublas::c_array_view<const unsigned int>(4,&(IA[0]))
                                                                              , ublas::c_array_view<const unsigned int>(6,&(JA[0]))
                                                                              , ublas::c_array_view<const double>(6,&(VA[0]))) ) << std::endl;

    unsigned int * ia = new unsigned int[4]();
    unsigned int * ja = new unsigned int[6]();
    double * va = new double[6]();

    std::copy(&(IA[0]),&(IA[4]),ia);
    std::copy(&(JA[0]),&(JA[6]),ja);
    std::copy(&(VA[0]),&(VA[6]),va);

    typedef ublas::compressed_matrix_view<ublas::column_major
      , IB
      , ublas::c_array_view<unsigned int>
      , ublas::c_array_view<unsigned int>
      , ublas::c_array_view<double> > COMPMATVIEW;

    COMPMATVIEW viewA(4,3,NNZ
                      , ublas::c_array_view<unsigned int>(4,ia)
                      , ublas::c_array_view<unsigned int>(6,ja)
                      , ublas::c_array_view<double>(6,va));

    std::cout << viewA << std::endl;

    delete[] va;
    delete[] ja;
    delete[] ia;

}


#include <deal.II/base/logstream.h>
#include <deal.II/base/parameter_handler.h>
#include <fstream>
#include <step_50.h>

using namespace dealii;
using namespace Step50;

template <int dim>
class Test_LaplaceProblem : public Step50::LaplaceProblem<dim>
{
  protected:
    using Step50::LaplaceProblem<dim>::read_lammps_input_file;
    using Step50::LaplaceProblem<dim>::refine_grid;
    using Step50::LaplaceProblem<dim>::setup_system;
    using Step50::LaplaceProblem<dim>::rhs_assembly_optimization;
    using Step50::LaplaceProblem<dim>::assemble_system;
    void charge_density_test(const std::vector<Point<dim> > & , double * ,
                             const std::map<typename parallel::distributed::Triangulation<dim>::cell_iterator, std::set<unsigned int> > &, bool & );

    const unsigned int degree;
    ParameterHandler &prm;
    unsigned int number_of_global_refinement , number_of_adaptive_refinement_cycles;
    double domain_size_left , domain_size_right;
    std::string Problemtype, PreconditionerType, LammpsInputFilename;
    double r_c, nonzero_density_radius_parameter;

  public:
    Test_LaplaceProblem (const unsigned int Degree , ParameterHandler &prm,
                         std::string &Problemtype, std::string &PreconditionerType, std::string &LammpsInputFile,
                         double &domain_size_left, double &domain_size_right, unsigned int &number_of_global_refinement, unsigned int &number_of_adaptive_refinement_cycles,
                         double &r_c, double &nonzero_density_radius_parameter) : Step50::LaplaceProblem<dim> (Degree , prm ,Problemtype, PreconditionerType, LammpsInputFile,
                                                                                domain_size_left, domain_size_right, number_of_global_refinement,
                                                                                number_of_adaptive_refinement_cycles, r_c, nonzero_density_radius_parameter),
        degree(Degree),
        prm(prm),
        number_of_global_refinement(number_of_global_refinement),
        number_of_adaptive_refinement_cycles(number_of_adaptive_refinement_cycles),
        domain_size_left(domain_size_left),
        domain_size_right(domain_size_right),
        Problemtype(Problemtype),
        PreconditionerType(PreconditionerType),
        LammpsInputFilename(LammpsInputFile),
        r_c(r_c),
        nonzero_density_radius_parameter(nonzero_density_radius_parameter)
    { }

    void run (bool &);
    ~Test_LaplaceProblem(){}

};

template class Test_LaplaceProblem<2>;
template class Test_LaplaceProblem<3>;

template <int dim>
void Test_LaplaceProblem<dim>::run(bool &flag_rhs_assembly)
{
    Timer timer;
    Step50::LaplaceProblem<dim>::flag_rhs_assembly = flag_rhs_assembly;
    Step50::LaplaceProblem<dim>::read_lammps_input_file(LammpsInputFilename);
    for (unsigned int cycle=0; cycle<number_of_adaptive_refinement_cycles; ++cycle)
        // first mesh size 4^2 = 16*16*16 and then 2 refinements
    {
        timer.start();

        Step50::LaplaceProblem<dim>::pcout << "Cycle " << cycle << ':' << std::endl;

        if (cycle == 0)
        {
            GridGenerator::hyper_cube (Step50::LaplaceProblem<dim>::triangulation,domain_size_left,domain_size_right);

            Step50::LaplaceProblem<dim>::triangulation.refine_global (number_of_global_refinement);  //eg. first mesh size 4^2 = 16*16*16
        }
        else
            Step50::LaplaceProblem<dim>::refine_grid ();

        Step50::LaplaceProblem<dim>::pcout << "   Number of active cells:       "<< Step50::LaplaceProblem<dim>::triangulation.n_global_active_cells() << std::endl;

        Step50::LaplaceProblem<dim>::setup_system ();

        Step50::LaplaceProblem<dim>::pcout << "   Number of degrees of freedom: " << Step50::LaplaceProblem<dim>::mg_dof_handler.n_dofs() << " (by level: ";
        for (unsigned int level=0; level<Step50::LaplaceProblem<dim>::triangulation.n_global_levels(); ++level)
            Step50::LaplaceProblem<dim>::pcout << Step50::LaplaceProblem<dim>::mg_dof_handler.n_dofs(level) << (level == Step50::LaplaceProblem<dim>::triangulation.n_global_levels()-1 ? ")" : ", ");
        Step50::LaplaceProblem<dim>::pcout << std::endl;

        if(flag_rhs_assembly != 0)
            Step50::LaplaceProblem<dim>::rhs_assembly_optimization(Step50::LaplaceProblem<dim>::atom_positions);

        Step50::LaplaceProblem<dim>::assemble_system (Step50::LaplaceProblem<dim>::atom_positions, Step50::LaplaceProblem<dim>::charges, Step50::LaplaceProblem<dim>::charges_list_for_each_cell
                                                      ,flag_rhs_assembly);
        charge_density_test(Step50::LaplaceProblem<dim>::atom_positions, Step50::LaplaceProblem<dim>::charges, Step50::LaplaceProblem<dim>::charges_list_for_each_cell
        ,flag_rhs_assembly);

        timer.stop();
        //std::cout << "   Elapsed CPU time: " << timer() << " seconds."<<std::endl;
        //std::cout << "   Elapsed wall time: " << timer.wall_time() << " seconds."<<std::endl;
        timer.reset();

        // Print the charges densities i.e. system rhs norms to compare with rhs optimization
        Step50::LaplaceProblem<dim>::pcout << "   L2 rhs norm " << std::setprecision(10) << std::scientific << Step50::LaplaceProblem<dim>::system_rhs.l2_norm() << std::endl;
        Step50::LaplaceProblem<dim>::pcout << "   LInfinity rhs norm " << std::setprecision(10) << std::scientific << Step50::LaplaceProblem<dim>::system_rhs.linfty_norm() << std::endl;
    }
}

//Integrate for each cell the charge density for associated atom list
//Add all cell contribution charge sensities to check if some error due to rhs assembly optimization
//Ideally we consider Charge neutral system
template <int dim>
void Test_LaplaceProblem<dim>::charge_density_test(const std::vector<Point<dim> > & atom_positions, double * charges,
                                                   const std::map<typename parallel::distributed::Triangulation<dim>::cell_iterator, std::set<unsigned int> > &charges_list_for_each_cell
                                                   , bool & flag_rhs_assembly)
{
    const QGauss<dim>  quadrature_formula(degree+1);

    FEValues<dim> fe_values (Step50::LaplaceProblem<dim>::fe, quadrature_formula,
                             update_values    |  update_gradients |
                             update_quadrature_points  |  update_JxW_values);

    const unsigned int   dofs_per_cell = Step50::LaplaceProblem<dim>::fe.dofs_per_cell;
    const unsigned int   n_q_points    = quadrature_formula.size();

    Vector<double>       cell_rhs (dofs_per_cell);

    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

    std::vector<double>    coefficient_values (n_q_points);
    std::vector<double>    density_values (n_q_points);

    double r = 0.0, r_squared = 0.0;
    const double r_c_squared_inverse = 1.0 / (r_c * r_c);

    const double constant_value = 4.0 * (numbers::PI)  / (std::pow(r_c, 3) * std::pow(numbers::PI, 1.5));

    std::set<unsigned int> set_atom_indices;
    typedef LA::MPI::Vector vector_t;
    vector_t total_charge_densities;
    total_charge_densities.reinit(Step50::LaplaceProblem<dim>::mg_dof_handler.locally_owned_dofs(), MPI_COMM_WORLD);

    typename DoFHandler<dim>::active_cell_iterator
    cell = Step50::LaplaceProblem<dim>::mg_dof_handler.begin_active(),
    endc = Step50::LaplaceProblem<dim>::mg_dof_handler.end();
    for (; cell!=endc; ++cell)
        if (cell->is_locally_owned())
        {
            cell_rhs = 0;

            fe_values.reinit (cell);

            Step50::LaplaceProblem<dim>::coeff_func->value_list (fe_values.get_quadrature_points(),
                                    coefficient_values);

                    const std::vector<Point<dim> > & quadrature_points = fe_values.get_quadrature_points();
                    set_atom_indices =  charges_list_for_each_cell.at(cell);

                    for(unsigned int q_points = 0; q_points < n_q_points; ++q_points)
                        {
                            density_values[q_points] = 0.0;
                                {
                                //With rhs assembly optimization
                                //If flag != 0 iterate only over the neighouring atoms and apply rhs optimization
                                if(flag_rhs_assembly != 0)
                                    {
                                            for(const auto & a : set_atom_indices)
                                            {
                                                //std::cout<< *iter << " ";
                                                r = 0.0;
                                                r_squared = 0.0;

                                                const Point<dim> Xi = atom_positions[a];
                                                r = Xi.distance(quadrature_points[q_points]);
                                                r_squared = r * r;

                                                density_values[q_points] +=  constant_value *
                                                                             exp(-r_squared * r_c_squared_inverse) *
                                                                             charges[a];
                                            }
                                    }
                                //Without optimization
                                //If flag == 0 iterate over all the atoms in the domain, i.e. do not optimize the assembly
                                if(flag_rhs_assembly == 0)
                                    {
                                        for(unsigned int k = 0; k < atom_positions.size(); ++k)
                                            {
                                                r = 0.0;
                                                r_squared = 0.0;

                                                const Point<dim> Xi = atom_positions[k];
                                                r = Xi.distance(quadrature_points[q_points]);
                                                r_squared = r * r;

                                                density_values[q_points] +=  constant_value *
                                                        exp(-r_squared * r_c_squared_inverse) *
                                                        charges[k];
                                            }
                                    }
                                }
                        }
                    set_atom_indices.clear();

            for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
                for (unsigned int i=0; i<dofs_per_cell; ++i)
                {
                        cell_rhs(i) += (density_values[q_point]* fe_values.JxW(q_point));
                }

            cell->get_dof_indices (local_dof_indices);
            Step50::LaplaceProblem<dim>::constraints.distribute_local_to_global (cell_rhs, local_dof_indices, total_charge_densities);
        }

    total_charge_densities.compress(VectorOperation::add);
    Step50::LaplaceProblem<dim>::pcout << "Total charge density over the domain after rhs assembly optimization " << std::setprecision(10) << std::scientific
                                        << total_charge_densities.l2_norm() << std::endl;
}

void check ()
{
  ParameterHandler prm;
  ParameterReader param(prm);
  param.declare_parameters();

  //Here create dynamically the list of all parameters required from the prm file for test purpose
  std::ostringstream oss;
  oss << "subsection Geometry" << std::endl
         <<"    set Number of global refinement = 4 "<< std::endl
        << "    set Domain limit left = -2.5" << std::endl
        << "    set Domain limit right = 2.5" << std::endl
        <<"end" <<std::endl
       <<"subsection Misc"<<std::endl
        << "    set Number of Adaptive Refinement = 1" << std::endl
        << "    set smoothing length = 0.5" << std::endl
//        << "    set Nonzero Density radius parameter around each charge = 3" << std::endl
        <<"end"<<std::endl
        << "    set Polynomial degree = 1" << std::endl
        <<"subsection Solver input data"<<std::endl
        << "    set Preconditioner = GMG" << std::endl
        <<"end"<<std::endl
       <<"subsection Problem Selection"<<std::endl
        << "    set Problem = GaussianCharges" << std::endl
        << "    set Dimension = 3" << std::endl
        <<"end"<<std::endl
       <<"subsection Lammps data"<<std::endl
      << "  set Lammps input file = " << SOURCE_DIR << "/atom_2.data" << std::endl
      <<"end"<<std::endl;

  prm.parse_input_from_string(oss.str().c_str());

  prm.enter_subsection ("Geometry");
  unsigned int number_of_global_refinement =prm.get_integer("Number of global refinement");
  double domain_size_left     = prm.get_double ("Domain limit left");
  double domain_size_right     = prm.get_double ("Domain limit right");
  prm.leave_subsection ();

  prm.enter_subsection ("Misc");
  unsigned int number_of_adaptive_refinement_cycles      = prm.get_integer ("Number of Adaptive Refinement");
  double r_c = prm.get_double ("smoothing length");
  double nonzero_density_radius_parameter ;//= prm.get_double("Nonzero Density radius parameter around each charge");
  prm.leave_subsection ();

  const unsigned int Degree = prm.get_integer("Polynomial degree");

  prm.enter_subsection("Solver input data");
  std::string PreconditionerType = (prm.get("Preconditioner"));
  prm.leave_subsection();

  prm.enter_subsection("Problem Selection");
  std::string Problemtype= (prm.get("Problem"));
  const unsigned int d = prm.get_integer("Dimension");
  prm.leave_subsection();

  prm.enter_subsection("Lammps data");
  std::string LammpsInputFile = (prm.get("Lammps input file"));
  prm.leave_subsection();

  bool flag_rhs_assembly;
  std::vector<double> r_c_variation {2.0,2.25,2.5,2.75,3.0,3.25,3.5,3.75,4.0,4.25,4.5,4.75,5.0,5.25,5.5,5.75,6.0};
  for(const auto & i : r_c_variation)
      {
          nonzero_density_radius_parameter = i;
          if(Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
              std::cout<<"cutoff radius: "<<std::fixed<<std::setprecision(2)<<nonzero_density_radius_parameter<<std::endl;

          if (d == 2)
          {
                Test_LaplaceProblem<2> test_laplace_problem(Degree , prm ,Problemtype, PreconditionerType, LammpsInputFile, domain_size_left, domain_size_right,
                                                            number_of_global_refinement, number_of_adaptive_refinement_cycles, r_c, nonzero_density_radius_parameter);
//                test_laplace_problem.run();
                flag_rhs_assembly = 0;
                std::cout << "Without rhs assembly optimization" <<std::endl;
                test_laplace_problem.run(flag_rhs_assembly);
//                test_laplace_problem.~Test_LaplaceProblem();

                Test_LaplaceProblem<2> test_laplace_problem_with_rhs_optimiation(Degree , prm ,Problemtype, PreconditionerType, LammpsInputFile, domain_size_left, domain_size_right,
                                                            number_of_global_refinement, number_of_adaptive_refinement_cycles, r_c, nonzero_density_radius_parameter);
                flag_rhs_assembly = 1;
                std::cout << "Rhs assembly optimization ENABLED" <<std::endl;
                test_laplace_problem_with_rhs_optimiation.run(flag_rhs_assembly);

          }
          else if (d == 3)
          {
                  Test_LaplaceProblem<3> test_laplace_problem(Degree , prm ,Problemtype, PreconditionerType, LammpsInputFile, domain_size_left, domain_size_right,
                                                              number_of_global_refinement, number_of_adaptive_refinement_cycles, r_c, nonzero_density_radius_parameter);
//                  test_laplace_problem.run();
                  flag_rhs_assembly = 0;
                  std::cout << "Without rhs assembly optimization" <<std::endl;
                  test_laplace_problem.run(flag_rhs_assembly);
//                  test_laplace_problem.~Test_LaplaceProblem();

                  Test_LaplaceProblem<3> test_laplace_problem_with_rhs_optimiation(Degree , prm ,Problemtype, PreconditionerType, LammpsInputFile, domain_size_left, domain_size_right,
                                                              number_of_global_refinement, number_of_adaptive_refinement_cycles, r_c, nonzero_density_radius_parameter);
                  flag_rhs_assembly = 1;
                  std::cout << "Rhs assembly optimization ENABLED" <<std::endl;
                  test_laplace_problem_with_rhs_optimiation.run(flag_rhs_assembly);

          }
          else if (d != 2 && d != 3)
          {
              AssertThrow(false, ExcMessage("Only 2d and 3d dimensions are supported."));
          }
      }

}

int main (int argc, char *argv[])
{

  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);

  check ();

  return 0;
}


#include <big_random.h>
#include <big_types.h>
#include <big_generate_random.h>
#include <big_is_orthonormal.h>
#include <big_gram_schmidt.h>

#include <big_generate_PD.h>
#include <big_generate_PSD.h>
#include <big_is_symmetric.h>

#include <big_matlab_write.h>

#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/test_tools.hpp>

BOOST_AUTO_TEST_SUITE(big_modified_gram_schmidt);

BOOST_AUTO_TEST_CASE(random_test_case)
{
  typedef ublas::compressed_matrix<double> matrix_type;

  matrix_type A;

  for(size_t tst=0;tst<5;++tst)
  {
    big::generate_random(10, 10, A);

    bool not_ortho = !big::is_orthonormal( A );
    BOOST_CHECK(not_ortho);

    big::gram_schmidt(A);

    bool did_it = big::is_orthonormal( A );
    BOOST_CHECK(did_it);
  }

  {
    using namespace big;
    big::fast_generate_PD( 10, A );

    bool is_ok = big::is_symmetric( A );
    BOOST_CHECK(is_ok);

    big::generate_PSD( 10, A, 0.5 );

    bool is_also_ok = big::is_symmetric( A );
    BOOST_CHECK(is_also_ok);
  }
}

BOOST_AUTO_TEST_SUITE_END();


#include "FiniteElementTetrahedronMesh.h"

#include <Eigen/Dense>

#include <map>

template<class T>
struct TetrahedralMesh : public FiniteElementTetrahedronMesh<T>
{
	using Base = FiniteElementTetrahedronMesh<T>;

	// from FiniteElementTetrahedonMesh
	using Base::m_meshElements;
	using Base::m_particleX;
	using Base::initializeUSD;
	using Base::initializeTopology;
	using Base::initializeParticles;
	using Vector3 = typename Base::Vector3;

	std::array<int, 3> m_cellSize; // dimensions in grid cells
	int m_radius; // radius of sphere in grid cells
	T m_gridDX;

	std::vector<std::array<int, 3>> m_activeCells; // Marks the "active" cells in the lattice
	std::map<std::array<int, 3>, int> m_activeNodes; // Maps the "active" nodes to their particle index

	std::vector<Vector3> m_particleUndeformedX;
	std::vector<int> m_leftSquish;
	std::vector<int> m_rightSquish;

	TetrahedralMesh()
		:Base(1.e2, .2, 5., .05)
	{
	}

	void initialize()
	{
		initializeUSD("tetrahedral.usda");

		// Activate cells within a sphere of radius m_radius (in cells)

		for (int cell_i = 0; cell_i < m_cellSize[0]; cell_i++)
			for (int cell_j = 0; cell_j < m_cellSize[1]; cell_j++)
				for (int cell_k = 0; cell_k < m_cellSize[1]; cell_k++) {

					int r = (cell_i - m_cellSize[0] / 2) * (cell_i - m_cellSize[0] / 2) +
						(cell_j - m_cellSize[1] / 2) * (cell_j - m_cellSize[1] / 2) +
						(cell_k - m_cellSize[2] / 2) * (cell_k - m_cellSize[2] / 2);

					if (r <= m_radius * m_radius)
						m_activeCells.push_back(std::array<int, 3>{cell_i, cell_j, cell_k});

				}

		std::cout << "Created a model including " << m_activeCells.size() << " lattice cells" << std::endl;

		// Create (uniquely numbered) particles at the node corners of active cells

		for (const auto& cell : m_activeCells) {
			std::array<int, 3> node;
			for (node[0] = cell[0]; node[0] <= cell[0] + 1; node[0]++)
				for (node[1] = cell[1]; node[1] <= cell[1] + 1; node[1]++)
					for (node[2] = cell[2]; node[2] <= cell[2] + 1; node[2]++) {
						auto search = m_activeNodes.find(node);
						if (search == m_activeNodes.end()) { // Particle not yet created at this lattice node location -> make one
							m_activeNodes.insert({ node, m_particleX.size() });
							m_particleX.emplace_back(m_gridDX * T(node[0]), m_gridDX * T(node[1]), m_gridDX * T(node[2]));
							m_particleUndeformedX.emplace_back(m_gridDX * T(node[0]), m_gridDX * T(node[1]), m_gridDX * T(node[2]));
						}
					}
		}
		std::cout << "Model contains " << m_particleX.size() << " particles" << std::endl;

		// Make tetrahedra out of all active cells (6 tetrahedra per cell)

		for (const auto& cell : m_activeCells) {
			int vertexIndices[2][2][2];
			for (int i = 0; i <= 1; i++)
				for (int j = 0; j <= 1; j++)
					for (int k = 0; k <= 1; k++) {
						std::array<int, 3> node{ cell[0] + i, cell[1] + j, cell[2] + k };
						auto search = m_activeNodes.find(node);
						if (search != m_activeNodes.end())
							vertexIndices[i][j][k] = search->second;
						else
							throw std::logic_error("particle at cell vertex not found");
					}

			m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][0][0], vertexIndices[1][1][0], vertexIndices[1][1][1]});
			m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][0][0], vertexIndices[1][1][1], vertexIndices[1][0][1]});
			m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][0][1], vertexIndices[1][1][1], vertexIndices[0][0][1]});
			m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][1][1], vertexIndices[0][1][1], vertexIndices[0][0][1]});
			m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][1][1], vertexIndices[0][1][0], vertexIndices[0][1][1]});
			m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][1][0], vertexIndices[0][1][0], vertexIndices[1][1][1]});
		}

		// Perform the USD-specific initialization of topology & particles
		// (this will also create a boundary *surface* to visualuze

		initializeTopology();
		initializeParticles();
		initializeUndeformedConfiguration();

		// Check particle indexing in mesh

		for (const auto& element : m_meshElements)
			for (const auto vertex : element)
				if (vertex < 0 || vertex >= m_particleX.size())
					throw std::logic_error("mismatch between mesh vertex and particle array");

		for (int e = 0; e < m_particleX.size(); e++)
		{
			float leftThreshold = 1.3 - m_gridDX * (m_radius - 1);
			float rightThreshold = 1.25 + m_gridDX * (m_radius - 1);
			// Left side
			if (m_particleUndeformedX[e][0] < leftThreshold) {
				std::cout << "Particles...  " << m_particleUndeformedX[e][0] << std::endl;
				m_leftSquish.push_back(e);
			}
			// Right side
			if (m_particleUndeformedX[e][0] > rightThreshold) {
				std::cout << "XXXParticles...  " << m_particleUndeformedX[e][0] << std::endl;
				m_rightSquish.push_back(e);
			}
		}
	}

	void clearConstrainedParticles(std::vector<Vector3>& x) override
	{
		for (const auto v : m_leftSquish)
			x[v] = Vector3::Zero();
		for (const auto v : m_rightSquish)
			x[v] = Vector3::Zero();
	}

	void setBoundaryConditions() override
	{
		std::cout << "Setting boundary conditions" << std::endl;
		T effectiveTime = std::min<T>(m_stepEndTime, 1.5);

		for (const auto v : m_leftSquish) {
			m_particleX[v] = m_particleUndeformedX[v] + effectiveTime * Vector3(-.5, .5, 0);
		}
		for (const auto v : m_rightSquish) {
			m_particleX[v] = m_particleUndeformedX[v] + effectiveTime * Vector3(.5, -.5, 0);
		}

		std::cout << "Boundary conditions set" << std::endl;
	}
};

int main(int argc, char *argv[])
{
	TetrahedralMesh<float> simulationMesh;
	simulationMesh.m_cellSize = { 50, 50, 50 };
	simulationMesh.m_radius = 20;
	simulationMesh.m_gridDX = 0.05;
	simulationMesh.m_nFrames = 75;
	simulationMesh.m_subSteps = 1;
	simulationMesh.m_frameDt = 0.02;

	// Initialize the simulation example
	simulationMesh.initialize();

	// Output the initial shape of the mesh
	simulationMesh.writeFrame(0);

	for (int frame = 1; frame <= simulationMesh.m_nFrames; frame++) {
		simulationMesh.simulateFrame(frame);
		simulationMesh.writeFrame(frame);
	}

	// Write the entire timeline to USD
	simulationMesh.writeUSD();

	return 0;
}