//把kitti的groundtru格式类型的pose(时间戳 4X3的转移矩阵[去掉了0 0 0 1]) 转化为 Tum数据集类型的数据( 'timestamp tx ty tz qx qy qz qw' )
//在转化的过程中经过一个对pose进行的一个变换transpose*pose*transposeInv 保存 , 详情见 int loadKittiPoses(const string &  file_name, vector<Eigen::Matrix4d> & poses, Eigen::Matrix4d &transpose);

#include <iostream>
#include <iomanip> //要用到格式控制符
#include <fstream>
#include <boost/program_options.hpp>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <Eigen/Geometry>
#include <Eigen/Core>
#include <boost/format.hpp>  // for formating strings

#include <vector>
#include <string>

#include <sstream>
#include <boost/filesystem.hpp>
#include <algorithm>
#include <iterator>
#include <math.h>

using namespace std;

int loadKittiPoses(const string & file_name, vector<Eigen::Matrix4d> &poses) ;
int loadKittiPoses(const string &  file_name, vector<Eigen::Matrix4d> & poses, Eigen::Matrix4d &transpose);
int loadKittiTimes( const string & file_name,  vector<double> & timestamps);
int convert(  vector<Eigen::Matrix4d> & poses,  vector<double> & timestamps,  const string & filename);

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

    if (argc != 4)
    {
        //example: ./project    kitti类型的pose.txt   kitti时间戳文件夹   要输出的文件名
        cerr << endl << "Usage: ./project    kittiPoseXX.txt     kittiTimeXX.txt    tumOutxx.txt " << endl;
        return 1;
    }

    string kittipath =  std::string(argv[1]);
    string kittTimepath =  std::string(argv[2]);
    string tumFileName = std::string(argv[3]);
    cout << kittipath << endl;
    cout << kittTimepath << endl;
    cout << tumFileName << endl;

    vector<Eigen::Matrix4d> poses;         // 相机位姿
    vector<double>  timestamps;// 时间戳

     Eigen::Matrix4d T_rectC2_to_rectC0;
     T_rectC2_to_rectC0 << 1.0000, 0.0000,   -0.0000,   -0.0595,
             0.0000,    1.0000,    0.0000,    0.0015,
             -0.0000,   -0.0000,    1.0000,   -0.0038,
             0,         0,         0,    1.0000; //00-02

//     T_rectC2_to_rectC0 << 1.0000, 0.0000,   -0.0000,   -0.0596,
//             0.0000,    1.0000,    0.0000,    0.0006,
//             -0.0000,   -0.0000,    1.0000,   -0.0027,
//             0,         0,         0,    1.0000; //03

//     T_rectC2_to_rectC0 << 1.0000, 0.0000,   -0.0000,   -0.0602,
//             0.0000,    1.0000,    0.0000,    0.0021,
//             -0.0000,   -0.0000,    1.0000,   -0.0062,
//             0,         0,         0,    1.0000;//04-10

    //load  poses
    if (loadKittiPoses(kittipath,  poses, T_rectC2_to_rectC0) == 0 )
    {
        cerr << "cannot find pose file" << endl;
        return 0;
    }
    //load  poses
    if (loadKittiTimes(kittTimepath,  timestamps) == 0 )
    {
        cerr << "cannot find time file" << endl;
        return 0;
    }

    if ( convert( poses, timestamps, tumFileName) == 0)
    {
        cerr << "convert failed!!!" << endl;
        return 0;
    }

    cout << "It is done!------------------------------------------------" << endl;

    return 1;
}

int loadKittiPoses(const string &  file_name, vector<Eigen::Matrix4d> & poses)
{
    FILE *fp = fopen(file_name.c_str(), "r");
    if (!fp)
        return  0;
    while (!feof(fp)) {
        double P[3] [4];
        if (fscanf(fp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
                   &P[0][0], &P[0][1], &P[0][2], &P[0][3],
                   &P[1][0], &P[1][1], &P[1][2], &P[1][3],
                   &P[2][0], &P[2][1], &P[2][2], &P[2][3] ) == 12) {
            Eigen::Matrix4d T;
            T << P[0][0], P[0][1], P[0][2], P[0][3],
            P[1][0], P[1][1], P[1][2], P[1][3],
            P[2][0], P[2][1], P[2][2], P[2][3],
            0, 0, 0, 1;
            poses.push_back(T);
        }
    }
    fclose(fp);
    return 1;

}

//保存transpose*T*transposeInv到poses
int loadKittiPoses(const string &  file_name, vector<Eigen::Matrix4d> & poses, Eigen::Matrix4d &transpose)
{
 Eigen::Matrix4d transposeInv=transpose.inverse();
	cout<<"transpose: "<<transpose<<endl;
cout<<"transposeInv: "<<transposeInv<<endl;
    FILE *fp = fopen(file_name.c_str(), "r");
    if (!fp)
        return  0;
    while (!feof(fp)) {
        double P[3] [4];
        if (fscanf(fp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
                   &P[0][0], &P[0][1], &P[0][2], &P[0][3],
                   &P[1][0], &P[1][1], &P[1][2], &P[1][3],
                   &P[2][0], &P[2][1], &P[2][2], &P[2][3] ) == 12) {
            Eigen::Matrix4d T;
            T << P[0][0], P[0][1], P[0][2], P[0][3],
            P[1][0], P[1][1], P[1][2], P[1][3],
            P[2][0], P[2][1], P[2][2], P[2][3],
            0, 0, 0, 1;
            poses.push_back(transpose*T*transposeInv);
        }
    }
    fclose(fp);
    return 1;

}

int loadKittiTimes( const string & file_name,  vector<double> & timestamps)
{
    FILE *fp = fopen(file_name.c_str(), "r");
    if (!fp)
        return  0;
    while (!feof(fp)) {
        double timestamp;
        if (fscanf(fp, "%lf ", &timestamp ) == 1) {
            timestamps.push_back(timestamp);
        }
    }
    fclose(fp);
    return 1;

}

int convert(  vector<Eigen::Matrix4d> & poses,  vector<double> & timestamps,  const string & filename)
{
    cout << endl << "Saving camera pose to " << filename << " ..." << endl;
    ofstream fileout;
    fileout.open(filename.c_str(),std::ios::out);
    fileout << "#Format: timestamp tx ty tz qx qy qz qw" << endl ;

    vector<double>::iterator itTimestamp = timestamps.begin();
    if (poses.size() != timestamps.size())
    {
        cerr << "!!!-----------poses.size() != timestamps.size()----------!!!" << endl;
        //return 0;
    }

//NOTE 这里以pose的size()为准
    for (vector<Eigen::Matrix4d>::iterator it = poses.begin(); it != poses.end(); it++, itTimestamp++)
    {
        Eigen::Matrix4d tmpPose = *it;
        Eigen::Matrix3d tmpRotationMatrix = tmpPose.block(0, 0, 3, 3);
        Eigen::Quaterniond q = Eigen::Quaterniond ( tmpRotationMatrix ); //// 请注意四元数 的顺序是(x,y,z,w), w 为实部，前三者为虚部
        //cout<< q.x() << " " << q.y() << " " << q.z() << " " << q.w() << endl;
        fileout << setprecision(6) << *itTimestamp << " " <<  setprecision(9) <<  tmpPose(0, 3) << " " << tmpPose(1, 3) << " " << tmpPose(2, 3) << " " << q.x() << " " << q.y() << " " << q.z() << " " << q.w() << endl;

    }

    fileout.close();
    cout << endl << "trajectory saved!" << endl;
    return 1;

}


/*
Compute probability & bayesian evidence that a Pfam or TIGR protein domain is near
their respective average tri-nucleotide or amino acid distribution.

Conversely, the same quantities an be computed for domains consistently far from the mean.
*/
#include <iostream>
#include <string>
#include <algorithm>
#include <boost/program_options.hpp>
#include "./task/domain_probability_task.cpp"
#include "./task/context.cpp"

using namespace std;
namespace po = boost::program_options;

int main(int ac, char* av[]) {
	try {
		po::options_description desc(
			"Compute probability & bayesian evidence that a Pfam or TIGR "
            "protein domain is near (or far) from their respective "
            "average tri-nucleotide or amino acid distribution"
		);
		desc.add_options()
            (
            	"help,h",
            	"Print help message"
            )
            (
                "kind,k",
                po::value<string>()->default_value(""),
                "One of \"tri-nucleotide\" or \"amino-acid\""
            )
            (
                "query,q",
                po::value<string>()->default_value(""),
                "Protein domain source = Pfam or TIGR (case insensitive)"
            )
            (
                "tail,d",
                po::value<string>()->default_value("left"),
                "Tail of the distribution of distances to tri-nucleotide mean on which to focus: "
                "left (closest to min distance 0) or right (closest to max distance 1)"
            )
            (
            	"n_threads,t",
            	po::value<int>()->default_value(4),
            	"Number of threads to use."
            )
            (
                "config_file,c",
                po::value<string>()->default_value(""),
                "Use JSON config file instead of command line parameters"
            )
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(ac, av, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            cerr << desc << endl;
            return 0;
        }

        string config_file_path = vm["config_file"].as<string>();
        if (!config_file_path.empty()) {
            cerr << "Using config file: " << config_file_path << endl;
            DomainProbabilityContext ctx = parse_domain_probability_context(config_file_path);
            compute_domain_probabilities(ctx);

        } else {
            string kind = vm["kind"].as<string>();
            if (kind == "tri-nucleotide" || kind == "amino-acid") {
                cerr << "Kind: " << kind << endl;
            } else if (kind.empty()) {
                cerr << "Error: parameter \"kind\" not set.";
                cerr << " See --help for usage." << endl;
                return 1;
            } else {
                cerr << "Error: unknown value for parameter \"kind\": \"" << kind;
                cerr << "\". See --help for usage." << endl;
                return 1;
            }

            auto query = vm["query"].as<string>();
            transform(query.begin(), query.end(), query.begin(), ::tolower);

            cerr << "Query: " << query << endl;
            if (query != "pfam" && query != "tigr") {
                cerr << "Error: --query must be one of pfam, tigr" << endl;
                cerr << desc << endl;
                return 1;
            }

            auto tail = vm["tail"].as<string>();
            transform(tail.begin(), tail.end(), tail.begin(), ::tolower);
            cerr << "Tail: " << tail << endl;
            if (tail != "left" && tail != "right") {
                cerr << "Error: --tail must be one of left, right" << endl;
                cerr << desc << endl;
                return 1;
            }

            const int n_threads = vm["n_threads"].as<int>();
            cerr << "Threads: " << n_threads << endl;

            DomainProbabilityContext ctx = get_context_from_parameters(
                kind,
                query,
                tail,
                n_threads
            );
            compute_domain_probabilities(ctx);
        }
	}
	catch (exception& e) {
		cerr << "Exception raised: " << e.what() << endl;
		return 1;
	}
	catch (...) {
		cerr << "Unknown exception raised" << endl;
		return 1;
	}
	return 0;
}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2019 Tinko Bartels, Berlin, Germany.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_GEOMETRIES_TRIANGULATION_HPP
#define BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_GEOMETRIES_TRIANGULATION_HPP

#include <vector>

#include <boost/range.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/iterator/indirect_iterator.hpp>

#include <boost/geometry/strategies/cartesian/side_by_triangle.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/views/detail/points_view.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/geometries/concepts/point_concept.hpp>

namespace boost { namespace geometry
{
template<typename Triangulation>
struct face_range_type {};

template<typename Triangulation>
typename face_range_type<Triangulation>::type
    face_range(Triangulation const& t);

template<typename Triangulation>
struct triangulation_face {};

namespace model
{

template
<
    typename Point,
    bool ClockWise = true,
    template<typename, typename> class VertexContainer = std::vector,
    template<typename, typename> class FaceContainer = std::vector,
    template<typename> class VertexAllocator = std::allocator,
    template<typename> class FaceAllocator = std::allocator
>
class triangulation;

template<typename Triangulation>
struct vertex_ref
{
    typename Triangulation::point_type m_p;
    typename Triangulation::face_iterator m_f;
};

template<typename Triangulation>
struct face_ref
{
    typedef typename Triangulation::point_type point_type;
    typedef typename Triangulation::vertex_iterator vertex_iterator;
    typedef typename Triangulation::face_iterator face_iterator;
    typedef std::array<vertex_iterator, 3> vertex_container;
    vertex_container m_v;
    std::array<face_iterator, 3> m_f;
    std::array<unsigned short, 3> m_o;
public:
    typedef boost::indirect_iterator<typename vertex_container::const_iterator>
        const_iterator;
    typedef boost::indirect_iterator<typename vertex_container::iterator>
        iterator;

    const_iterator begin() const { return const_iterator(m_v.begin()); }
    const_iterator end() const { return const_iterator(m_v.end()); }
};

template
<
    typename Value,
    template<typename, typename> class Container,
    template<typename> class Allocator
>
struct reserve_if_vector{
    static void apply(Container<Value, Allocator<Value>>& c, std::size_t n) {}
};

template<typename Value, template<typename> class Allocator>
struct reserve_if_vector<Value, std::vector, Allocator>{
    static void apply(std::vector<Value, Allocator<Value>>& c,
                      std::size_t n) { c.reserve(n);}
};

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator
>
class triangulation
{
private:
    BOOST_CONCEPT_ASSERT( (concepts::Point<Point>) );
public:
    typedef Point point_type;
    typedef face_ref<triangulation> face_type;
    typedef vertex_ref<triangulation> vertex_type;
    typedef unsigned short face_vertex_index;
    typedef VertexContainer<vertex_type, VertexAllocator<vertex_type>>
        vertex_container;
    typedef FaceContainer<face_type, FaceAllocator<face_type>> face_container;
    typedef typename face_container::iterator face_iterator;
    typedef typename face_container::const_iterator const_face_iterator;
    typedef typename vertex_container::iterator vertex_iterator;
    typedef typename vertex_container::const_iterator const_vertex_iterator;
    typedef typename coordinate_type<Point>::type coordinate_type;
    typedef typename model::segment<Point> segment_type;

    static const order_selector point_order =
        ClockWise ? clockwise : counterclockwise;
    struct halfedge_index
    {
        halfedge_index(face_iterator f, face_vertex_index v):m_f(f), m_v(v) {}
        face_iterator m_f;
        face_vertex_index m_v;
    };

    struct fulledge_index
    {
        fulledge_index(face_iterator f1,
                       face_vertex_index v1,
                       face_iterator f2,
                       face_vertex_index v2)
            : m_f1(f1),
              m_f2(f2),
              m_v1(v1),
              m_v2(v2) {}
        fulledge_index(halfedge_index e)
            : m_f1(e.m_f),
              m_f2(e.m_f->m_f[ e.m_v ]),
              m_v1(e.m_v),
              m_v2(e.m_f->m_o[ e.m_v ]) {}
        face_iterator m_f1, m_f2;
        face_vertex_index m_v1, m_v2;
    };

    static face_iterator invalid() { return face_iterator(); }
    triangulation(std::size_t points = 3)
    {
        reserve_if_vector<vertex_type, VertexContainer, VertexAllocator>::
            apply(m_vertices, points);
        reserve_if_vector<face_type, FaceContainer, FaceAllocator>::
            apply(m_faces, 2 * points - 5);
    }

    triangulation(std::size_t points, std::size_t faces)
    {
        reserve_if_vector<vertex_type, VertexContainer, VertexAllocator>::
            apply(m_vertices, points);
        reserve_if_vector<face_type, FaceContainer, FaceAllocator>::
            apply(m_faces, faces);
    }

    template <typename InputIt>
    triangulation(InputIt begin, InputIt end)
    {
        m_vertices.assign(begin, end);
        reserve_if_vector<face_type, FaceContainer, FaceAllocator>
            ::apply(m_faces, 2 * m_vertices.size() - 5);
    }

    typename vertex_container::iterator vertices_begin()
    {
        return m_vertices.begin();
    }

    typename vertex_container::const_iterator vertices_begin() const
    {
        return m_vertices.cbegin();
    }

    typename vertex_container::iterator vertices_end()
    {
        return m_vertices.end();
    }

    typename vertex_container::const_iterator vertices_end() const
    {
        return m_vertices.cend();
    }

    vertex_iterator add_vertex(const Point& p)
    {
        return m_vertices.insert(m_vertices.end(), vertex_type{p, invalid()});
    }

    face_container const& face_range() const
    {
        return m_faces;
    }

    vertex_container const& vertex_range() const
    {
        return m_vertices;
    }

    typename face_container::const_iterator faces_cbegin() const
    {
        return m_faces.cbegin();
    }

    typename face_container::iterator faces_begin()
    {
        return m_faces.begin();
    }

    typename face_container::const_iterator faces_cend() const
    {
        return m_faces.cend();
    }

    typename face_container::iterator faces_end()
    {
        return m_faces.end();
    }

    template <typename InputIt>
    void assign_vertices(InputIt begin, InputIt end)
    {
        m_vertices.assign(begin, end);
        m_faces.reserve(2 * m_vertices.size() - 5);
    }

    static Point const& face_vertex(face_iterator f, face_vertex_index v)
    {
        return f -> m_v[ v ] -> m_p;
    }

    static segment_type face_segment(halfedge_index e)
    {
        return segment_type(
            face_vertex(e.m_f, (e.m_v == 2 ? 0 : e.m_v + 1)),
            face_vertex(e.m_f, (e.m_v == 0 ? 2 : e.m_v - 1)) );
    }

    static Point& vertex(vertex_iterator v)
    {
        return v -> m_p;
    }

    static face_iterator neighbour(face_iterator f, unsigned short v)
    {
        return f -> m_f[ v ];
    }

    static const_face_iterator neighbour(const_face_iterator f,
                                         unsigned short v)
    {
        return f -> m_f[ v ];
    }

    static face_vertex_index opposite(face_iterator f, unsigned short v)
    {
        return f -> m_o[ v ];
    }

    static halfedge_index opposite(halfedge_index const& e)
    {
        return halfedge_index{ e.m_f -> m_f[ e.m_v ], e.m_f -> m_o[ e.m_v ] };
    }

    static halfedge_index next(halfedge_index const& e)
    {
        return halfedge_index{
            e.m_f,
            static_cast<unsigned short>(e.m_v == 2 ? 0 : e.m_v + 1) };
    }

    static halfedge_index prev(halfedge_index const& e)
    {
        return halfedge_index{
            e.m_f,
            static_cast<unsigned short>(e.m_v == 0 ? 2 : e.m_v - 1)};
    }

    vertex_iterator boundary_vertex() const
    {
        return m_boundary_vertex;
    }

    static vertex_iterator boundary_next(vertex_iterator v)
    {
        face_iterator fi = v -> m_f;
        if(fi -> m_v[ 0 ] == v )
            return fi -> m_v[ 1 ];
        else if(fi -> m_v[ 1 ] == v)
            return fi -> m_v[ 2 ];
        else
            return fi -> m_v[ 0 ];
    }

    vertex_iterator boundary_prev(vertex_iterator v) const
    {
        face_iterator fi = v -> m_f;
        unsigned short vi;
        if(fi -> m_v[ 0 ] == v) vi = 0;
        else if(fi -> m_v[ 1 ] == v) vi = 1;
        else vi = 2;
        if(m_faces.size() == 1)
            return vi == 0 ? fi -> m_v[ 2 ] : fi -> m_v[ vi - 1 ];
        halfedge_index e = next(halfedge_index{fi, vi});
        while( opposite(e).m_f != invalid() )
        {
            e = prev(opposite(e));
        }
        return e.m_f -> m_v[ e.m_v == 2 ? 0 : e.m_v + 1 ];
    }

    void clear()
    {
        m_vertices.clear();
        m_faces.clear();
    }

    std::size_t vertices() const
    {
        return m_vertices.size();
    }

    std::size_t faces() const
    {
        return m_faces.size();
    }

    static void flip(const halfedge_index& e)
    {
        face_iterator fi1 = e.m_f;
        face_type& f1 = *fi1;
        face_iterator fi2 = f1.m_f[ e.m_v ];
        face_type& f2 = *fi2;
        unsigned short const& v1 = e.m_v;
        unsigned short const v2 = f1.m_o[ v1 ];

        if( f1.m_v[ v1 == 0 ? 2 : v1 - 1 ] -> m_f == fi1 )
            f1.m_v[ v1 == 0 ? 2 : v1 - 1 ] -> m_f = fi2;
        if( f2.m_v[ v2 == 0 ? 2 : v2 - 1  ]-> m_f == fi2)
            f2.m_v[ v2 == 0 ? 2 : v2 - 1  ]-> m_f = fi1;
        f1.m_v[ v1 == 0 ? 2 : v1 - 1 ] = f2.m_v[ v2 ];
        f2.m_v[ v2 == 0 ? 2 : v2 - 1 ] = f1.m_v[ v1 ];

        f1.m_f[v1] = f2.m_f[ v2 == 2 ? 0 : v2 + 1 ];
        f1.m_o[v1] = f2.m_o[ v2 == 2 ? 0 : v2 + 1 ];
        if(f1.m_f[v1] != invalid()) {
            f1.m_f[v1] -> m_f[ f2.m_o[ v2 == 2 ? 0 : v2 + 1 ] ] = fi1;
            f1.m_f[v1] -> m_o[ f2.m_o[ v2 == 2 ? 0 : v2 + 1 ] ] = v1;
        }
        f2.m_f[v2] = f1.m_f[ v1 == 2 ? 0 : v1 + 1 ];
        f2.m_o[v2] = f1.m_o[ v1 == 2 ? 0 : v1 + 1 ];
        if(f2.m_f[v2] != invalid()) {
            f2.m_f[v2] -> m_f[ f1.m_o[ v1 == 2 ? 0 : v1 + 1 ] ] = fi2;
            f2.m_f[v2] -> m_o[ f1.m_o[ v1 == 2 ? 0 : v1 + 1 ] ] = v2;
        }
        f1.m_f[ v1 == 2 ? 0 : v1 + 1 ] = fi2;
        f1.m_o[ v1 == 2 ? 0 : v1 + 1 ] = v2 == 2 ? 0 : v2 + 1;
        f2.m_f[ v2 == 2 ? 0 : v2 + 1 ] = fi1;
        f2.m_o[ v2 == 2 ? 0 : v2 + 1 ] = v1 == 2 ? 0 : v1 + 1;
    }

    face_iterator add_face_on_boundary(halfedge_index e, vertex_iterator v)
    {
        const face_iterator f = e.m_f;
        const face_vertex_index adj = e.m_v;
        f -> m_o[adj] = 0;
        m_boundary_vertex = v;
        face_iterator pos = m_faces.insert(m_faces.end(),
            face_type{ {{v, f->m_v[ adj == 0 ? 2 : adj - 1 ],
                         f->m_v[ adj == 2 ? 0 : adj + 1 ] }},
                {{f, invalid(), invalid()}},
                {{adj, 4, 4}}});
        f -> m_f[adj] = pos;
        v -> m_f = pos;
        m_faces.back().m_v[ 2 ]->m_f = pos;
        return pos;
    }

    fulledge_index next_around_vertex(fulledge_index e)
    {
        if(e.m_f2 == invalid()) {
            face_vertex_index left_vi = (e.m_v1 == 0 ? 2 : e.m_v1 - 1);
            vertex_iterator left_v = e.m_f1->m_v[ left_vi ];
            face_iterator next_f = left_v->m_f;
            face_vertex_index next_vi =
                (next_f->m_v[ 0 ] == left_v) ? 2 :
                ((next_f->m_v[ 1 ] == left_v) ? 0 : 1);
            return fulledge_index(invalid(), 4, next_f, next_vi);
        } else {
            return fulledge_index(halfedge_index(e.m_f2,
                                                 e.m_v2 == 0 ? 2 : e.m_v2 - 1));
        }
    }

    static fulledge_index begin_vertex_edge(vertex_iterator vi)
    {
        face_iterator first_f = vi -> m_f;
        face_vertex_index next_vi =
            (first_f->m_v[ 0 ] == vi) ? 2 :
            ((first_f->m_v[ 1 ] == vi) ? 0 : 1);
        return fulledge_index(invalid(), 4, first_f, next_vi);
    }

    face_iterator add_isolated_face(vertex_iterator v1,
                                    vertex_iterator v2,
                                    vertex_iterator v3)
    {
        m_boundary_vertex = v1;
        face_iterator pos = m_faces.insert( m_faces.end(),
            face_type{
                {{ v1, v2, v3 }},
                {{ invalid(), invalid(), invalid() }},
                {{4, 4, 4}} } );
        v1 -> m_f = v2 -> m_f = v3 -> m_f = pos;
        return pos;
    }

    static halfedge_index face_edge(face_iterator f, face_vertex_index v = 0)
    {
        return halfedge_index{f, v};
    }

    static void connect(halfedge_index e1, halfedge_index e2)
    {
        face_iterator f1 = e1.m_f;
        face_iterator f2 = e2.m_f;
        unsigned short& v1 = e1.m_v;
        unsigned short& v2 = e2.m_v;
        f1 -> m_f[ v1 ] = f2;
        f1 -> m_o[ v1 ] = v2;
        f2 -> m_f[ v2 ] = f1;
        f2 -> m_o[ v2 ] = v1;
        if(f1 -> m_v[ (v1 == 2 ? 0 : v1 + 1) ] -> m_f == f1) {
            f1 -> m_v[ (v1 == 2 ? 0 : v1 + 1) ] -> m_f = f2;
        }
        if(f2 -> m_v[ (v2 == 2 ? 0 : v2 + 1) ] -> m_f == f2) {
            f2 -> m_v[ (v2 == 2 ? 0 : v2 + 1) ] -> m_f = f1;
        }
    }

    bool valid() const
    {
        bool valid = true;
        for(const_face_iterator fi = m_faces.begin();
            fi != m_faces.end();
            ++fi)
        {
            face_type const& f = *fi;
            for(unsigned short v = 0 ; v < 3 ; ++v)
            {
                if(f.m_f[ v ] == invalid())
                    continue;
                face_vertex_index const& o = f.m_o[ v ];
                valid = valid && (f.m_f[ v ]->m_o[ o ] == v);
                if(!valid) {
                    return false;
                }
                valid = valid && (f.m_f[ v ]->m_f[ o ] == fi);
                if(!valid) {
                    return false;
                }
                valid = valid
                    && (f.m_v[ (v + 1) % 3 ] == f.m_f[ v ]->m_v[ (o + 2) % 3 ])
                    && (f.m_v[ (v + 2) % 3 ] == f.m_f[ v ]->m_v[ (o + 1) % 3 ]);
                if(!valid) {
                    return false;
                }
                if(f.m_o[v] == 4) {
                    unsigned short next = (v + 1) % 3;
                    valid = valid && f.m_v[ next ]->m_f == fi;
                    if(!valid) {
                        return false;
                    }

                }
            }
        }
        for(const_vertex_iterator vi = m_vertices.cbegin();
            vi != m_vertices.cend();
            ++vi)
        {
            vertex_type const& v = *vi;
            if(v.m_f == invalid()) continue;

            bool found = false;
            for(face_vertex_index vj = 0; vj < 3 ; ++vj)
            {
                found = found || (v.m_f -> m_v[ vj ] == vi);
            }
            valid = valid && found;
            if(!valid) {
                return false;
            }
        }
        return valid;
    }
private:
    vertex_container m_vertices;
    face_container m_faces;
    vertex_iterator m_boundary_vertex;
};

template< typename Point >
struct edge_ref
{
    typename triangulation<Point>::halfedge_index m_e;
    triangulation<Point>& m_t;
};

template< typename Point >
using triangulation_face_range = typename triangulation<Point>::face_container;

template< typename Point >
using triangulation_vertex_range =
    typename triangulation<Point>::vertex_container;

} // namespace model

#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS
namespace traits
{

template<typename Triangulation>
struct tag< model::vertex_ref<Triangulation> >
{ typedef point_tag type; };

template<typename Triangulation>
struct coordinate_type< model::vertex_ref<Triangulation> >
{
    typedef typename coordinate_type<typename Triangulation::point_type>
        ::type type;
};

template<typename Triangulation>
struct dimension< model::vertex_ref<Triangulation> > : boost::mpl::int_<2> {};

template<typename Triangulation>
struct coordinate_system< model::vertex_ref<Triangulation> >
{
    typedef typename coordinate_system<typename Triangulation::point_type>
        ::type type;
};

template<typename Triangulation, std::size_t Dimension>
struct access<model::vertex_ref<Triangulation>, Dimension>
{
    static typename coordinate_type<typename Triangulation::point_type>::type
        get(model::vertex_ref<Triangulation> const& p)
    {
        return boost::geometry::get<Dimension>(p.m_p);
    }
};

template<typename Point> struct tag< model::edge_ref<Point> >
{ typedef segment_tag type; };

template<typename Point> struct point_type< model::edge_ref<Point> >
{ typedef Point type; };

template<typename Point, std::size_t Dimension>
struct indexed_access<model::edge_ref<Point>, 0, Dimension>
{
    static typename coordinate_type<Point>::type
        get(model::edge_ref<Point> const& p)
    {
        return get<Dimension>(
            p.m_t.face_vertex(p.m_e.m_f, (p.m_e.m_v == 2 ? 0 : p.m_e.m_v + 1))
        );
    }
};

template<typename Point, std::size_t Dimension>
struct indexed_access<model::edge_ref<Point>, 1, Dimension>
{
    static typename coordinate_type<Point>::type
        get(model::edge_ref<Point> const& p)
    {
        return get<Dimension>(
            p.m_t.face_vertex(p.m_e.m_f, (p.m_e.m_v == 0 ? 2 : p.m_e.m_v - 1)));
    }
};

template<typename Triangulation>
struct tag<model::face_ref<Triangulation>>
{ typedef ring_tag type; };

template<typename Triangulation>
struct point_order<model::face_ref<Triangulation>>
{ static const order_selector value = Triangulation::point_order; };

template<typename Triangulation>
struct closure<model::face_ref<Triangulation>>
{ static const closure_selector value = open; };

} // namespace traits
#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS

template<typename Point>
struct face_range_type<model::triangulation<Point>> {
    typedef typename model::triangulation_face_range<Point> type;
};

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator
>
inline typename model::triangulation
<
    Point,
    ClockWise,
    VertexContainer,
    FaceContainer,
    VertexAllocator,
    FaceAllocator
>::vertex_container const& vertex_range(
    model::triangulation<Point, ClockWise, VertexContainer, FaceContainer,
        VertexAllocator, FaceAllocator> const& t)
{ return t.vertex_range(); }

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator
>
inline typename model::triangulation
<
    Point,
    ClockWise,
    VertexContainer,
    FaceContainer,
    VertexAllocator,
    FaceAllocator
>::face_container const& face_range(
    model::triangulation<Point, ClockWise, VertexContainer, FaceContainer,
        VertexAllocator, FaceAllocator> const& t)
{ return t.face_range(); }

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator,
    typename FaceIterator,
    typename OutputIterator
>
inline void face_adjacent_range(
    model::triangulation<Point, ClockWise, VertexContainer, FaceContainer,
        VertexAllocator, FaceAllocator>& t,
    FaceIterator fi,
    OutputIterator out)
{
    typedef typename model::triangulation<Point, ClockWise, VertexContainer,
        FaceContainer, VertexAllocator, FaceAllocator > triangulation;
    typedef typename triangulation::face_iterator face_iterator;
    face_iterator const invalid = t.invalid();
    if(fi -> m_f[ 0 ] != invalid ) *out++ = fi->m_f[ 0 ];
    if(fi -> m_f[ 1 ] != invalid ) *out++ = fi->m_f[ 1 ];
    if(fi -> m_f[ 2 ] != invalid ) *out++ = fi->m_f[ 2 ];
}

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator,
    typename FaceIterator,
    typename OutputIterator
>
inline void face_incident_faces(
    model::triangulation<Point, ClockWise, VertexContainer, FaceContainer,
        VertexAllocator, FaceAllocator> & t,
    FaceIterator fi,
    OutputIterator out)
{
    typedef typename model::triangulation<Point, ClockWise, VertexContainer,
        FaceContainer, VertexAllocator, FaceAllocator> triangulation;
    typedef typename triangulation::face_iterator face_iterator;
    typedef typename triangulation::vertex_iterator vertex_iterator;
    typedef typename triangulation::face_vertex_index face_vertex_index;
    typedef typename triangulation::face_type face_type;
    face_iterator const invalid = t.invalid();
    for(face_vertex_index i = 0; i < 3; ++i)
    {
        face_iterator n = fi -> m_f[i];
        face_iterator m = fi -> m_f[i == 2 ? 0 : i + 1];
        face_iterator f_prev = fi;
        face_vertex_index v_prev = i;
        if(n != invalid) {
            f_prev = n;
            v_prev = fi -> m_o[i];
            v_prev = (v_prev == 0 ? 2 : v_prev - 1);
        }
        while(true)
        {
            face_iterator next = f_prev -> m_f[v_prev];
            if(next == invalid) {
                face_vertex_index j = (i == 2 ? 0 : i + 1);
                face_iterator m = fi -> m_f[j];
                if(m == invalid) break;
                face_vertex_index prev_vertex_index = (i == 0 ? 2 : i - 1 );
                vertex_iterator const& prev_vertex_it =
                    fi->m_v[ prev_vertex_index ];
                face_type& first = *prev_vertex_it->m_f;
                *out++ = prev_vertex_it->m_f;
                f_prev = next = prev_vertex_it->m_f;
                if(first.m_v[0] == prev_vertex_it) v_prev = 1;
                else if(first.m_v[1] == prev_vertex_it) v_prev = 2;
                else v_prev = 0;
                continue;
            } else {
                if(&(*next) == &(*fi)) break;
                *out++ = next;
                v_prev = f_prev -> m_o[v_prev];
                v_prev = (v_prev == 0 ? 2 : v_prev - 1);
                f_prev = next;
            }
        }
    }
}

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator,
    typename OutputIterator
>
inline void vertex_incident_faces(
    model::triangulation<Point, ClockWise, VertexContainer, FaceContainer,
        VertexAllocator, FaceAllocator> & t,
    typename model::triangulation<Point, ClockWise, VertexContainer,
        FaceContainer, VertexAllocator, FaceAllocator>::vertex_iterator vi,
    OutputIterator out)
{
    typedef typename model::triangulation<Point, ClockWise, VertexContainer,
        FaceContainer, VertexAllocator, FaceAllocator> triangulation;
    typedef typename triangulation::face_iterator face_iterator;
    typedef typename triangulation::fulledge_index fulledge_index;
    fulledge_index e = t.begin_vertex_edge(vi);
    face_iterator first_face = e.m_f2;
    *out++ = first_face;
    while(true) {
        e = t.next_around_vertex(e);
        if(e.m_f2 == t.invalid() || e.m_f2 == first_face) break;
        *out++ = e.m_f2;
    }
}

template
<
    typename Point,
    bool ClockWise,
    template<typename, typename> class VertexContainer,
    template<typename, typename> class FaceContainer,
    template<typename> class VertexAllocator,
    template<typename> class FaceAllocator,
    typename OutputIterator
>
inline void vertex_incident_vertices(
    model::triangulation<Point, ClockWise, VertexContainer, FaceContainer,
        VertexAllocator, FaceAllocator> & t,
    typename model::triangulation<Point, ClockWise, VertexContainer,
        FaceContainer, VertexAllocator, FaceAllocator>::vertex_iterator vi,
    OutputIterator out)
{
    typedef model::triangulation<Point, ClockWise, VertexContainer,
        FaceContainer, VertexAllocator, FaceAllocator> triangulation_type;
    typedef typename triangulation_type::face_iterator face_iterator;
    typedef typename triangulation_type::fulledge_index fulledge_index;
    fulledge_index e = t.begin_vertex_edge(vi);
    face_iterator first_face = e.m_f2;
    *out++ = e.m_f2 -> m_v[ e.m_v2 == 0 ? 2 : e.m_v2 - 1 ];
    while(true) {
        e = t.next_around_vertex(e);
        if(e.m_f2 == t.invalid() || e.m_f2 == first_face) break;
        *out++ = e.m_f2 -> m_v[ e.m_v2 == 0 ? 2 : e.m_v2 - 1 ];
    }
}

} // namespace geometry

} // namespace boost

#endif // BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_GEOMETRIES_TRIANGULATION_HPP


#include "CALPHADFreeEnergyFunctionsTernary.h"
#include "CALPHADConcSolverTernary.h"
#include "CALPHADEqConcSolverTernary.h"
#include "CALPHADFunctions.h"
#include "CALPHADTieLineConcSolverTernary.h"
#include "PhysicalConstants.h"
#include "datatypes.h"
#include "functions.h"
#include "well_functions.h"

#include <boost/property_tree/json_parser.hpp>

#include <cmath>
#include <iomanip>
#include <string>

namespace pt = boost::property_tree;

namespace Thermo4PFM
{

void readLmixTernaryParameters(
    pt::ptree& Lmix_db, CalphadDataType LmixABC[3][2])
{
    // L0
    {
        auto child = Lmix_db.get_child_optional("L0");
        if (child)
        {
            int i = 0;
            for (pt::ptree::value_type& v : Lmix_db.get_child("L0"))
            {
                LmixABC[0][i] = v.second.get_value<CalphadDataType>();
                i++;
            }
        }
    }
    // L1
    {
        auto child = Lmix_db.get_child_optional("L1");
        if (child)
        {
            int i = 0;
            for (pt::ptree::value_type& v : Lmix_db.get_child("L1"))
            {
                LmixABC[1][i] = v.second.get_value<CalphadDataType>();
                i++;
            }
        }
    }
    // L2
    {
        auto child = Lmix_db.get_child_optional("L2");
        if (child)
        {
            int i = 0;
            for (pt::ptree::value_type& v : Lmix_db.get_child("L2"))
            {
                LmixABC[2][i] = v.second.get_value<CalphadDataType>();
                i++;
            }
        }
    }
}

CALPHADFreeEnergyFunctionsTernary::CALPHADFreeEnergyFunctionsTernary(
    pt::ptree& calphad_db, boost::optional<pt::ptree&> newton_db,
    const EnergyInterpolationType energy_interp_func_type,
    const ConcInterpolationType conc_interp_func_type)
    : energy_interp_func_type_(energy_interp_func_type),
      conc_interp_func_type_(conc_interp_func_type),
      newton_tol_(1.e-8),
      newton_alpha_(1.),
      newton_maxits_(20),
      newton_verbose_(false)

{
    std::string fenergy_diag_filename("energy.vtk");
    fenergy_diag_filename_ = new char[fenergy_diag_filename.length() + 1];
    strcpy(fenergy_diag_filename_, fenergy_diag_filename.c_str());

    readParameters(calphad_db);

    if (newton_db) readNewtonparameters(newton_db.get());
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::readNewtonparameters(
    pt::ptree& newton_db)
{
    newton_tol_     = newton_db.get<double>("tol", newton_tol_);
    newton_alpha_   = newton_db.get<double>("alpha", newton_alpha_);
    newton_maxits_  = newton_db.get<int>("max_its", newton_maxits_);
    newton_verbose_ = newton_db.get<bool>("verbose", newton_verbose_);
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::readParameters(pt::ptree& calphad_db)
{
    pt::ptree& species0_db = calphad_db.get_child("SpeciesA");
    std::string name       = species0_db.get<std::string>("name", "unknown");
    std::string dbnameL("PhaseL");
    g_species_phaseL_[0].initialize(name, species0_db.get_child(dbnameL));
    std::string dbnameA("PhaseA");
    g_species_phaseA_[0].initialize(name, species0_db.get_child(dbnameA));

    pt::ptree& speciesB_db = calphad_db.get_child("SpeciesB");
    name                   = speciesB_db.get<std::string>("name", "unknown");
    g_species_phaseL_[1].initialize(name, speciesB_db.get_child(dbnameL));
    g_species_phaseA_[1].initialize(name, speciesB_db.get_child(dbnameA));

    pt::ptree& speciesC_db = calphad_db.get_child("SpeciesC");
    name                   = speciesC_db.get<std::string>("name", "unknown");
    g_species_phaseL_[2].initialize(name, speciesC_db.get_child(dbnameL));
    g_species_phaseA_[2].initialize(name, speciesC_db.get_child(dbnameA));

    // read Lmix coefficients

    // AB
    {
        pt::ptree& Lmix0_db = calphad_db.get_child("LmixABPhaseL");
        readLmixBinary(Lmix0_db, LmixABPhaseL_);

        pt::ptree& Lmix1_db = calphad_db.get_child("LmixABPhaseA");
        readLmixBinary(Lmix1_db, LmixABPhaseA_);
    }

    // AC
    {
        pt::ptree& Lmix0_db = calphad_db.get_child("LmixACPhaseL");
        readLmixBinary(Lmix0_db, LmixACPhaseL_);

        pt::ptree& Lmix1_db = calphad_db.get_child("LmixACPhaseA");
        readLmixBinary(Lmix1_db, LmixACPhaseA_);
    }

    // BC
    {
        pt::ptree& Lmix0_db = calphad_db.get_child("LmixBCPhaseL");
        readLmixBinary(Lmix0_db, LmixBCPhaseL_);

        pt::ptree& Lmix1_db = calphad_db.get_child("LmixBCPhaseA");
        readLmixBinary(Lmix1_db, LmixBCPhaseA_);
    }

    // ABC
    {
        // default values
        LmixABCPhaseL_[0][0] = 0.0;
        LmixABCPhaseL_[0][1] = 0.0;
        LmixABCPhaseL_[1][0] = 0.0;
        LmixABCPhaseL_[1][1] = 0.0;
        LmixABCPhaseL_[2][0] = 0.0;
        LmixABCPhaseL_[2][1] = 0.0;

        std::string dbnamemixL("LmixABCPhaseL");
        if (calphad_db.get_child_optional(dbnamemixL))
        {
            pt::ptree& Lmix0_db = calphad_db.get_child(dbnamemixL);
            readLmixTernaryParameters(Lmix0_db, LmixABCPhaseL_);
        }

        assert(LmixABCPhaseL_[0][0] == LmixABCPhaseL_[0][0]);
        assert(LmixABCPhaseL_[0][1] == LmixABCPhaseL_[0][1]);
        assert(LmixABCPhaseL_[1][0] == LmixABCPhaseL_[1][0]);
        assert(LmixABCPhaseL_[1][1] == LmixABCPhaseL_[1][1]);
        assert(LmixABCPhaseL_[2][0] == LmixABCPhaseL_[2][0]);
        assert(LmixABCPhaseL_[2][1] == LmixABCPhaseL_[2][1]);

        std::string dbnamemixA("LmixABCPhaseA");
        // default values
        LmixABCPhaseA_[0][0] = 0.0;
        LmixABCPhaseA_[0][1] = 0.0;
        LmixABCPhaseA_[1][0] = 0.0;
        LmixABCPhaseA_[1][1] = 0.0;
        LmixABCPhaseA_[2][0] = 0.0;
        LmixABCPhaseA_[2][1] = 0.0;
        if (calphad_db.get_child_optional(dbnamemixA))
        {
            pt::ptree& Lmix1_db = calphad_db.get_child(dbnamemixA);
            readLmixTernaryParameters(Lmix1_db, LmixABCPhaseA_);
        }

        assert(LmixABCPhaseA_[0][0] == LmixABCPhaseA_[0][0]);
        assert(LmixABCPhaseA_[0][1] == LmixABCPhaseA_[0][1]);
        assert(LmixABCPhaseA_[1][0] == LmixABCPhaseA_[1][0]);
        assert(LmixABCPhaseA_[1][1] == LmixABCPhaseA_[1][1]);
        assert(LmixABCPhaseA_[2][0] == LmixABCPhaseA_[2][0]);
        assert(LmixABCPhaseA_[2][1] == LmixABCPhaseA_[2][1]);
    }

    // print database just read
    // std::clog << "CALPHAD database..." << std::endl;
    // pt::write_json(std::clog, calphad_db);
}

//-----------------------------------------------------------------------

#ifdef HAVE_OPENMP_OFFLOAD
#pragma omp declare target
#endif

double CALPHADFreeEnergyFunctionsTernary::computeFreeEnergy(
    const double temperature, const double* conc, const PhaseIndex pi,
    const bool gp)
{
    const double conc0 = conc[0];
    const double conc1 = conc[1];

    CalphadDataType lAB[4]
        = { lmix0ABPhase(pi, temperature), lmix1ABPhase(pi, temperature),
              lmix2ABPhase(pi, temperature), lmix3ABPhase(pi, temperature) };
    CalphadDataType lAC[4]
        = { lmix0ACPhase(pi, temperature), lmix1ACPhase(pi, temperature),
              lmix2ACPhase(pi, temperature), lmix3ACPhase(pi, temperature) };
    CalphadDataType lBC[4]
        = { lmix0BCPhase(pi, temperature), lmix1BCPhase(pi, temperature),
              lmix2BCPhase(pi, temperature), lmix3BCPhase(pi, temperature) };

    CalphadDataType lABC[3] = { lmix0ABCPhase(pi, temperature),
        lmix1ABCPhase(pi, temperature), lmix2ABCPhase(pi, temperature) };

    CALPHADSpeciesPhaseGibbsEnergy* g_species;

    switch (pi)
    {
        case PhaseIndex::phaseL:
            g_species = &g_species_phaseL_[0];
            break;
        case PhaseIndex::phaseA:
            g_species = &g_species_phaseA_[0];
            break;
        default:
            //            std::cout << "CALPHADFreeEnergyFunctionsTernary::"
            //                         "computeFreeEnergy(), undefined phase!!!"
            //                      << std::endl;
            // abort();
            return 0.;
    }

    double conc2 = 1. - conc0 - conc1;
    double fe    = conc0 * g_species[0].fenergy(temperature)
                + conc1 * g_species[1].fenergy(temperature)
                + conc2 * g_species[2].fenergy(temperature)
                + CALPHADcomputeFMixTernary(lAB, lAC, lBC, lABC, conc0, conc1)
                + CALPHADcomputeFIdealMixTernary(
                      gas_constant_R_JpKpmol * temperature, conc0, conc1);

    // subtract -mu*c to get grand potential
    if (gp)
    {
        double deriv[2];
        computeDerivFreeEnergy(temperature, conc, pi, deriv);
        fe -= deriv[0] * conc0;
        fe -= deriv[1] * conc1;
    }

    return fe;
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::computeDerivFreeEnergy(
    const double temperature, const double* const conc, const PhaseIndex pi,
    double* deriv)
{
    CalphadDataType lAB[4]
        = { lmix0ABPhase(pi, temperature), lmix1ABPhase(pi, temperature),
              lmix2ABPhase(pi, temperature), lmix3ABPhase(pi, temperature) };
    CalphadDataType lAC[4]
        = { lmix0ACPhase(pi, temperature), lmix1ACPhase(pi, temperature),
              lmix2ACPhase(pi, temperature), lmix3ACPhase(pi, temperature) };
    CalphadDataType lBC[4]
        = { lmix0BCPhase(pi, temperature), lmix1BCPhase(pi, temperature),
              lmix2BCPhase(pi, temperature), lmix3BCPhase(pi, temperature) };

    CalphadDataType lABC[3] = { lmix0ABCPhase(pi, temperature),
        lmix1ABCPhase(pi, temperature), lmix2ABCPhase(pi, temperature) };

    CALPHADSpeciesPhaseGibbsEnergy* g_species;

    switch (pi)
    {
        case PhaseIndex::phaseL:
            g_species = &g_species_phaseL_[0];
            break;
        case PhaseIndex::phaseA:
            g_species = &g_species_phaseA_[0];
            break;
        default:
            //            std::cout << "CALPHADFreeEnergyFunctionsTernary::"
            //                         "computeFreeEnergy(), undefined phase!!!"
            //                      << std::endl;
            // abort();
            return;
    }

    CALPHADcomputeFMix_derivTernary(
        lAB, lAC, lBC, lABC, conc[0], conc[1], deriv);

    deriv[0] += g_species[0].fenergy(temperature);
    deriv[0] -= g_species[2].fenergy(temperature);

    deriv[1] += g_species[1].fenergy(temperature);
    deriv[1] -= g_species[2].fenergy(temperature);

    double tmp[2];
    CALPHADcomputeFIdealMix_derivTernary(
        gas_constant_R_JpKpmol * temperature, conc[0], conc[1], tmp);
    deriv[0] += tmp[0];
    deriv[1] += tmp[1];
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::computeSecondDerivativeFreeEnergy(
    const double temp, const double* const conc, const PhaseIndex pi,
    double* d2fdc2)
{
    // assert(conc[0] >= 0.);
    // assert(conc[0] <= 1.);
    // assert(conc[1] >= 0.);
    // assert(conc[1] <= 1.);

    CalphadDataType lAB[4]  = { lmix0ABPhase(pi, temp), lmix1ABPhase(pi, temp),
        lmix2ABPhase(pi, temp), lmix3ABPhase(pi, temp) };
    CalphadDataType lAC[4]  = { lmix0ACPhase(pi, temp), lmix1ACPhase(pi, temp),
        lmix2ACPhase(pi, temp), lmix3ACPhase(pi, temp) };
    CalphadDataType lBC[4]  = { lmix0BCPhase(pi, temp), lmix1BCPhase(pi, temp),
        lmix2BCPhase(pi, temp), lmix3BCPhase(pi, temp) };
    CalphadDataType lABC[3] = { lmix0ABCPhase(pi, temp),
        lmix1ABCPhase(pi, temp), lmix2ABCPhase(pi, temp) };
    const double rt         = gas_constant_R_JpKpmol * temp;

    double deriv1[4];
    CALPHADcomputeFIdealMix_deriv2Ternary(rt, conc[0], conc[1], &deriv1[0]);

    double deriv2[4];
    CALPHADcomputeFMix_deriv2Ternary(
        lAB, lAC, lBC, lABC, conc[0], conc[1], &deriv2[0]);

    d2fdc2[0] = deriv1[0] + deriv2[0];
    d2fdc2[1] = deriv1[1] + deriv2[1];
    d2fdc2[2] = deriv1[2] + deriv2[2];
    d2fdc2[3] = deriv1[3] + deriv2[3];
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::computeTdependentParameters(
    const double temperature, CalphadDataType* L_AB_L, CalphadDataType* L_AC_L,
    CalphadDataType* L_BC_L, CalphadDataType* L_ABC_L, CalphadDataType* L_AB_S,
    CalphadDataType* L_AC_S, CalphadDataType* L_BC_S, CalphadDataType* L_ABC_S,
    CalphadDataType* fA, CalphadDataType* fB, CalphadDataType* fC)
{
    fA[0] = g_species_phaseL_[0].fenergy(temperature);
    fB[0] = g_species_phaseL_[1].fenergy(temperature);
    fC[0] = g_species_phaseL_[2].fenergy(temperature);

    fA[1] = g_species_phaseA_[0].fenergy(temperature);
    fB[1] = g_species_phaseA_[1].fenergy(temperature);
    fC[1] = g_species_phaseA_[2].fenergy(temperature);

    L_AB_L[0]  = lmix0ABPhaseL(temperature);
    L_AB_L[1]  = lmix1ABPhaseL(temperature);
    L_AB_L[2]  = lmix2ABPhaseL(temperature);
    L_AB_L[3]  = lmix3ABPhaseL(temperature);
    L_AC_L[0]  = lmix0ACPhaseL(temperature);
    L_AC_L[1]  = lmix1ACPhaseL(temperature);
    L_AC_L[2]  = lmix2ACPhaseL(temperature);
    L_AC_L[3]  = lmix3ACPhaseL(temperature);
    L_BC_L[0]  = lmix0BCPhaseL(temperature);
    L_BC_L[1]  = lmix1BCPhaseL(temperature);
    L_BC_L[2]  = lmix2BCPhaseL(temperature);
    L_BC_L[3]  = lmix3BCPhaseL(temperature);
    L_ABC_L[0] = lmix0ABCPhaseL(temperature);
    L_ABC_L[1] = lmix1ABCPhaseL(temperature);
    L_ABC_L[2] = lmix2ABCPhaseL(temperature);

    L_AB_S[0]  = lmix0ABPhaseA(temperature);
    L_AB_S[1]  = lmix1ABPhaseA(temperature);
    L_AB_S[2]  = lmix2ABPhaseA(temperature);
    L_AB_S[3]  = lmix3ABPhaseA(temperature);
    L_AC_S[0]  = lmix0ACPhaseA(temperature);
    L_AC_S[1]  = lmix1ACPhaseA(temperature);
    L_AC_S[2]  = lmix2ACPhaseA(temperature);
    L_AC_S[3]  = lmix3ACPhaseA(temperature);
    L_BC_S[0]  = lmix0BCPhaseA(temperature);
    L_BC_S[1]  = lmix1BCPhaseA(temperature);
    L_BC_S[2]  = lmix2BCPhaseA(temperature);
    L_BC_S[3]  = lmix3BCPhaseA(temperature);
    L_ABC_S[0] = lmix0ABCPhaseA(temperature);
    L_ABC_S[1] = lmix1ABCPhaseA(temperature);
    L_ABC_S[2] = lmix2ABCPhaseA(temperature);

    // assert(L_ABC_L[0] == L_ABC_L[0]);
    // assert(fC[0] == fC[0]);
}

//=======================================================================

// compute equilibrium concentrations in various phases for given temperature
bool CALPHADFreeEnergyFunctionsTernary::computeCeqT(
    const double temperature, double* ceq, const int maxits, const bool verbose)
{
    // if (verbose)
    //    std::cout << "CALPHADFreeEnergyFunctionsTernary::computeCeqT()"
    //              << std::endl;
    // assert(temperature > 0.);

    CalphadDataType L_AB_L[4];
    CalphadDataType L_AC_L[4];
    CalphadDataType L_BC_L[4];

    CalphadDataType L_ABC_L[3];

    CalphadDataType L_AB_S[4];
    CalphadDataType L_AC_S[4];
    CalphadDataType L_BC_S[4];

    CalphadDataType L_ABC_S[3];

    CalphadDataType fA[2];
    CalphadDataType fB[2];
    CalphadDataType fC[2];

    computeTdependentParameters(temperature, L_AB_L, L_AC_L, L_BC_L, L_ABC_L,
        L_AB_S, L_AC_S, L_BC_S, L_ABC_S, fA, fB, fC);

    double RTinv = 1.0 / (gas_constant_R_JpKpmol * temperature);

    CALPHADEqConcSolverTernary eq_solver;
    eq_solver.setup(RTinv, L_AB_L, L_AC_L, L_BC_L, L_AB_S, L_AC_S, L_BC_S,
        L_ABC_L, L_ABC_S, fA, fB, fC);
    int ret = eq_solver.ComputeConcentration(ceq, newton_tol_, maxits);
#ifndef HAVE_OPENMP_OFFLOAD
    if (ret >= 0)
    {
        if (verbose)
        {
            std::cout << "CALPHAD, c0 phase0=" << ceq[0] << std::endl;
            std::cout << "CALPHAD, c1 phase0=" << ceq[1] << std::endl;
            std::cout << "CALPHAD, c0 phase1=" << ceq[2] << std::endl;
            std::cout << "CALPHAD, c1 phase1=" << ceq[3] << std::endl;
        }
    }
    else
    {
        std::cout << "CALPHADFreeEnergyFunctionsTernary, WARNING: ceq "
                     "computation did not converge"
                  << std::endl;
    }
#endif
    return (ret >= 0);
}

//=======================================================================

bool CALPHADFreeEnergyFunctionsTernary::computeTieLine(const double temperature,
    const double c0, const double c1, double* ceq, const int maxits,
    const bool verbose)
{
    CalphadDataType L_AB_L[4];
    CalphadDataType L_AC_L[4];
    CalphadDataType L_BC_L[4];

    CalphadDataType L_ABC_L[3];

    CalphadDataType L_AB_S[4];
    CalphadDataType L_AC_S[4];
    CalphadDataType L_BC_S[4];

    CalphadDataType L_ABC_S[3];

    CalphadDataType fA[2];
    CalphadDataType fB[2];
    CalphadDataType fC[2];

    computeTdependentParameters(temperature, L_AB_L, L_AC_L, L_BC_L, L_ABC_L,
        L_AB_S, L_AC_S, L_BC_S, L_ABC_S, fA, fB, fC);

    double RTinv = 1.0 / (gas_constant_R_JpKpmol * temperature);

    CALPHADTieLineConcSolverTernary eq_solver;
    eq_solver.setup(c0, c1, RTinv, L_AB_L, L_AC_L, L_BC_L, L_AB_S, L_AC_S,
        L_BC_S, L_ABC_L, L_ABC_S, fA, fB, fC);
    int ret = eq_solver.ComputeConcentration(ceq, newton_tol_, newton_maxits_);
#ifndef HAVE_OPENMP_OFFLOAD
    if (ret >= 0)
    {
        if (verbose)
        {
            std::cout << "CALPHAD, c0 phase0=" << ceq[0] << std::endl;
            std::cout << "CALPHAD, c1 phase0=" << ceq[1] << std::endl;
            std::cout << "CALPHAD, c0 phase1=" << ceq[2] << std::endl;
            std::cout << "CALPHAD, c1 phase1=" << ceq[3] << std::endl;
        }
    }
    else
    {
        std::cout << "CALPHADFreeEnergyFunctionsTernary, WARNING: ceq "
                     "computation did not converge"
                  << std::endl;
    }
#endif
    return (ret >= 0);
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::computePhasesFreeEnergies(
    const double temperature, const double* const hphi, const double conc0,
    const double conc1, double& fl, double& fa)
{
    // std::cout<<"CALPHADFreeEnergyFunctionsTernary::computePhasesFreeEnergies()"<<endl;

    double cauxilliary[4] = { conc0, conc1, conc0, conc1 };

    CalphadDataType L_AB_L[4];
    CalphadDataType L_AC_L[4];
    CalphadDataType L_BC_L[4];

    CalphadDataType L_ABC_L[3];

    CalphadDataType L_AB_S[4];
    CalphadDataType L_AC_S[4];
    CalphadDataType L_BC_S[4];

    CalphadDataType L_ABC_S[3];

    CalphadDataType fA[2];
    CalphadDataType fB[2];
    CalphadDataType fC[2];

    computeTdependentParameters(temperature, L_AB_L, L_AC_L, L_BC_L, L_ABC_L,
        L_AB_S, L_AC_S, L_BC_S, L_ABC_S, fA, fB, fC);

    double RTinv = 1.0 / (gas_constant_R_JpKpmol * temperature);
    CALPHADConcSolverTernary solver;
    solver.setup(conc0, conc1, hphi[0], RTinv, L_AB_L, L_AC_L, L_BC_L, L_AB_S,
        L_AC_S, L_BC_S, L_ABC_L, L_ABC_S, fA, fB, fC);
    int ret
        = solver.ComputeConcentration(cauxilliary, newton_tol_, newton_maxits_);
#ifndef HAVE_OPENMP_OFFLOAD
    if (ret < 0)
    {
        std::cerr << "ERROR in "
                     "CALPHADFreeEnergyFunctionsTernary::"
                     "computePhasesFreeEnergies() "
                     "---"
                  << "conc0=" << conc0 << ", conc1=" << conc1
                  << ", hphi=" << hphi[0] << std::endl;
        abort();
    }

    assert(conc0 >= 0.);
#endif
    double concl[2] = { cauxilliary[0], cauxilliary[1] };
    fl = computeFreeEnergy(temperature, &concl[0], PhaseIndex::phaseL, false);

    double conca[2] = { cauxilliary[2], cauxilliary[3] };
    fa = computeFreeEnergy(temperature, &conca[0], PhaseIndex::phaseA, false);
}

//-----------------------------------------------------------------------
// output: x
int CALPHADFreeEnergyFunctionsTernary::computePhaseConcentrations(
    const double temperature, const double* const conc, const double* const phi,
    double* x)
{
    // assert(conc[0] == conc[0]);
    // assert(conc[1] == conc[1]);
    // assert(x[0] >= 0.);
    // assert(x[1] >= 0.);
    // assert(x[0] <= 1.);
    // assert(x[1] <= 1.);

    const double conc0 = conc[0];
    const double conc1 = conc[1];

    const double RTinv = 1.0 / (gas_constant_R_JpKpmol * temperature);

    CalphadDataType L_AB_L[4];
    CalphadDataType L_AC_L[4];
    CalphadDataType L_BC_L[4];

    CalphadDataType L_ABC_L[3];

    CalphadDataType L_AB_S[4];
    CalphadDataType L_AC_S[4];
    CalphadDataType L_BC_S[4];

    CalphadDataType L_ABC_S[3];

    CalphadDataType fA[2];
    CalphadDataType fB[2];
    CalphadDataType fC[2];

    computeTdependentParameters(temperature, L_AB_L, L_AC_L, L_BC_L, L_ABC_L,
        L_AB_S, L_AC_S, L_BC_S, L_ABC_S, fA, fB, fC);
    // assert(fC[0] == fC[0]);

    const double hphi = interp_func(conc_interp_func_type_, phi[0]);

    // conc could be outside of [0.,1.] in a trial step
    double c0 = conc0 >= 0. ? conc0 : 0.;
    c0        = c0 <= 1. ? c0 : 1.;
    double c1 = conc1 >= 0. ? conc1 : 0.;
    c1        = c1 <= 1. ? c1 : 1.;

    CALPHADConcSolverTernary solver;
    solver.setup(c0, c1, hphi, RTinv, L_AB_L, L_AC_L, L_BC_L, L_AB_S, L_AC_S,
        L_BC_S, L_ABC_L, L_ABC_S, fA, fB, fC);
    int ret = solver.ComputeConcentration(x, newton_tol_, newton_maxits_);
#ifndef HAVE_OPENMP_OFFLOAD
    if (ret == -1)
    {
        std::cerr << "ERROR, "
                     "CALPHADFreeEnergyFunctionsTernary::"
                     "computePhaseConcentrations() "
                     "failed for conc0="
                  << conc0 << ", conc1=" << conc1 << ", hphi=" << hphi
                  << std::endl;
    }
#endif
    return ret;
}

#ifdef HAVE_OPENMP_OFFLOAD
#pragma omp end declare target
#endif

//-----------------------------------------------------------------------

void CALPHADFreeEnergyFunctionsTernary::energyVsPhiAndC(
    const double temperature, const double* const ceq, const bool found_ceq,
    const double phi_well_scale, const int npts_phi, const int npts_c)
{
    std::clog << "CALPHADFreeEnergyFunctionsTernary::energyVsPhiAndC()..."
              << std::endl;

    const double* const ceqL = &ceq[0];
    const double* const ceqS = &ceq[2];
    // std::clog<<"Input:
    // "<<ceq[0]<<","<<ceq[1]<<","<<ceq[2]<<","<<ceq[3]<<endl;

    double slopec = 0.;
    double fc0    = 0.;
    double fc1    = 0.;
    if (found_ceq)
    {
        // compute slope of f between equilibrium concentrations
        // to add slopec*conc to energy later on

        fc0    = computeFreeEnergy(temperature, &ceqL[0], PhaseIndex::phaseL);
        fc1    = computeFreeEnergy(temperature, &ceqS[0], PhaseIndex::phaseA);
        slopec = -(fc1 - fc0) / (ceqL[1] - ceqL[0]);
    }
    std::clog << std::setprecision(8) << "fc0: " << fc0 << "..."
              << ", fc1: " << fc1 << "..." << std::endl;
    std::clog << "CALPHADFreeEnergyFunctionsTernary: Use slope: " << slopec
              << "..." << std::endl;

    {
        // reset cmin, cmax, deltac
        double c0min = std::min(ceqL[0], ceqS[0]);
        double c0max = std::max(ceqL[0], ceqS[0]);

        double dc0     = c0max - c0min;
        c0min          = std::max(0.25 * c0min, c0min - 0.25 * dc0);
        c0max          = std::min(1. - 0.25 * (1. - c0max), c0max + 0.25 * dc0);
        c0max          = std::max(c0max, c0min + dc0);
        double deltac0 = (c0max - c0min) / (npts_c - 1);

        double c1min = std::min(ceqL[1], ceqS[1]);
        double c1max = std::max(ceqL[1], ceqS[1]);

        double dc1     = c1max - c1min;
        c1min          = std::max(0.25 * c1min, c1min - 0.25 * dc1);
        c1max          = std::min(1. - 0.25 * (1. - c1max), c1max + 0.25 * dc1);
        c1max          = std::max(c1max, c1min + dc1);
        double deltac1 = (c1max - c1min) / (npts_c - 1);

        std::clog << "Range for c0: " << c0min << " to " << c0max << std::endl;
        std::clog << "Range for c1: " << c1min << " to " << c1max << std::endl;

        std::ofstream tfile(fenergy_diag_filename_, std::ios::out);

        printEnergyVsPhiHeader(temperature, npts_phi, npts_c, npts_c, c0min,
            c0max, c1min, c1max, tfile);

        for (int i0 = 0; i0 < npts_c; i0++)
        {
            int i1    = (1. - c0min - deltac0 * i0 - c1min) / deltac1;
            int i1max = i1 < npts_c ? i1 : npts_c;
            for (int i1 = 0; i1 < i1max; i1++)
            {
                double c[2] = { c0min + deltac0 * i0, c1min + deltac1 * i1 };
                printEnergyVsPhi(
                    c, temperature, phi_well_scale, npts_phi, tfile);
            }
        }
    }
}

// Print out free energy as a function of phase
// for given composition and temperature
// File format: ASCII VTK, readble with Visit
void CALPHADFreeEnergyFunctionsTernary::printEnergyVsPhiHeader(
    const double temperature, const int nphi, const int nc0, const int nc1,
    const double c0min, const double c0max, const double c1min,
    const double c1max, std::ostream& os) const
{
    os << "# vtk DataFile Version 2.0" << std::endl;
    os << "Free energy [J/mol] at T=" << temperature << std::endl;
    os << "ASCII" << std::endl;
    os << "DATASET STRUCTURED_POINTS" << std::endl;

    os << "DIMENSIONS   " << nphi << " " << nc0 << " " << nc1 << std::endl;
    double asp_ratio_c0 = (nc0 > 1) ? (c0max - c0min) / (nc0 - 1) : 1.;
    double asp_ratio_c1 = (nc1 > 1) ? (c1max - c1min) / (nc1 - 1) : 1.;
    os << "ASPECT_RATIO " << 1. / (nphi - 1) << " " << asp_ratio_c0 << " "
       << asp_ratio_c1 << std::endl;
    os << "ORIGIN        0. " << c0min << " " << c1min << std::endl;
    os << "POINT_DATA   " << nphi * nc0 * nc1 << std::endl;
    os << "SCALARS energy float 1" << std::endl;
    os << "LOOKUP_TABLE default" << std::endl;
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::printEnergyVsPhi(const double* conc,
    const double temperature, const double phi_well_scale, const int npts,
    std::ostream& os)
{
    // std::cout << "CALPHADFreeEnergyFunctionsTernary::printEnergyVsPhi()..."
    // << std::endl;
    const double dphi = 1.0 / (double)(npts - 1);

    // os << "# phi     f(phi)     for c=" << conc
    //           << " and T=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        const double phi = i * dphi;

        double e       = fchem(&phi, conc, temperature);
        const double w = phi_well_scale * well_func(phi);

        os << e + w << std::endl;
    }
    // os << std::endl;
}

//=======================================================================
// compute free energy in [J/mol]
double CALPHADFreeEnergyFunctionsTernary::fchem(
    const double* const phi, const double* const conc, const double temperature)
{
    const double conc0 = conc[0];
    const double conc1 = conc[1];

    const double hcphi = interp_func(conc_interp_func_type_, phi[0]);

    const double tol = 1.e-8;
    double fl        = 0.;
    double fa        = 0.;
    if ((phi[0] > tol) & (phi[0] < 1. - tol))
    {
        computePhasesFreeEnergies(temperature, &hcphi, conc0, conc1, fl, fa);
    }
    else
    {
        // don't solve for phases concentrations, just compute energy
        // in either phase
        double conc[2] = { conc0, conc1 };
        if (phi[0] <= tol)
        {
            fl = computeFreeEnergy(temperature, &conc[0], PhaseIndex::phaseL);
        }
        else
        {
            fa = computeFreeEnergy(temperature, &conc[0], PhaseIndex::phaseA);
        }
    }

    const double hfphi = interp_func(energy_interp_func_type_, phi[0]);

    return (1.0 - hfphi) * fl + hfphi * fa;
}

//=======================================================================

void CALPHADFreeEnergyFunctionsTernary::printEnergyVsComposition(
    const double temperature, std::ostream& os, const int npts)
{
    const double dc = 1.0 / (double)(npts - 1);

    std::string name1("Fl");
    name1 += g_species_phaseL_[0].name();
    name1 += g_species_phaseL_[2].name();
    os << "#" << name1 << std::endl;
    os << "#phi=0, c1=0, temperature=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        double conc[2];
        conc[0] = i * dc;
        conc[1] = 0.;

        const double phi = 0.;

        double e = fchem(&phi, conc, temperature);
        os << conc[0] << "\t" << e << std::endl;
    }
    os << std::endl;

    std::string name2("Fs");
    name2 += g_species_phaseA_[0].name();
    name2 += g_species_phaseA_[2].name();
    os << "#" << name2 << std::endl;
    os << "#phi=1, c1=0, temperature=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        double conc[2];
        conc[0] = i * dc;
        conc[1] = 0.;

        const double phi = 1.;

        double e = fchem(&phi, conc, temperature);
        os << conc[0] << "\t" << e << std::endl;
    }
    os << std::endl;

    std::string name3("Fl");
    name3 += g_species_phaseL_[1].name();
    name3 += g_species_phaseL_[2].name();
    os << "#" << name3 << std::endl;
    os << "#phi=0, c0=0, temperature=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        double conc[2];
        conc[0] = 0.;
        conc[1] = i * dc;

        const double phi = 0.;

        double e = fchem(&phi, conc, temperature);
        os << conc[1] << "\t" << e << std::endl;
    }
    os << std::endl;

    std::string name4("Fs");
    name4 += g_species_phaseA_[1].name();
    name4 += g_species_phaseA_[2].name();
    os << "#" << name4 << std::endl;
    os << "#phi=1, c0=0, temperature=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        double conc[2];
        conc[0] = 0.;
        conc[1] = i * dc;

        const double phi = 1.;

        double e = fchem(&phi, conc, temperature);
        os << conc[1] << "\t" << e << std::endl;
    }
    os << std::endl;

    std::string name5("Fl");
    name5 += g_species_phaseL_[0].name();
    name5 += g_species_phaseL_[1].name();
    os << "#" << name5 << std::endl;
    os << "#phi=0, temperature=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        double conc[2];
        conc[0] = i * dc;
        conc[1] = 1. - i * dc;

        const double phi = 0.;

        double e = fchem(&phi, conc, temperature);
        os << conc[0] << "\t" << e << std::endl;
    }
    os << std::endl;

    std::string name6("Fs");
    name6 += g_species_phaseA_[0].name();
    name6 += g_species_phaseA_[1].name();
    os << "#" << name6 << std::endl;
    os << "#phi=1, temperature=" << temperature << std::endl;
    for (int i = 0; i < npts; i++)
    {
        double conc[2];
        conc[0] = i * dc;
        conc[1] = 1. - i * dc;

        const double phi = 1.;

        double e = fchem(&phi, conc, temperature);
        os << conc[0] << "\t" << e << std::endl;
    }
    os << std::endl;
}

void CALPHADFreeEnergyFunctionsTernary::preRunDiagnostics(
    const double T0, const double T1)
{
    std::ofstream os1("FlC0vsT.dat", std::ios::out);
    os1 << "#Species 0, Phase L" << std::endl;
    g_species_phaseL_[0].plotFofT(os1, T0, T1);

    std::ofstream os2("FlC1vsT.dat", std::ios::out);
    os2 << "#Species 1, Phase L" << std::endl;
    g_species_phaseL_[1].plotFofT(os2, T0, T1);

    std::ofstream os3("FlC2vsT.dat", std::ios::out);
    os3 << "#Species 2, Phase L" << std::endl;
    g_species_phaseL_[2].plotFofT(os3, T0, T1);

    std::ofstream os4("FsC0vsT.dat", std::ios::out);
    os4 << "#Species 0, Phase A" << std::endl;
    g_species_phaseA_[0].plotFofT(os4, T0, T1);

    std::ofstream os5("FsC1vsT.dat", std::ios::out);
    os5 << "#Species 1, Phase A" << std::endl;
    g_species_phaseA_[1].plotFofT(os5, T0, T1);

    std::ofstream os6("FsC2vsT.dat", std::ios::out);
    os6 << "#Species 2, Phase A" << std::endl;
    g_species_phaseA_[2].plotFofT(os6, T0, T1);
}
}


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program 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.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.

#include "graph_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"

#include "graph_augment.hh"
#include <boost/graph/stoer_wagner_min_cut.hpp>

using namespace std;
using namespace boost;
using namespace graph_tool;

struct get_min_cut
{
    template <class Graph, class EdgeWeight, class PartMap>
    void operator()(Graph& g, EdgeWeight eweight, PartMap part_map, double& mc) const
    {
        try
        {
            mc = stoer_wagner_min_cut(g, eweight, parity_map(part_map));
        }
        catch (bad_graph&)
        {
            throw ValueException("Graph has less than 2 vertices.");
        }
    }

};

double min_cut(GraphInterface& gi, boost::any weight, boost::any part_map)
{
    double mc = 0;

    typedef ConstantPropertyMap<size_t,GraphInterface::edge_t> cweight_t;

    if (weight.empty())
        weight = cweight_t(1);

    typedef boost::mpl::push_back<writable_edge_scalar_properties, cweight_t>::type
        weight_maps;

    run_action<graph_tool::detail::never_directed>()
        (gi, std::bind(get_min_cut(),  placeholders::_1,  placeholders::_2,
                       placeholders::_3, std::ref(mc)),
         weight_maps(), writable_vertex_scalar_properties())(weight, part_map);
    return mc;
}

struct do_get_residual_graph
{
    template <class Graph, class CapacityMap, class ResidualMap,
              class AugmentedMap>
    void operator()(Graph& g, CapacityMap capacity, ResidualMap res,
                    AugmentedMap augmented) const
    {
        residual_graph(g, capacity, res, augmented);
    }
};

void get_residual_graph(GraphInterface& gi, boost::any capacity,
                        boost::any res, boost::any oaugment)
{
    typedef property_map_type::apply<uint8_t,
                                     GraphInterface::edge_index_map_t>::type
        emap_t;
    emap_t augment = boost::any_cast<emap_t>(oaugment);
    run_action<>()
        (gi, std::bind(do_get_residual_graph(), placeholders::_1,
                       placeholders::_2, placeholders::_3, augment),
         edge_scalar_properties(), edge_scalar_properties())(capacity, res);
}


// ---------------------------------------------------------------------
//
// Copyright (c) 2017 - 2019 by the IBAMR developers
// All rights reserved.
//
// This file is part of IBAMR.
//
// IBAMR is free software and is distributed under the 3-clause BSD
// license. The full text of the license can be found in the file
// COPYRIGHT at the top level directory of IBAMR.
//
// ---------------------------------------------------------------------

// Config files
#include <IBAMR_config.h>
#include <IBTK_config.h>

#include <SAMRAI_config.h>

// Headers for basic PETSc functions
#include <petscsys.h>

// Headers for basic SAMRAI objects
#include <BergerRigoutsos.h>
#include <CartesianGridGeometry.h>
#include <LoadBalancer.h>
#include <StandardTagAndInitialize.h>
#include <VariableDatabase.h>

// Headers for application-specific algorithm/data structure objects
#include <ibamr/CIBMethod.h>
#include <ibamr/CIBMobilitySolver.h>
#include <ibamr/CIBSaddlePointSolver.h>
#include <ibamr/CIBStaggeredStokesSolver.h>
#include <ibamr/DirectMobilitySolver.h>
#include <ibamr/IBExplicitHierarchyIntegrator.h>
#include <ibamr/IBHydrodynamicForceEvaluator.h>
#include <ibamr/IBStandardForceGen.h>
#include <ibamr/IBStandardInitializer.h>
#include <ibamr/INSStaggeredHierarchyIntegrator.h>
#include <ibamr/INSStaggeredPressureBcCoef.h>
#include <ibamr/KrylovMobilitySolver.h>
#include <ibamr/app_namespaces.h>

#include <ibtk/AppInitializer.h>
#include <ibtk/LData.h>
#include <ibtk/LDataManager.h>
#include <ibtk/muParserCartGridFunction.h>
#include <ibtk/muParserRobinBcCoefs.h>

#include <boost/multi_array.hpp>

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

// Center of mass velocity
void
ConstrainedCOMOuterVel(double /*data_time*/, IBTK::Vector3d& U_com, IBTK::Vector3d& W_com, void* /*ctx*/)
{
    U_com.setZero();
    W_com.setZero();
    U_com[0] = 1.0;
    U_com[1] = 2.0;
    W_com[1] = 5.0;

    return;
} // ConstrainedCOMOuterVel

// Center of mass velocity
void
ConstrainedCOMInnerVel(double /*data_time*/, IBTK::Vector3d& U_com, IBTK::Vector3d& W_com, void* /*ctx*/)
{
    U_com.setZero();
    W_com.setZero();
    return;
} // ConstrainedCOMInnerVel

void
ConstrainedNodalVel(Vec /*U_k*/, const RigidDOFVector& /*U*/, const IBTK::Vector3d& /*X_com*/, void* /*ctx*/)
{
    // intentionally left blank.
    return;
} // ConstrainedNodalVel

// These forces on the structure are computed when the "above" velocities are prescribed on them.
void
NetExternalForceTorqueOuter(double /*data_time*/, IBTK::Vector3d& F_ext, IBTK::Vector3d& T_ext, void* /*ctx*/)
{
    F_ext << 100.916, 1351.74, 0.000916801;
    T_ext << -2.23858e-09, 58206.1, -1.12567e-09;

    return;
} // NetExternalForceTorqueOuter

void
NetExternalForceTorqueInner(double /*data_time*/, IBTK::Vector3d& F_ext, IBTK::Vector3d& T_ext, void* /*ctx*/)
{
    F_ext << -116.762, -201.82, -0.00128781;
    T_ext << 1.46423e-09, -1393.33, 9.03505e-10;

    return;
} // NetExternalForceTorqueInner

// Function prototypes
void output_data(Pointer<PatchHierarchy<NDIM> > patch_hierarchy,
                 LDataManager* l_data_manager,
                 const int iteration_num,
                 const double loop_time,
                 const string& data_dump_dirname);

/*******************************************************************************
 * For each run, the input filename and restart information (if needed) must   *
 * be given on the command line.  For non-restarted case, command line is:     *
 *                                                                             *
 *    executable <input file name>                                             *
 *                                                                             *
 * For restarted run, command line is:                                         *
 *                                                                             *
 *    executable <input file name> <restart directory> <restart number>        *
 *                                                                             *
 *******************************************************************************/
int
main(int argc, char* argv[])
{
    // Initialize PETSc, MPI, and SAMRAI.
    PetscInitialize(&argc, &argv, NULL, NULL);
    SAMRAI_MPI::setCommunicator(PETSC_COMM_WORLD);
    SAMRAI_MPI::setCallAbortInSerialInsteadOfExit();
    SAMRAIManager::startup();
    SAMRAIManager::setMaxNumberPatchDataEntries(2054);

    { // cleanup dynamically allocated objects prior to shutdown

        // Parse command line options, set some standard options from the input
        // file, initialize the restart database (if this is a restarted run),
        // and enable file logging.
        Pointer<AppInitializer> app_initializer = new AppInitializer(argc, argv, "CIB.log");
        Pointer<Database> input_db = app_initializer->getInputDatabase();

        // Read default Petsc options
        if (input_db->keyExists("petsc_options_file"))
        {
            std::string petsc_options_file = input_db->getString("petsc_options_file");
            PetscOptionsInsertFile(PETSC_COMM_WORLD, NULL, petsc_options_file.c_str(), PETSC_TRUE);
        }

        // Get various standard options set in the input file.
        const bool dump_viz_data = app_initializer->dumpVizData();
        const int viz_dump_interval = app_initializer->getVizDumpInterval();
        const bool uses_visit = dump_viz_data && !app_initializer->getVisItDataWriter().isNull();

        const bool dump_restart_data = app_initializer->dumpRestartData();
        const int restart_dump_interval = app_initializer->getRestartDumpInterval();
        const string restart_dump_dirname = app_initializer->getRestartDumpDirectory();

        const bool dump_postproc_data = app_initializer->dumpPostProcessingData();
        const int postproc_data_dump_interval = app_initializer->getPostProcessingDataDumpInterval();
        const string postproc_data_dump_dirname = app_initializer->getPostProcessingDataDumpDirectory();
        if (dump_postproc_data && (postproc_data_dump_interval > 0) && !postproc_data_dump_dirname.empty())
        {
            Utilities::recursiveMkdir(postproc_data_dump_dirname);
        }

        const bool dump_timer_data = app_initializer->dumpTimerData();
        const int timer_dump_interval = app_initializer->getTimerDumpInterval();

        // Create major algorithm and data objects that comprise the
        // application.  These objects are configured from the input database
        // and, if this is a restarted run, from the restart database.

        // INS integrator
        Pointer<INSStaggeredHierarchyIntegrator> navier_stokes_integrator = new INSStaggeredHierarchyIntegrator(
            "INSStaggeredHierarchyIntegrator",
            app_initializer->getComponentDatabase("INSStaggeredHierarchyIntegrator"));

        // CIB method
        const unsigned int num_structures = input_db->getIntegerWithDefault("num_structures", 1);
        Pointer<CIBMethod> ib_method_ops =
            new CIBMethod("CIBMethod", app_initializer->getComponentDatabase("CIBMethod"), num_structures);

        // Krylov solver for INS integrator that solves for [u,p,U,L]
        Pointer<CIBStaggeredStokesSolver> CIBSolver =
            new CIBStaggeredStokesSolver("CIBStaggeredStokesSolver",
                                         input_db->getDatabase("CIBStaggeredStokesSolver"),
                                         navier_stokes_integrator,
                                         ib_method_ops,
                                         "SP_");

        // Register the Krylov solver with INS integrator
        navier_stokes_integrator->setStokesSolver(CIBSolver);

        Pointer<IBHierarchyIntegrator> time_integrator =
            new IBExplicitHierarchyIntegrator("IBHierarchyIntegrator",
                                              app_initializer->getComponentDatabase("IBHierarchyIntegrator"),
                                              ib_method_ops,
                                              navier_stokes_integrator);
        Pointer<CartesianGridGeometry<NDIM> > grid_geometry = new CartesianGridGeometry<NDIM>(
            "CartesianGeometry", app_initializer->getComponentDatabase("CartesianGeometry"));
        Pointer<PatchHierarchy<NDIM> > patch_hierarchy = new PatchHierarchy<NDIM>("PatchHierarchy", grid_geometry);
        Pointer<StandardTagAndInitialize<NDIM> > error_detector =
            new StandardTagAndInitialize<NDIM>("StandardTagAndInitialize",
                                               time_integrator,
                                               app_initializer->getComponentDatabase("StandardTagAndInitialize"));
        Pointer<BergerRigoutsos<NDIM> > box_generator = new BergerRigoutsos<NDIM>();
        Pointer<LoadBalancer<NDIM> > load_balancer =
            new LoadBalancer<NDIM>("LoadBalancer", app_initializer->getComponentDatabase("LoadBalancer"));
        Pointer<GriddingAlgorithm<NDIM> > gridding_algorithm =
            new GriddingAlgorithm<NDIM>("GriddingAlgorithm",
                                        app_initializer->getComponentDatabase("GriddingAlgorithm"),
                                        error_detector,
                                        box_generator,
                                        load_balancer);
        // Configure the IB solver.
        Pointer<IBStandardInitializer> ib_initializer = new IBStandardInitializer(
            "IBStandardInitializer", app_initializer->getComponentDatabase("IBStandardInitializer"));
        ib_method_ops->registerLInitStrategy(ib_initializer);

        // Specify structure kinematics
        FreeRigidDOFVector outer_free_dofs, inner_free_dofs;

        // Make some DOFs prescribed and some free.
        outer_free_dofs << 0, 0, 1, 1, 1, 1;
        inner_free_dofs << 1, 1, 0, 1, 1, 0;
        ib_method_ops->setSolveRigidBodyVelocity(0, outer_free_dofs);
        ib_method_ops->setSolveRigidBodyVelocity(1, inner_free_dofs);

        ib_method_ops->registerConstrainedVelocityFunction(NULL, &ConstrainedCOMOuterVel, NULL, 0);
        ib_method_ops->registerConstrainedVelocityFunction(NULL, &ConstrainedCOMInnerVel, NULL, 1);

        ib_method_ops->registerExternalForceTorqueFunction(&NetExternalForceTorqueOuter, NULL, 0);
        ib_method_ops->registerExternalForceTorqueFunction(&NetExternalForceTorqueInner, NULL, 1);

        // Create initial condition specification objects.
        Pointer<CartGridFunction> u_init = new muParserCartGridFunction(
            "u_init", app_initializer->getComponentDatabase("VelocityInitialConditions"), grid_geometry);
        navier_stokes_integrator->registerVelocityInitialConditions(u_init);
        Pointer<CartGridFunction> p_init = new muParserCartGridFunction(
            "p_init", app_initializer->getComponentDatabase("PressureInitialConditions"), grid_geometry);
        navier_stokes_integrator->registerPressureInitialConditions(p_init);

        // Set up visualization plot file writers.
        Pointer<VisItDataWriter<NDIM> > visit_data_writer = app_initializer->getVisItDataWriter();
        Pointer<LSiloDataWriter> silo_data_writer = app_initializer->getLSiloDataWriter();
        if (uses_visit)
        {
            ib_initializer->registerLSiloDataWriter(silo_data_writer);
            ib_method_ops->registerLSiloDataWriter(silo_data_writer);
            ib_method_ops->registerVisItDataWriter(visit_data_writer);
            time_integrator->registerVisItDataWriter(visit_data_writer);
        }

        // Create boundary condition specification objects (when necessary).
        const IntVector<NDIM>& periodic_shift = grid_geometry->getPeriodicShift();
        vector<RobinBcCoefStrategy<NDIM>*> u_bc_coefs(NDIM);
        if (periodic_shift.min() > 0)
        {
            for (unsigned int d = 0; d < NDIM; ++d)
            {
                u_bc_coefs[d] = NULL;
            }
        }
        else
        {
            for (unsigned int d = 0; d < NDIM; ++d)
            {
                const std::string bc_coefs_name = "u_bc_coefs_" + std::to_string(d);

                const std::string bc_coefs_db_name = "VelocityBcCoefs_" + std::to_string(d);

                Pointer<Database> bc_coefs_db = app_initializer->getComponentDatabase(bc_coefs_db_name);
                u_bc_coefs[d] = new muParserRobinBcCoefs(bc_coefs_name, bc_coefs_db, grid_geometry);
            }
            navier_stokes_integrator->registerPhysicalBoundaryConditions(u_bc_coefs);
        }

        // Initialize hierarchy configuration and data on all patches.
        time_integrator->initializePatchHierarchy(patch_hierarchy, gridding_algorithm);

        // Set physical boundary operator used in spreading.
        ib_method_ops->setVelocityPhysBdryOp(time_integrator->getVelocityPhysBdryOp());

        // Register mobility matrices (if needed)
        std::string mobility_solver_type = input_db->getString("MOBILITY_SOLVER_TYPE");
        if (mobility_solver_type == "DIRECT")
        {
            std::string mat_name1 = "struct-1";
            std::string mat_name2 = "struct-2";
            std::vector<std::vector<unsigned> > struct_ids1;
            std::vector<std::vector<unsigned> > struct_ids2;
            std::vector<unsigned> prototype_structs1;
            std::vector<unsigned> prototype_structs2;

            // Dense matrix
            prototype_structs1.push_back(0);
            prototype_structs2.push_back(1);

            struct_ids1.push_back(prototype_structs1);
            struct_ids2.push_back(prototype_structs2);

            DirectMobilitySolver* direct_solvers = NULL;
            CIBSolver->getSaddlePointSolver()->getCIBMobilitySolver()->getMobilitySolvers(NULL, &direct_solvers, NULL);

            direct_solvers->registerMobilityMat(
                mat_name1, prototype_structs1, EMPIRICAL, std::make_pair(LAPACK_SVD, LAPACK_SVD), 0);
            direct_solvers->registerStructIDsWithMobilityMat(mat_name1, struct_ids1);

            int next_proc = 0;
            if (SAMRAI_MPI::getNodes() > 1) next_proc = 1;
            direct_solvers->registerMobilityMat(
                mat_name2, prototype_structs2, EMPIRICAL, std::make_pair(LAPACK_SVD, LAPACK_SVD), next_proc);
            direct_solvers->registerStructIDsWithMobilityMat(mat_name2, struct_ids2);
        }
        navier_stokes_integrator->setStokesSolverNeedsInit();

        // Set up the hydro force objects
        double rho_fluid = input_db->getDouble("RHO");
        double mu_fluid = input_db->getDouble("MU");
        double start_time = time_integrator->getIntegratorTime();
        Pointer<IBHydrodynamicForceEvaluator> hydro_force =
            new IBHydrodynamicForceEvaluator("IBHydrodynamicForce", rho_fluid, mu_fluid, start_time, true);

        // Get the initial box position and velocity from input
        const string init_hydro_force_box_out_db_name = "InitHydroForceBox_0";
        IBTK::Vector3d box_X_lower_out, box_X_upper_out, box_init_vel_out;

        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("lower_left_corner", &box_X_lower_out[0], 3);
        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("upper_right_corner", &box_X_upper_out[0], 3);
        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("init_velocity", &box_init_vel_out[0], 3);

        const string init_hydro_force_box_in_db_name = "InitHydroForceBox_1";
        IBTK::Vector3d box_X_lower_in, box_X_upper_in, box_init_vel_in;

        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("lower_left_corner", &box_X_lower_in[0], 3);
        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("upper_right_corner", &box_X_upper_in[0], 3);
        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("init_velocity", &box_init_vel_in[0], 3);

        // Register the control volumes
        hydro_force->registerStructure(box_X_lower_out, box_X_upper_out, patch_hierarchy, box_init_vel_out, 0);
        hydro_force->registerStructure(box_X_lower_in, box_X_upper_in, patch_hierarchy, box_init_vel_in, 1);

        // Set up optional visualization of select boxes
        hydro_force->registerStructurePlotData(visit_data_writer, patch_hierarchy, 0);
        hydro_force->registerStructurePlotData(visit_data_writer, patch_hierarchy, 1);

        // Set the origin of the torque evaluation
        IBTK::Vector3d torque_origin_out, torque_origin_in;
        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("torque_origin", &torque_origin_out[0], 3);
        input_db->getDatabase(init_hydro_force_box_out_db_name)
            ->getDoubleArray("torque_origin", &torque_origin_in[0], 3);

        hydro_force->setTorqueOrigin(torque_origin_out, 0);
        hydro_force->setTorqueOrigin(torque_origin_in, 1);

        // Deallocate initialization objects.
        app_initializer.setNull();

        // Print the input database contents to the log file.
        plog << "Input database:\n";
        input_db->printClassData(plog);

        // Get velocity and pressure variables from integrator
        VariableDatabase<NDIM>* var_db = VariableDatabase<NDIM>::getDatabase();

        const Pointer<Variable<NDIM> > u_var = navier_stokes_integrator->getVelocityVariable();
        const Pointer<VariableContext> u_ctx = navier_stokes_integrator->getCurrentContext();
        const int u_idx = var_db->mapVariableAndContextToIndex(u_var, u_ctx);

        const Pointer<Variable<NDIM> > p_var = navier_stokes_integrator->getPressureVariable();
        const Pointer<VariableContext> p_ctx = navier_stokes_integrator->getCurrentContext();
        const int p_idx = var_db->mapVariableAndContextToIndex(p_var, p_ctx);

        // Write out initial visualization data.
        int iteration_num = time_integrator->getIntegratorStep();
        double loop_time = time_integrator->getIntegratorTime();

        if (dump_viz_data && uses_visit)
        {
            pout << "\n\nWriting visualization files...\n\n";
            time_integrator->setupPlotData();
            visit_data_writer->writePlotData(patch_hierarchy, iteration_num, loop_time);
            silo_data_writer->writePlotData(iteration_num, loop_time);
        }
        if (dump_postproc_data)
        {
            output_data(patch_hierarchy,
                        ib_method_ops->getLDataManager(),
                        iteration_num,
                        loop_time,
                        postproc_data_dump_dirname);
        }

        // Main time step loop.
        double loop_time_end = time_integrator->getEndTime();
        double current_time, new_time;
        double dt = 0.0;

        while (!MathUtilities<double>::equalEps(loop_time, loop_time_end) && time_integrator->stepsRemaining())
        {
            iteration_num = time_integrator->getIntegratorStep();
            loop_time = time_integrator->getIntegratorTime();
            current_time = loop_time;

            pout << "\n";
            pout << "+++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            pout << "At beginning of timestep # " << iteration_num << "\n";
            pout << "Simulation time is " << loop_time << "\n";

            dt = time_integrator->getMaximumTimeStepSize();
            loop_time += dt;
            new_time = loop_time;

            pout << "Advancing hierarchy by timestep size dt = " << dt << "\n";

            if (time_integrator->atRegridPoint()) navier_stokes_integrator->setStokesSolverNeedsInit();
            if (ib_method_ops->flagRegrid())
            {
                time_integrator->regridHierarchy();
                navier_stokes_integrator->setStokesSolverNeedsInit();
            }

            // Update the location of the box for time n + 1
            hydro_force->updateStructureDomain(IBTK::Vector3d::Zero(), dt, patch_hierarchy, 0);
            hydro_force->updateStructureDomain(IBTK::Vector3d::Zero(), dt, patch_hierarchy, 1);

            // Compute the momentum of u^n in box n+1 on the newest hierarchy
            hydro_force->computeLaggedMomentumIntegral(
                u_idx, patch_hierarchy, navier_stokes_integrator->getVelocityBoundaryConditions());

            // Advance the hierarchy
            time_integrator->advanceHierarchy(dt);

            pout << "\n\nNet rigid force and torque on structure 0 is : \n"
                 << ib_method_ops->getNetRigidGeneralizedForce(0) << "\n\n";
            pout << "\n\nNet rigid force and torque on structure 1 is : \n"
                 << ib_method_ops->getNetRigidGeneralizedForce(1) << "\n\n";

            RDV U;
            ib_method_ops->getNewRigidBodyVelocity(0, U);
            pout << "\n\nRigid body velocity of structure 0 is : \n" << U << "\n\n";
            ib_method_ops->getNewRigidBodyVelocity(1, U);
            pout << "\n\nRigid body velocity of structure 1 is : \n" << U << "\n\n";

            pout << "\n";
            pout << "At end       of timestep # " << iteration_num << "\n";
            pout << "Simulation time is " << loop_time << "\n";
            pout << "+++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            pout << "\n";

            // Set the linear and angular momentum of the spheres (zero in Stokes flow)
            IBTK::Vector3d Sphere_mom;
            IBTK::Vector3d Sphere_ang_mom;
            Sphere_mom.setZero();
            Sphere_ang_mom.setZero();

            // Store the new momenta of the sphere
            hydro_force->updateStructureMomentum(Sphere_mom, Sphere_ang_mom, 0);
            hydro_force->updateStructureMomentum(Sphere_mom, Sphere_ang_mom, 1);

            // Evaluate hydrodynamic force on the sphere.
            hydro_force->computeHydrodynamicForce(u_idx,
                                                  p_idx,
                                                  /*f_idx*/ -1,
                                                  patch_hierarchy,
                                                  dt,
                                                  navier_stokes_integrator->getVelocityBoundaryConditions(),
                                                  navier_stokes_integrator->getPressureBoundaryConditions());
            // Post processing call for writing data
            hydro_force->postprocessIntegrateData(current_time, new_time);

            // Update optional visualization of select boxes
            hydro_force->updateStructurePlotData(patch_hierarchy, 0);
            hydro_force->updateStructurePlotData(patch_hierarchy, 1);

            // At specified intervals, write visualization and restart files,
            // print out timer data, and store hierarchy data for post
            // processing.
            iteration_num += 1;
            const bool last_step = !time_integrator->stepsRemaining();
            if (dump_viz_data && uses_visit && (iteration_num % viz_dump_interval == 0 || last_step))
            {
                pout << "\nWriting visualization files...\n\n";
                time_integrator->setupPlotData();
                visit_data_writer->writePlotData(patch_hierarchy, iteration_num, loop_time);
                silo_data_writer->writePlotData(iteration_num, loop_time);
            }
            if (dump_restart_data && (iteration_num % restart_dump_interval == 0 || last_step))
            {
                pout << "\nWriting restart files...\n\n";
                RestartManager::getManager()->writeRestartFile(restart_dump_dirname, iteration_num);
            }
            if (dump_timer_data && (iteration_num % timer_dump_interval == 0 || last_step))
            {
                pout << "\nWriting timer data...\n\n";
                TimerManager::getManager()->print(plog);
            }
            if (dump_postproc_data && (iteration_num % postproc_data_dump_interval == 0 || last_step))
            {
                output_data(patch_hierarchy,
                            ib_method_ops->getLDataManager(),
                            iteration_num,
                            loop_time,
                            postproc_data_dump_dirname);
            }
        }

        // Cleanup boundary condition specification objects (when necessary).
        for (unsigned int d = 0; d < NDIM; ++d) delete u_bc_coefs[d];

    } // cleanup dynamically allocated objects prior to shutdown

    SAMRAIManager::shutdown();
    PetscFinalize();
} // main

void
output_data(Pointer<PatchHierarchy<NDIM> > /*patch_hierarchy*/,
            LDataManager* /*l_data_manager*/,
            const int iteration_num,
            const double loop_time,
            const string& /*data_dump_dirname*/)
{
    plog << "writing hierarchy data at iteration " << iteration_num << " to disk" << endl;
    plog << "simulation time is " << loop_time << endl;

    return;
} // output_data