<commit_before>/***********************************************
 * Author: Jun Jiang - jiangjun4@sina.com
 * Create: 2017-08-19 18:50
 * Last modified : 2017-08-19 18:50
 * Filename      : FNN.cpp
 * Description   :
 **********************************************/
#include "fnn/FNN.h"
#include "fnn/Layer.h"
#include "utils/Log.h"
#include "utils/Shuffler.h"
#include <vector>

namespace abcdl{
namespace fnn{

void FNN::train(const abcdl::algebra::Mat& train_data,
                const abcdl::algebra::Mat& train_label,
                const abcdl::algebra::Mat& test_data,
                const abcdl::algebra::Mat& test_label){
    LOG(INFO) << "fnn start training...";

    size_t layer_size = _layers.size();
    size_t num_train_data = train_data.rows();

    CHECK(num_train_data == train_label.rows());
    CHECK(train_data.cols() == _layers[0]->get_input_dim());
    CHECK(train_label.cols() == _layers[_layers.size() -1]->get_output_dim());

    abcdl::algebra::Mat data;
    abcdl::algebra::Mat label;
    abcdl::utils::Shuffler shuffler(num_train_data);
    auto now = []{return std::chrono::system_clock::now();};

    for(size_t i = 0; i != _epoch; i++){

        shuffler.shuffle();
        auto start_time = now();
        real total_loss = 0;

        for(size_t j = 0; j != num_train_data; j++){
            train_data.get_row(&data, shuffler.get(j));
            train_label.get_row(&label, shuffler.get(j));

            for(size_t k = 0; k != layer_size; k++){
                if(k == 0){
                    ((InputLayer*)_layers[k])->set_x(data);
                }
                _layers[k]->forward(_layers[k-1]);
            }

            for(size_t k = layer_size - 1; k > 0; k--){
                if(k == layer_size - 1){
                    ((OutputLayer*)_layers[k])->set_y(label);
                    _layers[k]->backward(_layers[k-1], nullptr);
                }else{
                    _layers[k]->backward(_layers[k-1], _layers[k+1]);
                }

                //mini_batch_update
                if(j % _batch_size == _batch_size - 1 || j == num_train_data - 1){
                    _layers[k]->update_gradient(j % _batch_size + 1, _alpha, _lamda);
                }
            }

            total_loss += _loss->loss(label, _layers[layer_size - 1]->get_activate_data());

            if(j % 100 == 0){
                printf("Epoch[%ld/%ld] Train[%ld/%ld]\r", i + 1, _epoch, j, num_train_data);
            }
        }

        auto train_time = now();
        printf("Epoch[%ld] loss[%f] training run time:[%ld]ms\n", i + 1, total_loss/num_train_data, std::chrono::duration_cast<std::chrono::milliseconds>(train_time - start_time).count());

        if(test_data.rows() > 0){
            real loss = 0;
            size_t num = evaluate(test_data, test_label, &loss);
            printf("Epoch[%ld][%ld/%ld] rate[%f]\n", i + 1, num, test_data.rows(), num/(real)test_data.rows());
    	    printf("Epoch[%ld] loss[%f] predict run time:[%ld]ms\n", i + 1, loss, std::chrono::duration_cast<std::chrono::milliseconds>(now() - train_time).count());
        }
    }
}

size_t FNN::evaluate(const abcdl::algebra::Mat& test_data,
                     const abcdl::algebra::Mat& test_label,
                     real* loss){
    CHECK(test_data.cols() == _layers[0]->get_input_dim());
    CHECK(test_data.rows() == test_label.rows());
    CHECK(test_label.cols() == _layers[_layers.size() - 1]->get_output_dim());

    size_t rows = test_data.rows();
    size_t predict_num = 0;
    abcdl::algebra::Mat mat;

    real total_loss = 0;
    for(size_t i = 0; i != rows; i++){
        predict(mat, test_data.get_row(i));
        if(mat.argmax() == test_label.get_row(i).argmax()){
            ++predict_num;
        }

        total_loss += _loss->loss(test_label.get_row(i), mat);
    }

    *loss = total_loss / rows;

    return predict_num;
}

void FNN::predict(abcdl::algebra::Mat& result, const abcdl::algebra::Mat& predict_data){
    CHECK(predict_data.cols() == _layers[0]->get_input_dim());
    size_t layer_size = _layers.size();
    for(size_t k = 0; k != layer_size; k++){
        if(k == 0){
            ((InputLayer*)_layers[k])->set_x(predict_data);
        }
        _layers[k]->forward(_layers[k-1]);
    }
    result = _layers[layer_size - 1]->get_activate_data();
}

bool FNN::load_model(const std::string& path){
    std::vector<abcdl::algebra::Mat*> models;
    if(!_model_loader.read<real>(path, &models, "FNNMODEL") || models.size() % 2 != 0){
        for(auto& model : models){
            delete model;
        }
        models.clear();
        return false;
    }

    for(auto& layer : _layers){
        delete layer;
    }
    _layers.clear();

    for(size_t i = 0; i != models.size() / 2; i++){
        if(i == 0){
            _layers.push_back(new InputLayer(models[0]->rows()));
        }
        if(i == models.size() / 2 - 1){
            _layers.push_back(new OutputLayer(models[i * 2]->rows(), models[i * 2]->cols(), new abcdl::framework::SigmoidActivateFunc(), new abcdl::framework::CrossEntropyCost(), *models[i * 2], *models[i * 2 + 1]));
        }else{
            _layers.push_back(new FullConnLayer(models[i * 2]->rows(), models[i * 2]->cols(), new abcdl::framework::SigmoidActivateFunc(), *models[i * 2], *models[i * 2 + 1]));
        }
    }

    for(auto& model : models){
        delete model;
    }
    models.clear();
    return true;
}

bool FNN::write_model(const std::string& path){
    std::vector<abcdl::algebra::Mat*> models;
    for(size_t i = 1; i != _layers.size(); i++){
        models.push_back(&_layers[i]->get_weight());
        models.push_back(&_layers[i]->get_bias());
    }
    return _model_loader.write<real>(models, path, "FNNMODEL", false);
}

}//namespace fnn
}//namespace abcdl
<commit_msg>fix display train epoch id<commit_after>/***********************************************
 * Author: Jun Jiang - jiangjun4@sina.com
 * Create: 2017-08-19 18:50
 * Last modified : 2017-08-19 18:50
 * Filename      : FNN.cpp
 * Description   :
 **********************************************/
#include "fnn/FNN.h"
#include "fnn/Layer.h"
#include "utils/Log.h"
#include "utils/Shuffler.h"
#include <vector>

namespace abcdl{
namespace fnn{

void FNN::train(const abcdl::algebra::Mat& train_data,
                const abcdl::algebra::Mat& train_label,
                const abcdl::algebra::Mat& test_data,
                const abcdl::algebra::Mat& test_label){
    LOG(INFO) << "fnn start training...";

    size_t layer_size = _layers.size();
    size_t num_train_data = train_data.rows();

    CHECK(num_train_data == train_label.rows());
    CHECK(train_data.cols() == _layers[0]->get_input_dim());
    CHECK(train_label.cols() == _layers[_layers.size() -1]->get_output_dim());

    abcdl::algebra::Mat data;
    abcdl::algebra::Mat label;
    abcdl::utils::Shuffler shuffler(num_train_data);
    auto now = []{return std::chrono::system_clock::now();};

    for(size_t i = 0; i != _epoch; i++){

        shuffler.shuffle();
        auto start_time = now();
        real total_loss = 0;

        for(size_t j = 0; j != num_train_data; j++){
            train_data.get_row(&data, shuffler.get(j));
            train_label.get_row(&label, shuffler.get(j));

            for(size_t k = 0; k != layer_size; k++){
                if(k == 0){
                    ((InputLayer*)_layers[k])->set_x(data);
                }
                _layers[k]->forward(_layers[k-1]);
            }

            for(size_t k = layer_size - 1; k > 0; k--){
                if(k == layer_size - 1){
                    ((OutputLayer*)_layers[k])->set_y(label);
                    _layers[k]->backward(_layers[k-1], nullptr);
                }else{
                    _layers[k]->backward(_layers[k-1], _layers[k+1]);
                }

                //mini_batch_update
                if(j % _batch_size == _batch_size - 1 || j == num_train_data - 1){
                    _layers[k]->update_gradient(j % _batch_size + 1, _alpha, _lamda);
                }
            }

            total_loss += _loss->loss(label, _layers[layer_size - 1]->get_activate_data());

            if(j % 100 == 0){
                printf("Epoch[%ld/%ld] Train[%ld/%ld]\r", i + 1, _epoch, j, num_train_data);
            }
        }

        auto train_time = now();
        printf("Epoch[%ld] loss[%f] training run time:[%ld]ms\n", i + 1, total_loss/num_train_data, std::chrono::duration_cast<std::chrono::milliseconds>(train_time - start_time).count());

        if(test_data.rows() > 0){
            real loss = 0;
            size_t num = evaluate(test_data, test_label, &loss);
    	    printf("Epoch[%ld] loss[%f] [%ld/%ld] rate[%f] predict run time:[%ld]ms\n", i + 1, loss, num, test_data.rows(), num/(real)test_data.rows(), std::chrono::duration_cast<std::chrono::milliseconds>(now() - train_time).count());
        }
    }
}

size_t FNN::evaluate(const abcdl::algebra::Mat& test_data,
                     const abcdl::algebra::Mat& test_label,
                     real* loss){
    CHECK(test_data.cols() == _layers[0]->get_input_dim());
    CHECK(test_data.rows() == test_label.rows());
    CHECK(test_label.cols() == _layers[_layers.size() - 1]->get_output_dim());

    size_t rows = test_data.rows();
    size_t predict_num = 0;
    abcdl::algebra::Mat mat;

    real total_loss = 0;
    for(size_t i = 0; i != rows; i++){
        predict(mat, test_data.get_row(i));
        if(mat.argmax() == test_label.get_row(i).argmax()){
            ++predict_num;
        }

        total_loss += _loss->loss(test_label.get_row(i), mat);
    }

    *loss = total_loss / rows;

    return predict_num;
}

void FNN::predict(abcdl::algebra::Mat& result, const abcdl::algebra::Mat& predict_data){
    CHECK(predict_data.cols() == _layers[0]->get_input_dim());
    size_t layer_size = _layers.size();
    for(size_t k = 0; k != layer_size; k++){
        if(k == 0){
            ((InputLayer*)_layers[k])->set_x(predict_data);
        }
        _layers[k]->forward(_layers[k-1]);
    }
    result = _layers[layer_size - 1]->get_activate_data();
}

bool FNN::load_model(const std::string& path){
    std::vector<abcdl::algebra::Mat*> models;
    if(!_model_loader.read<real>(path, &models, "FNNMODEL") || models.size() % 2 != 0){
        for(auto& model : models){
            delete model;
        }
        models.clear();
        return false;
    }

    for(auto& layer : _layers){
        delete layer;
    }
    _layers.clear();

    for(size_t i = 0; i != models.size() / 2; i++){
        if(i == 0){
            _layers.push_back(new InputLayer(models[0]->rows()));
        }
        if(i == models.size() / 2 - 1){
            _layers.push_back(new OutputLayer(models[i * 2]->rows(), models[i * 2]->cols(), new abcdl::framework::SigmoidActivateFunc(), new abcdl::framework::CrossEntropyCost(), *models[i * 2], *models[i * 2 + 1]));
        }else{
            _layers.push_back(new FullConnLayer(models[i * 2]->rows(), models[i * 2]->cols(), new abcdl::framework::SigmoidActivateFunc(), *models[i * 2], *models[i * 2 + 1]));
        }
    }

    for(auto& model : models){
        delete model;
    }
    models.clear();
    return true;
}

bool FNN::write_model(const std::string& path){
    std::vector<abcdl::algebra::Mat*> models;
    for(size_t i = 1; i != _layers.size(); i++){
        models.push_back(&_layers[i]->get_weight());
        models.push_back(&_layers[i]->get_bias());
    }
    return _model_loader.write<real>(models, path, "FNNMODEL", false);
}

}//namespace fnn
}//namespace abcdl
<|endoftext|>


<commit_before>#ifndef LM_HH
#define LM_HH

#include <float.h>
#include "misc/types.hh"
#include "FixedArray.hh"

namespace fsalm {

  class Semiring {
  public:
    Semiring() : one(0), zero(-1e30) { }
    virtual ~Semiring() { }
    virtual float plus(float a, float b) = 0;
    virtual float times(float a, float b) = 0;
    virtual float divide(float a, float b) = 0;
    virtual bool equal(float a, float b) = 0;
    float one;
    float zero;
  };

  class MaxPlusSemiring : public Semiring {
  public:
    virtual ~MaxPlusSemiring() { }
    virtual float plus(float a, float b) { return a < b ? b : a; }
    virtual float times(float a, float b) { return a + b; }
    virtual float divide(float a, float b) { return a - b; }
    virtual bool equal(float a, float b) { return a == b; }
  };

  /** Class for storing ngram language model in a fst format with
   * backoff arcs.
   *
   * INVARIANTS:
   *
   * - before trim() there may be nodes without children and bt=0
   *
   * - after trim() only final node has bt=0 and has zero children
   *
   * - node n has zero children if limit[n-1] == limit[n] or limit[n] == 0
   *
   * IMPLEMENTATION NOTES:
   *
   * - Backoff information is stored in the nodes instead of normal
   * arcs.  Otherwise, creating the arcs in proper order would be
   * troublesome from ARPA format.
   *
   * ASSUMES:
   * - children of a context are inserted in sorted chunk
   * - lower order is inserted before higher order
   */
  class LM {
  public:

    typedef FixedArray<int> Array;
    typedef FixedArray<float> FloatArray;

    Str start_str;
    Str end_str;
    Semiring *semiring;

    LM();
    void reset();
    const SymbolMap &symbol_map() const { return m_symbol_map; }
    int start_symbol() const { return m_start_symbol; }
    int end_symbol() const { return m_end_symbol; }
    int final_node_id() const { return m_final_node_id; }
    int empty_node_id() const { return m_empty_node_id; }
    int initial_node_id() const { return m_initial_node_id; }
    int order() const { return m_order; }
    int num_arcs() const { return m_arcs.symbol.num_elems(); }
    int num_nodes() const { return m_nodes.bo_target.num_elems(); }
    float final_score() const { return m_final_score; }

    /** Return the number of explicit children of node. */
    int num_children(int node_id) const;

    /** String describing a vector of symbol indices. */
    Str str(const IntVec &vec) const;

    /** Find context node for given backoff vector by backoffing
     * further if necessary.
     *
     * \note Throws exception if given ngram ends in sentence end
     * symbol.
     *
     * \param vec = candidate context for the backoff
     * \return node index corresponding to backoff context
     */
    int find_backoff(IntVec vec) const;

    /** Move to next node throught given symbol but do not backoff.
     *
     * \param node_id = the node to start from (final node not allowed)
     * \param symbol = the symbol to search
     * \param score = float pointer to which the possible score is ADDED
     * \return the resulting node (or -1 if explicit arc with symbol not found)
     */
    int walk_no_bo(int node_id, int symbol, float *score = NULL) const;
    int walk_no_bo(int node_id, const IntVec &vec, float *score = NULL) const;

    /** Move to next node through given symbols without backoffing.
     * Same as above, but returns node indices for each symbol in a
     * vector. */
    IntVec walk_no_bo_vec(int node_id, const IntVec &vec,
                          float *score = NULL) const;

    /** Move to next node through given symbol by backoffing if necessary.
     *
     * \param node_id = the node to start from (final node not allowed)
     * \param symbol = the symbol to traverse through
     * \param score = float pointer to which the score is ADDED
     * \return the resulting node
     */
    int walk(int node_id, int symbol, float *score = NULL) const;
    int walk(int node_id, const IntVec &vec, float *score = NULL) const;

    void new_arc(int src_node_id, int symbol, int tgt_node_id, float score);
    void new_ngram(const IntVec &vec, float score, float bo_score);
    int new_node();
    void set_arc(int arc_id, int symbol, int target, float score);
    void trim();
    void quantize(int bits);

    /** Compute potential of each node (used by push). */
    void compute_potential(FloatVec &d);

    /** Push scores as early as possible. */
    void push();

    void read_arpa(FILE *file, bool show_progress = false);
    void read(FILE *file);
    void write(FILE *file) const;

    /** Write the language model in MIT fst format. */
    void write_fst(FILE *file, Str bo_symbol = "<B>") const;

    /** Write the language model in ATT fsmt format. */
    void write_fsmt(FILE *file, Str bo_symbol = "<B>") const;
    void write_fsmt_node(FILE *file, int n, Str bo_symbol) const;

    /** Fetch probabilities for all symbols (ignoring non-events). */
    void fetch_probs(int node_id, FloatVec &vec);

    Str debug_str() const;
    bool debug_check_sum(int node_id) const;
    bool debug_check_sums() const;
    bool debug_check_zero_bo() const;

  private:

    /** Incoming arc used temporarily by compute_potential() */
    struct InArc {
      InArc() : source(-1), arc_id(-1) { }
      InArc(int source, int arc_id) : source(source), arc_id(arc_id) { }
      int source;
      int arc_id;
    };

    /** Node information */
    struct {
      FloatArray bo_score;
      Array bo_target;
      Array limit_arc;
    } m_nodes;

    /** Arc information */
    struct {
      Array symbol;
      Array target;
      FloatArray score;
    } m_arcs;

    /** Cache containing information about the last ngram inserted in
     * the strucure. */
    struct {
      IntVec ctx_vec;
      int ctx_node_id;
    } m_cache;

    /** Mapping between language model symbols and indices. */
    SymbolMap m_symbol_map;

    /** Bit array defining non-event symbols. */
    BoolVec m_non_event;

    int m_order;
    int m_empty_node_id;
    int m_initial_node_id;
    int m_final_node_id;
    int m_start_symbol;
    int m_end_symbol;
    float m_final_score;
  };

};

#endif /* LM_HH */
<commit_msg>Added some Doxygen comments.<commit_after>#ifndef LM_HH
#define LM_HH

#include <float.h>
#include "misc/types.hh"
#include "FixedArray.hh"

namespace fsalm {

  class Semiring {
  public:
    Semiring() : one(0), zero(-1e30) { }
    virtual ~Semiring() { }
    virtual float plus(float a, float b) = 0;
    virtual float times(float a, float b) = 0;
    virtual float divide(float a, float b) = 0;
    virtual bool equal(float a, float b) = 0;
    float one;
    float zero;
  };

  class MaxPlusSemiring : public Semiring {
  public:
    virtual ~MaxPlusSemiring() { }
    virtual float plus(float a, float b) { return a < b ? b : a; }
    virtual float times(float a, float b) { return a + b; }
    virtual float divide(float a, float b) { return a - b; }
    virtual bool equal(float a, float b) { return a == b; }
  };

  /** Class for storing ngram language model in a fst format with
   * backoff arcs.
   *
   * INVARIANTS:
   *
   * - before trim() there may be nodes without children and bt=0
   *
   * - after trim() only final node has bt=0 and has zero children
   *
   * - node n has zero children if limit[n-1] == limit[n] or limit[n] == 0
   *
   * IMPLEMENTATION NOTES:
   *
   * - Backoff information is stored in the nodes instead of normal
   * arcs.  Otherwise, creating the arcs in proper order would be
   * troublesome from ARPA format.
   *
   * ASSUMES:
   * - children of a context are inserted in sorted chunk
   * - lower order is inserted before higher order
   */
  class LM {
  public:

    typedef FixedArray<int> Array;
    typedef FixedArray<float> FloatArray;

    Str start_str;
    Str end_str;
    Semiring *semiring;

    LM();
    void reset();
    const SymbolMap &symbol_map() const { return m_symbol_map; }
    int start_symbol() const { return m_start_symbol; }
    int end_symbol() const { return m_end_symbol; }
    int final_node_id() const { return m_final_node_id; }
    int empty_node_id() const { return m_empty_node_id; }
    int initial_node_id() const { return m_initial_node_id; }
    int order() const { return m_order; }
    int num_arcs() const { return m_arcs.symbol.num_elems(); }
    int num_nodes() const { return m_nodes.bo_target.num_elems(); }
    float final_score() const { return m_final_score; }

    /** Return the number of explicit children of node. */
    int num_children(int node_id) const;

    /** String describing a vector of symbol indices. */
    Str str(const IntVec &vec) const;

    /** Find context node for given backoff vector by backoffing
     * further if necessary.
     *
     * \note Throws exception if given ngram ends in sentence end
     * symbol.
     *
     * \param vec = candidate context for the backoff
     * \return node index corresponding to backoff context
     */
    int find_backoff(IntVec vec) const;

    /** Move to next node throught given symbol but do not backoff.
     *
     * \param node_id = the node to start from (final node not allowed)
     * \param symbol = the symbol to search
     * \param score = float pointer to which the possible score is ADDED
     * \return the resulting node (or -1 if explicit arc with symbol not found)
     */
    int walk_no_bo(int node_id, int symbol, float *score = NULL) const;
    int walk_no_bo(int node_id, const IntVec &vec, float *score = NULL) const;

    /** Move to next node through given symbols without backoffing.
     * Same as above, but returns node indices for each symbol in a
     * vector. */
    IntVec walk_no_bo_vec(int node_id, const IntVec &vec,
                          float *score = NULL) const;

    /** Move to next node through given symbol by backoffing if necessary.
     *
     * \param node_id = the node to start from (final node not allowed)
     * \param symbol = the symbol to traverse through
     * \param score = float pointer to which the score is ADDED
     * \return the resulting node
     */
    int walk(int node_id, int symbol, float *score = NULL) const;
    int walk(int node_id, const IntVec &vec, float *score = NULL) const;

    void new_arc(int src_node_id, int symbol, int tgt_node_id, float score);
    void new_ngram(const IntVec &vec, float score, float bo_score);
    int new_node();
    void set_arc(int arc_id, int symbol, int target, float score);
    void trim();
    void quantize(int bits);

    /** Compute potential of each node (used by push). */
    void compute_potential(FloatVec &d);

    /** Push scores as early as possible. */
    void push();

    /** Reads the language model in ARPA format. */
    void read_arpa(FILE *file, bool show_progress = false);
    /** Reads the language model in a non-standard format. */
    void read(FILE *file);
    /** Writes the language model in a non-standard format. */
    void write(FILE *file) const;

    /** Write the language model in MIT fst format. */
    void write_fst(FILE *file, Str bo_symbol = "<B>") const;

    /** Write the language model in ATT fsmt format. */
    void write_fsmt(FILE *file, Str bo_symbol = "<B>") const;
    void write_fsmt_node(FILE *file, int n, Str bo_symbol) const;

    /** Fetch probabilities for all symbols (ignoring non-events). */
    void fetch_probs(int node_id, FloatVec &vec);

    Str debug_str() const;
    bool debug_check_sum(int node_id) const;
    bool debug_check_sums() const;
    bool debug_check_zero_bo() const;

  private:

    /** Incoming arc used temporarily by compute_potential() */
    struct InArc {
      InArc() : source(-1), arc_id(-1) { }
      InArc(int source, int arc_id) : source(source), arc_id(arc_id) { }
      int source;
      int arc_id;
    };

    /** Node information */
    struct {
      FloatArray bo_score;
      Array bo_target;
      Array limit_arc;
    } m_nodes;

    /** Arc information */
    struct {
      Array symbol;
      Array target;
      FloatArray score;
    } m_arcs;

    /** Cache containing information about the last ngram inserted in
     * the strucure. */
    struct {
      IntVec ctx_vec;
      int ctx_node_id;
    } m_cache;

    /** Mapping between language model symbols and indices. */
    SymbolMap m_symbol_map;

    /** Bit array defining non-event symbols. */
    BoolVec m_non_event;

    int m_order;
    int m_empty_node_id;
    int m_initial_node_id;
    int m_final_node_id;
    int m_start_symbol;
    int m_end_symbol;
    float m_final_score;
  };

};

#endif /* LM_HH */
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2010, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <vector>

#include "IECore/DisplayDriver.h"
#include "IECore/MessageHandler.h"
#include "IECore/SimpleTypedData.h"
#include "IECore/VectorTypedData.h"

#include "IECoreRI/Dspy.h"

using namespace std;
using namespace IECoreRI;
using namespace IECore;
using namespace Imath;

// deal with UserParameter naming differences between prman and 3delight
#ifdef PRMANEXPORT

#define UP_VALUETYPE vtype
#define UP_VALUECOUNT vcount

#else

#define UP_VALUETYPE valueType
#define UP_VALUECOUNT valueCount

#endif

//////////////////////////////////////////////////////////////////////////
// Registration
//////////////////////////////////////////////////////////////////////////

class Dspy::Registration
{

	public :

		Registration()
		{
			PtDspyDriverFunctionTable table;
			memset( &table, 0, sizeof( table ) );
			table.Version = k_PtDriverCurrentVersion;
			table.pOpen = &imageOpen;
			table.pQuery = &imageQuery;
			table.pWrite = &imageData;
			table.pClose = &imageClose;

			DspyRegisterDriverTable( "ie", &table );
		}

};

Dspy::Registration Dspy::g_registration;

//////////////////////////////////////////////////////////////////////////
// Functions
//////////////////////////////////////////////////////////////////////////

PtDspyError Dspy::imageOpen( PtDspyImageHandle *image, const char *driverName, const char *fileName, int width, int height, int paramcount, const UserParameter *parameters, int formatCount, PtDspyDevFormat *format, PtFlagStuff *flags )
{

	*image = 0;

	// get channel names

	vector<string> channels;

	if( formatCount == 1 )
	{
		channels.push_back( "R" );
	}
	else if( formatCount == 3 )
	{
		channels.push_back( "R" );
		channels.push_back( "G" );
		channels.push_back( "B" );
	}
	else if( formatCount == 4 )
	{
		channels.push_back( "R" );
		channels.push_back( "G" );
		channels.push_back( "B" );
		channels.push_back( "A" );
	}
	else
	{
		msg( Msg::Error, "Dspy::imageOpen", "Invalid number of channels!" );
		return PkDspyErrorBadParams;
	}
	for( int i = 0; i < formatCount; i++ )
	{
		format[i].type = PkDspyFloat32 | PkDspyByteOrderNative;
	}

	// process the parameter list. we use some of the parameters to help determine
	// the display and data windows, and the others we convert ready to passed to
	// DisplayDriver::create().

	V2i originalSize( width, height );
	V2i origin( 0 );

	CompoundDataPtr convertedParameters = new CompoundData;

	for( int p = 0; p < paramcount; p++ )
	{
		if ( !strcmp( parameters[p].name, "OriginalSize" ) && parameters[p].UP_VALUETYPE == (char)'i' && parameters[p].UP_VALUECOUNT == (char)2 && parameters[p].nbytes == (int) (parameters[p].UP_VALUECOUNT * sizeof(int)) )
		{
			originalSize.x = static_cast<const int *>(parameters[p].value)[0];
			originalSize.y = static_cast<const int *>(parameters[p].value)[1];
		}
		else if ( !strcmp( parameters[p].name, "origin" ) && parameters[p].UP_VALUETYPE == (char)'i' && parameters[p].UP_VALUECOUNT == (char)2 && parameters[p].nbytes == (int)(parameters[p].UP_VALUECOUNT * sizeof(int)) )
		{
			origin.x = static_cast<const int *>(parameters[p].value)[0];
			origin.y = static_cast<const int *>(parameters[p].value)[1];
		}
		else
		{
			DataPtr newParam;

			if ( !parameters[p].nbytes )
			{
				continue;
			}

			const int *pInt;
			const float *pFloat;
			char const **pChar;

			// generic converter
			switch( parameters[p].UP_VALUETYPE )
			{
			case 'i':
				// sanity check
				if ( parameters[p].nbytes / parameters[p].UP_VALUECOUNT != sizeof(int) )
				{
					msg( Msg::Error, "Dspy::imageOpen", "Invalid int data size" );
					continue;
				}
				pInt = static_cast<const int *>(parameters[p].value);
				if ( parameters[p].UP_VALUECOUNT == 1 )
				{
					newParam = new IntData( pInt[0] );
				}
				else
				{
					std::vector< int > newVec( pInt, pInt + parameters[p].UP_VALUECOUNT );
					newParam = new IntVectorData( newVec );
				}
				break;
			case 'f':
				if ( parameters[p].nbytes / parameters[p].UP_VALUECOUNT != sizeof(float) )
				{
					msg( Msg::Error, "Dspy::imageOpen", "Invalid float data size" );
					continue;
				}
				pFloat = static_cast<const float *>(parameters[p].value);
				if ( parameters[p].UP_VALUECOUNT == 1 )
				{
					newParam = new FloatData( pFloat[0] );
				}
				else
				{
					std::vector< float > newVec( pFloat, pFloat + parameters[p].UP_VALUECOUNT );
					newParam = new FloatVectorData( newVec );
				}
				break;
			case 's':
				if ( parameters[p].nbytes / parameters[p].UP_VALUECOUNT != sizeof(char*) )
				{
					msg( Msg::Error, "Dspy::imageOpen", "Invalid string data size" );
					continue;
				}
				pChar = (const char **)(parameters[p].value);
				if ( parameters[p].UP_VALUECOUNT == 1 )
				{
					newParam = new StringData( pChar[0] );
				}
				else
				{
					StringVectorDataPtr newStringVec = new StringVectorData();
					for ( int s = 0; s < parameters[p].UP_VALUECOUNT; s++ )
					{
						newStringVec->writable().push_back( pChar[s] );
					}
					newParam = newStringVec;
				}
				break;
			default :
				// we shouldn't ever get here...
				break;
			}
			if( newParam )
			{
				convertedParameters->writable()[ parameters[p].name ] = newParam;
			}
		}
	}

	convertedParameters->writable()[ "fileName" ] = new StringData( fileName );

	// calculate display and data windows

	Box2i displayWindow(
		V2i( 0 ),
		originalSize - V2i( 1 )
	);

	Box2i dataWindow(
		origin,
		origin + V2i( width - 1, height - 1)
	);

	// create the display driver

	IECore::DisplayDriverPtr dd = 0;
	try
	{
		const StringData *driverType = convertedParameters->member<StringData>( "driverType", true /* throw if missing */ );
		dd = DisplayDriver::create( driverType->readable(), displayWindow, dataWindow, channels, convertedParameters );
	}
	catch( std::exception &e )
	{
		msg( Msg::Error, "Dspy::imageOpen", e.what() );
		return PkDspyErrorUnsupported;
	}

	if( !dd )
	{
		msg( Msg::Error, "Dspy::imageOpen", "DisplayDriver::create returned 0." );
		return PkDspyErrorUnsupported;
	}

	// update flags and return

	if( dd->scanLineOrderOnly() )
	{
		flags->flags |= PkDspyFlagsWantsScanLineOrder;
	}

	dd->addRef(); // this will be removed in imageClose()
	*image = (PtDspyImageHandle)dd.get();
	return PkDspyErrorNone;

}

PtDspyError Dspy::imageQuery( PtDspyImageHandle image, PtDspyQueryType type, int size, void *data )
{
	return PkDspyErrorUnsupported;
}

PtDspyError Dspy::imageData( PtDspyImageHandle image, int xMin, int xMaxPlusOne, int yMin, int yMaxPlusOne, int entrySize, const unsigned char *data )
{
	DisplayDriver *dd = static_cast<DisplayDriver *>( image );
	Box2i dataWindow = dd->dataWindow();

	// convert coordinates from cropped image to original image coordinates.
	Box2i box( V2i( xMin + dataWindow.min.x, yMin + dataWindow.min.y ), V2i( xMaxPlusOne - 1 + dataWindow.min.x, yMaxPlusOne - 1 + dataWindow.min.y ) );
	int channels = dd->channelNames().size();
	int blockSize = (xMaxPlusOne - xMin) * (yMaxPlusOne - yMin);
	int bufferSize = channels * blockSize;

	if( entrySize % sizeof(float) )
	{
		msg( Msg::Error, "Dspy::imageData", "The entry size is not multiple of sizeof(float)!" );
		return PkDspyErrorUnsupported;
	}

	if( entrySize == (int)(channels*sizeof(float)) )
	{
		try
		{
			dd->imageData( box, (const float *)data, bufferSize );
		}
		catch( std::exception &e )
		{
			if( strcmp( e.what(), "stop" ) == 0 )
			{
				/// \todo I would prefer DisplayDriver::imageData to have a return
				/// value which could be used to request stop/continue behaviour.
				return PkDspyErrorStop;
			}
			else
			{
				msg( Msg::Error, "Dspy::imageData", e.what() );
				return PkDspyErrorUndefined;
			}
		}
	}
	else
	{
		msg( Msg::Error, "Dspy::imageData", "Unexpected entry size value!" );
		return PkDspyErrorBadParams;
	}
	return PkDspyErrorNone;
}

PtDspyError Dspy::imageClose( PtDspyImageHandle image )
{
	if ( !image )
	{
		return PkDspyErrorNone;
	}

	DisplayDriver *dd = static_cast<DisplayDriver*>( image );
	try
	{
		dd->imageClose();
	}
	catch( std::exception &e )
	{
		msg( Msg::Error, "Dspy::imageClose", e.what() );
	}

	try
	{
		dd->removeRef();
	}
	catch( std::exception &e )
	{
		msg( Msg::Error, "DspyImageData", e.what() );
		return PkDspyErrorBadParams;
	}

	return PkDspyErrorNone;
}
<commit_msg>Fix for prman builds, where PkDspyErrorStop isn't available.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2010, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <vector>

#include "IECore/DisplayDriver.h"
#include "IECore/MessageHandler.h"
#include "IECore/SimpleTypedData.h"
#include "IECore/VectorTypedData.h"

#include "IECoreRI/Dspy.h"

using namespace std;
using namespace IECoreRI;
using namespace IECore;
using namespace Imath;

// deal with UserParameter naming differences between prman and 3delight
#ifdef PRMANEXPORT

#define UP_VALUETYPE vtype
#define UP_VALUECOUNT vcount

#else

#define UP_VALUETYPE valueType
#define UP_VALUECOUNT valueCount

#endif

//////////////////////////////////////////////////////////////////////////
// Registration
//////////////////////////////////////////////////////////////////////////

class Dspy::Registration
{

	public :

		Registration()
		{
			PtDspyDriverFunctionTable table;
			memset( &table, 0, sizeof( table ) );
			table.Version = k_PtDriverCurrentVersion;
			table.pOpen = &imageOpen;
			table.pQuery = &imageQuery;
			table.pWrite = &imageData;
			table.pClose = &imageClose;

			DspyRegisterDriverTable( "ie", &table );
		}

};

Dspy::Registration Dspy::g_registration;

//////////////////////////////////////////////////////////////////////////
// Functions
//////////////////////////////////////////////////////////////////////////

PtDspyError Dspy::imageOpen( PtDspyImageHandle *image, const char *driverName, const char *fileName, int width, int height, int paramcount, const UserParameter *parameters, int formatCount, PtDspyDevFormat *format, PtFlagStuff *flags )
{

	*image = 0;

	// get channel names

	vector<string> channels;

	if( formatCount == 1 )
	{
		channels.push_back( "R" );
	}
	else if( formatCount == 3 )
	{
		channels.push_back( "R" );
		channels.push_back( "G" );
		channels.push_back( "B" );
	}
	else if( formatCount == 4 )
	{
		channels.push_back( "R" );
		channels.push_back( "G" );
		channels.push_back( "B" );
		channels.push_back( "A" );
	}
	else
	{
		msg( Msg::Error, "Dspy::imageOpen", "Invalid number of channels!" );
		return PkDspyErrorBadParams;
	}
	for( int i = 0; i < formatCount; i++ )
	{
		format[i].type = PkDspyFloat32 | PkDspyByteOrderNative;
	}

	// process the parameter list. we use some of the parameters to help determine
	// the display and data windows, and the others we convert ready to passed to
	// DisplayDriver::create().

	V2i originalSize( width, height );
	V2i origin( 0 );

	CompoundDataPtr convertedParameters = new CompoundData;

	for( int p = 0; p < paramcount; p++ )
	{
		if ( !strcmp( parameters[p].name, "OriginalSize" ) && parameters[p].UP_VALUETYPE == (char)'i' && parameters[p].UP_VALUECOUNT == (char)2 && parameters[p].nbytes == (int) (parameters[p].UP_VALUECOUNT * sizeof(int)) )
		{
			originalSize.x = static_cast<const int *>(parameters[p].value)[0];
			originalSize.y = static_cast<const int *>(parameters[p].value)[1];
		}
		else if ( !strcmp( parameters[p].name, "origin" ) && parameters[p].UP_VALUETYPE == (char)'i' && parameters[p].UP_VALUECOUNT == (char)2 && parameters[p].nbytes == (int)(parameters[p].UP_VALUECOUNT * sizeof(int)) )
		{
			origin.x = static_cast<const int *>(parameters[p].value)[0];
			origin.y = static_cast<const int *>(parameters[p].value)[1];
		}
		else
		{
			DataPtr newParam;

			if ( !parameters[p].nbytes )
			{
				continue;
			}

			const int *pInt;
			const float *pFloat;
			char const **pChar;

			// generic converter
			switch( parameters[p].UP_VALUETYPE )
			{
			case 'i':
				// sanity check
				if ( parameters[p].nbytes / parameters[p].UP_VALUECOUNT != sizeof(int) )
				{
					msg( Msg::Error, "Dspy::imageOpen", "Invalid int data size" );
					continue;
				}
				pInt = static_cast<const int *>(parameters[p].value);
				if ( parameters[p].UP_VALUECOUNT == 1 )
				{
					newParam = new IntData( pInt[0] );
				}
				else
				{
					std::vector< int > newVec( pInt, pInt + parameters[p].UP_VALUECOUNT );
					newParam = new IntVectorData( newVec );
				}
				break;
			case 'f':
				if ( parameters[p].nbytes / parameters[p].UP_VALUECOUNT != sizeof(float) )
				{
					msg( Msg::Error, "Dspy::imageOpen", "Invalid float data size" );
					continue;
				}
				pFloat = static_cast<const float *>(parameters[p].value);
				if ( parameters[p].UP_VALUECOUNT == 1 )
				{
					newParam = new FloatData( pFloat[0] );
				}
				else
				{
					std::vector< float > newVec( pFloat, pFloat + parameters[p].UP_VALUECOUNT );
					newParam = new FloatVectorData( newVec );
				}
				break;
			case 's':
				if ( parameters[p].nbytes / parameters[p].UP_VALUECOUNT != sizeof(char*) )
				{
					msg( Msg::Error, "Dspy::imageOpen", "Invalid string data size" );
					continue;
				}
				pChar = (const char **)(parameters[p].value);
				if ( parameters[p].UP_VALUECOUNT == 1 )
				{
					newParam = new StringData( pChar[0] );
				}
				else
				{
					StringVectorDataPtr newStringVec = new StringVectorData();
					for ( int s = 0; s < parameters[p].UP_VALUECOUNT; s++ )
					{
						newStringVec->writable().push_back( pChar[s] );
					}
					newParam = newStringVec;
				}
				break;
			default :
				// we shouldn't ever get here...
				break;
			}
			if( newParam )
			{
				convertedParameters->writable()[ parameters[p].name ] = newParam;
			}
		}
	}

	convertedParameters->writable()[ "fileName" ] = new StringData( fileName );

	// calculate display and data windows

	Box2i displayWindow(
		V2i( 0 ),
		originalSize - V2i( 1 )
	);

	Box2i dataWindow(
		origin,
		origin + V2i( width - 1, height - 1)
	);

	// create the display driver

	IECore::DisplayDriverPtr dd = 0;
	try
	{
		const StringData *driverType = convertedParameters->member<StringData>( "driverType", true /* throw if missing */ );
		dd = DisplayDriver::create( driverType->readable(), displayWindow, dataWindow, channels, convertedParameters );
	}
	catch( std::exception &e )
	{
		msg( Msg::Error, "Dspy::imageOpen", e.what() );
		return PkDspyErrorUnsupported;
	}

	if( !dd )
	{
		msg( Msg::Error, "Dspy::imageOpen", "DisplayDriver::create returned 0." );
		return PkDspyErrorUnsupported;
	}

	// update flags and return

	if( dd->scanLineOrderOnly() )
	{
		flags->flags |= PkDspyFlagsWantsScanLineOrder;
	}

	dd->addRef(); // this will be removed in imageClose()
	*image = (PtDspyImageHandle)dd.get();
	return PkDspyErrorNone;

}

PtDspyError Dspy::imageQuery( PtDspyImageHandle image, PtDspyQueryType type, int size, void *data )
{
	return PkDspyErrorUnsupported;
}

PtDspyError Dspy::imageData( PtDspyImageHandle image, int xMin, int xMaxPlusOne, int yMin, int yMaxPlusOne, int entrySize, const unsigned char *data )
{
	DisplayDriver *dd = static_cast<DisplayDriver *>( image );
	Box2i dataWindow = dd->dataWindow();

	// convert coordinates from cropped image to original image coordinates.
	Box2i box( V2i( xMin + dataWindow.min.x, yMin + dataWindow.min.y ), V2i( xMaxPlusOne - 1 + dataWindow.min.x, yMaxPlusOne - 1 + dataWindow.min.y ) );
	int channels = dd->channelNames().size();
	int blockSize = (xMaxPlusOne - xMin) * (yMaxPlusOne - yMin);
	int bufferSize = channels * blockSize;

	if( entrySize % sizeof(float) )
	{
		msg( Msg::Error, "Dspy::imageData", "The entry size is not multiple of sizeof(float)!" );
		return PkDspyErrorUnsupported;
	}

	if( entrySize == (int)(channels*sizeof(float)) )
	{
		try
		{
			dd->imageData( box, (const float *)data, bufferSize );
		}
		catch( std::exception &e )
		{
			if( strcmp( e.what(), "stop" ) == 0 )
			{
				/// \todo I would prefer DisplayDriver::imageData to have a return
				/// value which could be used to request stop/continue behaviour.
				/// prman doesn't seem to support PkDspyErrorStop, which should
				/// also be resolved at some point.
#ifdef PRMANEXPORT
				return PkDspyErrorUndefined;
#else
				return PkDspyErrorStop;
#endif
			}
			else
			{
				msg( Msg::Error, "Dspy::imageData", e.what() );
				return PkDspyErrorUndefined;
			}
		}
	}
	else
	{
		msg( Msg::Error, "Dspy::imageData", "Unexpected entry size value!" );
		return PkDspyErrorBadParams;
	}
	return PkDspyErrorNone;
}

PtDspyError Dspy::imageClose( PtDspyImageHandle image )
{
	if ( !image )
	{
		return PkDspyErrorNone;
	}

	DisplayDriver *dd = static_cast<DisplayDriver*>( image );
	try
	{
		dd->imageClose();
	}
	catch( std::exception &e )
	{
		msg( Msg::Error, "Dspy::imageClose", e.what() );
	}

	try
	{
		dd->removeRef();
	}
	catch( std::exception &e )
	{
		msg( Msg::Error, "DspyImageData", e.what() );
		return PkDspyErrorBadParams;
	}

	return PkDspyErrorNone;
}
<|endoftext|>


<commit_before>
#include "IntegrateTFDH.h"

#include "Element.h"
#include "PlasmaFunctions.h"
#include "PlasmaState.h"
#include "PhysicalConstants.h"
#include "RadialFunction.h"

#include <cmath>
#include <vector>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_odeiv2.h>

#include <iostream>

namespace {

  struct TfdhParams {
    const Element& e;
    const PlasmaState& p;
    TfdhParams(const Element& re, const PlasmaState& rp) : e(re), p(rp) {}
  };

  int tfdhOde(double r, const double f[], double dfdr[], void *params) {
    const double& qe = PhysicalConstantsCGS::ElectronCharge;
    const double phi = qe*f[0]/r;
    const PlasmaState& p = static_cast<TfdhParams*>(params)->p;
    const double ne = Plasma::ne(phi, p);
    const double ionChargeDensity = Plasma::totalIonChargeDensity(phi, p);
    dfdr[0] = f[1];
    dfdr[1] = -4.0*M_PI*qe * r * (ionChargeDensity - ne);
    return 0;
  }
}

RadialFunction TFDH::solve(const Element& e, const PlasmaState& p)
{
  // set parameters here:
  const double ri = 1e-10;
  const double rf = 1e-6;

  const double dv0 = rootfindPotential(e, p, ri, rf);
  return integrateODE(e, p, ri, rf, dv0);
}

RadialFunction TFDH::integrateODE(const Element& e, const PlasmaState& p,
    const double r_init, const double r_final, const double dv0)
{
  const double dr_start = r_init;
  const double eps_abs = 1e-6;
  const double eps_rel = 0;
  TfdhParams params(e,p);
  gsl_odeiv2_system sys = {tfdhOde, nullptr, 2, &params};
  gsl_odeiv2_driver* d = gsl_odeiv2_driver_alloc_y_new(&sys,
      gsl_odeiv2_step_rk8pd, dr_start, eps_abs, eps_rel);

  // TODO -- clean all this up
  const double& qe = PhysicalConstantsCGS::ElectronCharge;
  double r = r_init;
  double solution[2] = {qe*e.Z + r_init*dv0, dv0};

  std::vector<double> radii, potentials;

  const int numsteps = 100;
  for (int i=0; i<numsteps; ++i) {
    const double ri = r_init + (i+1)*(r_final-r_init)/numsteps;
    const int status = gsl_odeiv2_driver_apply(d, &r, ri, solution);
    assert(status==GSL_SUCCESS);
    radii.push_back(ri);
    potentials.push_back(solution[0]/ri);
  }

  gsl_odeiv2_driver_free(d);
  return RadialFunction(radii, potentials);
}

double TFDH::rootfindPotential(const Element& e, const PlasmaState& p,
    const double r_init, const double r_final)
{
  // find interval that brackets correct potential
  const double dv = 1.0;
  double v0 = 0;
  double v1 = 0;

  const int num_tries = 100;
  for (int i=0; i<num_tries; ++i) {
    const auto& rf = integrateODE(e, p, r_init, r_final, v0);
    if (rf.data.back() < 0.0) break;
    v1 = v0;
    v0 -= dv;
  }

  // guess a central potential
  double guess_dv0 = 1;

  // iterate guess
  // avoid fancy rootfinders here, because we aren't dealing with a smooth
  // function and its root -- rather, we are trying to find the boundary
  // between two distinct sets: dv0 too large, dv0 too small
}

<commit_msg>Implement potential rootfind<commit_after>
#include "IntegrateTFDH.h"

#include "Element.h"
#include "PlasmaFunctions.h"
#include "PlasmaState.h"
#include "PhysicalConstants.h"
#include "RadialFunction.h"

#include <cmath>
#include <vector>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_odeiv2.h>

#include <iostream>

namespace {

  struct TfdhParams {
    const Element& e;
    const PlasmaState& p;
    TfdhParams(const Element& re, const PlasmaState& rp) : e(re), p(rp) {}
  };

  int tfdhOde(double r, const double f[], double dfdr[], void *params) {
    const double& qe = PhysicalConstantsCGS::ElectronCharge;
    const double phi = qe*f[0]/r;
    const PlasmaState& p = static_cast<TfdhParams*>(params)->p;
    const double ne = Plasma::ne(phi, p);
    const double ionChargeDensity = Plasma::totalIonChargeDensity(phi, p);
    dfdr[0] = f[1];
    dfdr[1] = -4.0*M_PI*qe * r * (ionChargeDensity - ne);
    return 0;
  }
}

RadialFunction TFDH::solve(const Element& e, const PlasmaState& p)
{
  // set parameters here:
  const double ri = 1e-10;
  const double rf = 1e-6;

  const double dv0 = rootfindPotential(e, p, ri, rf);
  return integrateODE(e, p, ri, rf, dv0);
}

RadialFunction TFDH::integrateODE(const Element& e, const PlasmaState& p,
    const double r_init, const double r_final, const double dv0)
{
  const double dr_start = r_init;
  const double eps_abs = 1e-6;
  const double eps_rel = 0;
  TfdhParams params(e,p);
  gsl_odeiv2_system sys = {tfdhOde, nullptr, 2, &params};
  gsl_odeiv2_driver* d = gsl_odeiv2_driver_alloc_y_new(&sys,
      gsl_odeiv2_step_rk8pd, dr_start, eps_abs, eps_rel);

  // TODO -- clean all this up
  const double& qe = PhysicalConstantsCGS::ElectronCharge;
  double r = r_init;
  double solution[2] = {qe*e.Z + r_init*dv0, dv0};

  std::vector<double> radii, potentials;

  const int numsteps = 100;
  for (int i=0; i<numsteps; ++i) {
    const double ri = r_init + (i+1)*(r_final-r_init)/numsteps;
    const int status = gsl_odeiv2_driver_apply(d, &r, ri, solution);
    assert(status==GSL_SUCCESS);
    radii.push_back(ri);
    potentials.push_back(solution[0]/ri);
  }

  gsl_odeiv2_driver_free(d);
  return RadialFunction(radii, potentials);
}

double TFDH::rootfindPotential(const Element& e, const PlasmaState& p,
    const double r_init, const double r_final)
{
  // find interval that brackets correct potential
  const double dv = 1.0;
  double v0 = 0;
  double v1 = 0;

  const int num_tries = 100;
  for (int i=0; i<num_tries; ++i) {
    const auto& rf = integrateODE(e, p, r_init, r_final, v0);
    if (rf.data.back() < 0.0) break;
    v1 = v0;
    v0 -= dv;
  }

  // iterate guess
  // avoid fancy rootfinders here, because we aren't dealing with a smooth
  // function and its root -- rather, we are trying to find the boundary
  // between two distinct sets: dv0 too large, dv0 too small
  const int num_root_tries = 100;
  double v_try;
  for (int i=0; i<num_root_tries; ++i) {
    v_try = (v0+v1)/2.0;
    if (v_try==v0 or v_try==v1) // underflow
      return v_try;

    const auto& rf = integrateODE(e, p, r_init, r_final, v_try);
    if (rf.data.back() >= 0.0) {
      v1 = v_try;
    } else {
      v0 = v_try;
    }
  }

  return v_try;
}

<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE Emwxnet
#include "../Data.h"
#include "../Loggers/Default.h"
#include "../Loggers/Logger.h"
#include "../Global.h"
#include "../Member.h"
#include "../Field.h"
#include "../Location.h"
#include "../Parameters.h"
#include "../ParameterIos/ParameterIo.h"
#include "../Configurations/Configuration.h"
#include <gtest/gtest.h>

namespace {
   class EmwxnetTest : public ::testing::Test {
      protected:
         EmwxnetTest() {
            // You can do set-up work for each test here.
            mData = new Data("test.emwxnet");
            Options opt("tag=parEmwxnet class=ParameterIoEmwxnet finder=finder");
            mParIo = ParameterIo::getScheme(opt, *mData);
         }
         virtual ~EmwxnetTest() {
            delete mData;
            delete mParIo;
            // You can do clean-up work that doesn't throw exceptions here.
         }
         virtual void SetUp() {
            // Code here will be called immediately after the constructor (right
            // before each test).
         }

         virtual void TearDown() {
            // Code here will be called immediately after each test (right
            // before the destructor).
         }
         Data* mData;
         ParameterIo* mParIo ;
   };

   TEST_F(EmwxnetTest, write) {
      int date = 20120101;
      int init = 0;
      float offset = 0;
      int index = 0;
      Location location("naefs", 415);
      std::string variable = "TMin24";
      Configuration conf("confRaw", *mData);

      std::vector<float> values;
      values.push_back(1);
      values.push_back(4.3);
      Parameters par(values);
      mParIo->add(Component::TypeCorrector, date, init, offset, location, variable, conf, index, par);
      mParIo->write();
   }
   TEST_F(EmwxnetTest, read) {
      int date = 20120101;
      int init = 0;
      float offset = 0;
      int index = 0;
      Location location("naefs", 415);
      std::string variable = "TMin24";
      Configuration conf("confRaw", *mData);

      Parameters par;
      mParIo->read(Component::TypeCorrector, date, init, offset, location, variable, conf, index, par);
      ASSERT_EQ(par.size(), 2);
      EXPECT_FLOAT_EQ(par[0], 1);
      EXPECT_FLOAT_EQ(par[1], 4.3);

      mParIo->read(Component::TypeSelector, date, init, offset, location, variable, conf, index, par);
      EXPECT_EQ(par.size(), 0);
   }
}
int main(int argc, char **argv) {
     ::testing::InitGoogleTest(&argc, argv);
       return RUN_ALL_TESTS();
}
<commit_msg>Remove Emwxnet test<commit_after><|endoftext|>


<commit_before>// Timer.cpp
// Implements the Timer class

#include "Timer.h"

#include <string>
#include <iostream>

#include "InputXML.h"
#include "CycException.h"
#include "Logger.h"
#include "Material.h"

using namespace std;

Timer* Timer::instance_ = 0;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::runSim() {
  CLOG(LEV_INFO1) << "Simulation set to run from start="
                  << startDate_ << " to end=" << endDate_;

  time_ = -1;
  handlePreHistory();
  time_ = time0_;
  CLOG(LEV_INFO1) << "Beginning simulation";
  while (date_ < endDate()){
    if (date_.day() == 1){
      CLOG(LEV_INFO2) << "Current date: " << date_ << " Current time: " << time_ << " {";
      CLOG(LEV_DEBUG3) << "The list of current tick listeners is: " << reportListeners();

      //Material::decayMaterials(time_);
      sendTick();
      sendResolve();
    }

    int eom_day = lastDayOfMonth();
    for (int i = 1; i < eom_day+1; i++){
      sendDailyTasks();
      if (i == eom_day){
        sendTock();
        CLOG(LEV_INFO2) << "}";
      }
      date_ += boost::gregorian::days(1);
    }

    time_++;
  }

  // initiate deletion of models that don't have parents.
  // dealloc will propogate through hierarchy as models delete their children
  int count = 0;
  while (Model::getModelList().size() > 0) {
    Model* model = Model::getModelList().at(count);
    try {
      model->parent();
    } catch (CycIndexException err) {
      delete model;
      count = 0;
      continue;
    }
    count++;
  }
}

int Timer::lastDayOfMonth(){
  int lastDay =
    boost::gregorian::gregorian_calendar::end_of_month_day(date_.year(),
							   date_.month());
  return lastDay;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Timer::reportListeners() {
  string report = "";
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
      agent != tick_listeners_.end();
      agent++) {
    report += (*agent)->name() + " ";
  }
  return report;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::handlePreHistory() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handlePreHistory();
    } catch(CycException err) {
      LOG(LEV_ERROR, "none!") << "ERROR occured in handlePreHistory(): " << err.what();
    }
  }
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendResolve() {
  for(vector<MarketModel*>::iterator agent=resolve_listeners_.begin();
       agent != resolve_listeners_.end();
       agent++) {
    try {
      CLOG(LEV_INFO3) << "Sending resolve to Model ID=" << (*agent)->ID()
                      << ", name=" << (*agent)->name() << " {";
      (*agent)->resolve();
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendResolve(): " << err.what();
    }
    CLOG(LEV_INFO3) << "}";
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTick() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      CLOG(LEV_INFO3) << "Sending tick to Model ID=" << (*agent)->ID()
                      << ", name=" << (*agent)->name() << " {";
      (*agent)->handleTick(time_);
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendTick(): " << err.what();
    }
    CLOG(LEV_INFO3) << "}";
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTock() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      CLOG(LEV_INFO3) << "Sending tock to Model ID=" << (*agent)->ID()
                      << ", name=" << (*agent)->name() << " {";
      (*agent)->handleTock(time_);
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendTock(): " << err.what();
    }
    CLOG(LEV_INFO3) << "}";
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendDailyTasks() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleDailyTasks(time_,date_.day());
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendDailyTasks(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerTickListener(TimeAgent* agent) {
  CLOG(LEV_INFO2) << "Model ID=" << agent->ID() << ", name=" << agent->name()
                  << " has registered to receive 'ticks' and 'tocks'.";
  tick_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerResolveListener(MarketModel* agent) {
  CLOG(LEV_INFO2) << "Model ID=" << agent->ID() << ", name=" << agent->name()
                  << " has registered to receive 'resolves'.";
  resolve_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::time() {
  return time_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer* Timer::Instance() {
  // If we haven't created a Timer yet, create it, and then and return it
  // either way.
  if (0 == instance_) {
    instance_ = new Timer();
    instance_->initialize();
  }

  return instance_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::initialize(int dur, int m0, int y0, int start, int decay) {

  if (m0 < 1 || m0 > 12)
    throw CycRangeException("Invalid month0; must be between 1 and 12 (inclusive).");

  if (y0 < 1942)
    throw CycRangeException("Invalid year0; the first man-made nuclear reactor was build in 1942");

  if (y0 > 2063)
    throw CycRangeException("Invalid year0; why start a simulation after we've got warp drive?: http://en.wikipedia.org/wiki/Warp_drive#Development_of_the_backstory");

  if (decay > dur)
    throw CycRangeException("Invalid decay interval; no decay occurs if the interval is greater than the simulation duriation. For no decay, use -1 .");
  //Material::setDecay(decay);

  month0_ = m0;
  year0_ = y0;

  time0_ = start;
  time_ = start;
  simDur_ = dur;

  startDate_ = boost::gregorian::date(year0_,month0_,1);
  endDate_ = getEndDate(startDate_,simDur_);
  date_ = boost::gregorian::date(startDate_);

}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
boost::gregorian::date Timer::getEndDate(boost::gregorian::date startDate, int simDur_){
  boost::gregorian::date endDate(startDate);
  endDate += boost::gregorian::months(simDur_-1);
  endDate += boost::gregorian::days(boost::gregorian::gregorian_calendar::end_of_month_day(endDate.year(),endDate.month())-1);
  return endDate;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::simDur() {
  return simDur_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer::Timer() {
  time_ = 0;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::convertDate(int month, int year) {
  return (year - year0_) * 12 + (month - month0_) + time0_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<int, int> Timer::convertDate(int time) {
  int month = (time - time0_) % 12 + 1;
  int year = (time - time0_ - (month - 1)) / 12 + year0_;
  return make_pair(month, year);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::load_simulation() {

  int dur, m0, y0, sim0, dec;
  string dur_str, m0_str, y0_str, sim0_str, decay_str;

  xmlNodePtr cur = XMLinput->get_xpath_element("/simulation");
  // get duration
  dur_str = (XMLinput->get_xpath_content(cur,"duration"));
  // get start month
  m0_str = (XMLinput->get_xpath_content(cur,"startmonth"));
  // get start year
  y0_str = (XMLinput->get_xpath_content(cur,"startyear"));
  // get simulation start
  sim0_str = (XMLinput->get_xpath_content(cur,"simstart"));
  // get decay interval
  decay_str = (XMLinput->get_xpath_content(cur,"decay"));

  dur = strtol(dur_str.c_str(), NULL, 10);
  m0 = strtol(m0_str.c_str(), NULL, 10);
  y0 = strtol(y0_str.c_str(), NULL, 10);
  sim0 = strtol(sim0_str.c_str(), NULL, 10);
  dec = strtol(decay_str.c_str(), NULL, 10);

  TI->initialize(dur, m0, y0, sim0, dec);

}

<commit_msg>uncommented the decay-related functions in timer<commit_after>// Timer.cpp
// Implements the Timer class

#include "Timer.h"

#include <string>
#include <iostream>

#include "InputXML.h"
#include "CycException.h"
#include "Logger.h"
#include "Material.h"

using namespace std;

Timer* Timer::instance_ = 0;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::runSim() {
  CLOG(LEV_INFO1) << "Simulation set to run from start="
                  << startDate_ << " to end=" << endDate_;

  time_ = -1;
  handlePreHistory();
  time_ = time0_;
  CLOG(LEV_INFO1) << "Beginning simulation";
  while (date_ < endDate()){
    if (date_.day() == 1){
      CLOG(LEV_INFO2) << "Current date: " << date_ << " Current time: " << time_ << " {";
      CLOG(LEV_DEBUG3) << "The list of current tick listeners is: " << reportListeners();

      Material::decayMaterials(time_);
      sendTick();
      sendResolve();
    }

    int eom_day = lastDayOfMonth();
    for (int i = 1; i < eom_day+1; i++){
      sendDailyTasks();
      if (i == eom_day){
        sendTock();
        CLOG(LEV_INFO2) << "}";
      }
      date_ += boost::gregorian::days(1);
    }

    time_++;
  }

  // initiate deletion of models that don't have parents.
  // dealloc will propogate through hierarchy as models delete their children
  int count = 0;
  while (Model::getModelList().size() > 0) {
    Model* model = Model::getModelList().at(count);
    try {
      model->parent();
    } catch (CycIndexException err) {
      delete model;
      count = 0;
      continue;
    }
    count++;
  }
}

int Timer::lastDayOfMonth(){
  int lastDay =
    boost::gregorian::gregorian_calendar::end_of_month_day(date_.year(),
							   date_.month());
  return lastDay;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Timer::reportListeners() {
  string report = "";
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
      agent != tick_listeners_.end();
      agent++) {
    report += (*agent)->name() + " ";
  }
  return report;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::handlePreHistory() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handlePreHistory();
    } catch(CycException err) {
      LOG(LEV_ERROR, "none!") << "ERROR occured in handlePreHistory(): " << err.what();
    }
  }
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendResolve() {
  for(vector<MarketModel*>::iterator agent=resolve_listeners_.begin();
       agent != resolve_listeners_.end();
       agent++) {
    try {
      CLOG(LEV_INFO3) << "Sending resolve to Model ID=" << (*agent)->ID()
                      << ", name=" << (*agent)->name() << " {";
      (*agent)->resolve();
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendResolve(): " << err.what();
    }
    CLOG(LEV_INFO3) << "}";
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTick() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      CLOG(LEV_INFO3) << "Sending tick to Model ID=" << (*agent)->ID()
                      << ", name=" << (*agent)->name() << " {";
      (*agent)->handleTick(time_);
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendTick(): " << err.what();
    }
    CLOG(LEV_INFO3) << "}";
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendTock() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      CLOG(LEV_INFO3) << "Sending tock to Model ID=" << (*agent)->ID()
                      << ", name=" << (*agent)->name() << " {";
      (*agent)->handleTock(time_);
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendTock(): " << err.what();
    }
    CLOG(LEV_INFO3) << "}";
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::sendDailyTasks() {
  for(vector<TimeAgent*>::iterator agent=tick_listeners_.begin();
       agent != tick_listeners_.end();
       agent++) {
    try {
      (*agent)->handleDailyTasks(time_,date_.day());
    } catch(CycException err) {
      CLOG(LEV_ERROR) << "ERROR occured in sendDailyTasks(): " << err.what();
    }
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerTickListener(TimeAgent* agent) {
  CLOG(LEV_INFO2) << "Model ID=" << agent->ID() << ", name=" << agent->name()
                  << " has registered to receive 'ticks' and 'tocks'.";
  tick_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::registerResolveListener(MarketModel* agent) {
  CLOG(LEV_INFO2) << "Model ID=" << agent->ID() << ", name=" << agent->name()
                  << " has registered to receive 'resolves'.";
  resolve_listeners_.push_back(agent);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::time() {
  return time_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer* Timer::Instance() {
  // If we haven't created a Timer yet, create it, and then and return it
  // either way.
  if (0 == instance_) {
    instance_ = new Timer();
    instance_->initialize();
  }

  return instance_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::initialize(int dur, int m0, int y0, int start, int decay) {

  if (m0 < 1 || m0 > 12)
    throw CycRangeException("Invalid month0; must be between 1 and 12 (inclusive).");

  if (y0 < 1942)
    throw CycRangeException("Invalid year0; the first man-made nuclear reactor was build in 1942");

  if (y0 > 2063)
    throw CycRangeException("Invalid year0; why start a simulation after we've got warp drive?: http://en.wikipedia.org/wiki/Warp_drive#Development_of_the_backstory");

  if (decay > dur)
    throw CycRangeException("Invalid decay interval; no decay occurs if the interval is greater than the simulation duriation. For no decay, use -1 .");
  Material::setDecay(decay);

  month0_ = m0;
  year0_ = y0;

  time0_ = start;
  time_ = start;
  simDur_ = dur;

  startDate_ = boost::gregorian::date(year0_,month0_,1);
  endDate_ = getEndDate(startDate_,simDur_);
  date_ = boost::gregorian::date(startDate_);

}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
boost::gregorian::date Timer::getEndDate(boost::gregorian::date startDate, int simDur_){
  boost::gregorian::date endDate(startDate);
  endDate += boost::gregorian::months(simDur_-1);
  endDate += boost::gregorian::days(boost::gregorian::gregorian_calendar::end_of_month_day(endDate.year(),endDate.month())-1);
  return endDate;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::simDur() {
  return simDur_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Timer::Timer() {
  time_ = 0;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Timer::convertDate(int month, int year) {
  return (year - year0_) * 12 + (month - month0_) + time0_;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<int, int> Timer::convertDate(int time) {
  int month = (time - time0_) % 12 + 1;
  int year = (time - time0_ - (month - 1)) / 12 + year0_;
  return make_pair(month, year);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Timer::load_simulation() {

  int dur, m0, y0, sim0, dec;
  string dur_str, m0_str, y0_str, sim0_str, decay_str;

  xmlNodePtr cur = XMLinput->get_xpath_element("/simulation");
  // get duration
  dur_str = (XMLinput->get_xpath_content(cur,"duration"));
  // get start month
  m0_str = (XMLinput->get_xpath_content(cur,"startmonth"));
  // get start year
  y0_str = (XMLinput->get_xpath_content(cur,"startyear"));
  // get simulation start
  sim0_str = (XMLinput->get_xpath_content(cur,"simstart"));
  // get decay interval
  decay_str = (XMLinput->get_xpath_content(cur,"decay"));

  dur = strtol(dur_str.c_str(), NULL, 10);
  m0 = strtol(m0_str.c_str(), NULL, 10);
  y0 = strtol(y0_str.c_str(), NULL, 10);
  sim0 = strtol(sim0_str.c_str(), NULL, 10);
  dec = strtol(decay_str.c_str(), NULL, 10);

  TI->initialize(dur, m0, y0, sim0, dec);

}

<|endoftext|>


<commit_before>#include "WinHTTPClient.h"

#include "BasicAuth.h"

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cctype>

#if defined(WIN32) || defined(WIN64)
#include <windows.h>
#include <sysinfoapi.h>

#define strcasecmp _stricmp
#endif

#include <iostream>
#include <URI.h>
#include <winhttp.h>

using namespace std;

static wstring from_utf8(const std::string & s) {
  std::unique_ptr<wchar_t[]> tmp(new wchar_t[s.size() + 1]);
  auto r = MultiByteToWideChar(CP_UTF8, 0, s.data(), -1, tmp.get(), s.size() + 1);
  return wstring(tmp.get(), static_cast<size_t>(r));
}

static string to_utf8(const wstring & s) {
  std::unique_ptr<char[]> tmp(new char[4 * s.size() + 1]);
  auto r = WideCharToMultiByte(CP_UTF8,
			       0,
			       s.data(),
			       -1,
			       tmp.get(),
			       4*s.size() + 1,
			       NULL,
			       NULL);
  return string(tmp.get(), static_cast<size_t>(r));
}

static long long get_current_time_ms() {
#ifdef WIN32
  FILETIME ft_now;
  GetSystemTimeAsFileTime(&ft_now);
  long long ll_now = (LONGLONG)ft_now.dwLowDateTime + (((LONGLONG)ft_now.dwHighDateTime) << 32LL);
  ll_now /= 10000;
  return ll_now - 116444736000000000LL;
#else
  struct timeval tv;
  int r = gettimeofday(&tv, 0);
  if (r == 0) {
    return (long long)1000 * tv.tv_sec + tv.tv_usec / 1000;
  } else {
    return 0;
  }
#endif
}

class WinHTTPClient : public HTTPClient {
 public:
  WinHTTPClient(const std::string & user_agent, bool enable_cookies = true, bool enable_keepalive = true)
    : HTTPClient(user_agent, enable_cookies, enable_keepalive)
  {
  }

  ~WinHTTPClient() {
  }

  void request(const HTTPRequest & req, const Authorization & auth, HTTPClientInterface & callback) override {
    URI uri(req.getURI());

    auto ua_text = from_utf8(getUserAgent());
    auto target_scheme = uri.getScheme();
    auto target_host = from_utf8(uri.getDomain());
    auto target_port = uri.getPort();
    auto target_path = from_utf8(uri.getPath());

    bool is_secure = false;
    if (!target_port) {
      if (target_scheme == "http") {
	target_port = INTERNET_DEFAULT_HTTP_PORT;
      } else if (target_scheme == "https") {
	target_port = INTERNET_DEFAULT_HTTPS_PORT;
	is_secure = true;
      } else {
	return;
      }
    }

    // Use WinHttpOpen to obtain a session handle.
    auto hSession = WinHttpOpen(ua_text.c_str(),
				WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
				WINHTTP_NO_PROXY_NAME,
				WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession) {
      // Specify an HTTP server.
      auto hConnect = WinHttpConnect(hSession, target_host.c_str(), target_port, 0);
      if (hConnect) {
	wstring request_type;
	bool has_data = true;
	switch (req.getType()) {
	case HTTPRequest::GET:
	  request_type = L"GET";
	  has_data = false;
	  break;
	case HTTPRequest::POST:
	  request_type = L"POST";
	  break;
	case HTTPRequest::OPTIONS:
	  request_type = L"OPTIONS";
	  break;
	}

	DWORD dwFlags = WINHTTP_FLAG_ESCAPE_DISABLE | WINHTTP_FLAG_ESCAPE_DISABLE_QUERY;
	if (is_secure) dwFlags |= WINHTTP_FLAG_SECURE;

	auto hRequest = WinHttpOpenRequest(hConnect, request_type.c_str(), target_path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags);
	if (hRequest) {
	  map<string, string> combined_headers;
	  for (auto & hd : default_headers) {
	    combined_headers[hd.first] = hd.second;
	  }
	  for (auto & hd : req.getHeaders()) {
	    combined_headers[hd.first] = hd.second;
	  }

	  if (req.getType() == HTTPRequest::POST) {
	    if (!req.getContentType().empty()) {
	      combined_headers["Content-Type"] = req.getContentType();
	    }
	  }

	  for (auto & [ name, value ] : combined_headers) {
	    auto header = from_utf8(name + ": " + value + "\r\n");
	    WinHttpAddRequestHeaders(hRequest, header.c_str(), header.length(), WINHTTP_ADDREQ_FLAG_ADD);
	  }

	  DWORD dwSize = 0;
	  DWORD dwDownloaded = 0;
	  LPSTR pszOutBuffer;

	  if (has_data) {
	    dwSize = req.getContent().size();
	  }

	  if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, dwSize, 0) &&
	      (!has_data || WinHttpWriteData(hRequest, req.getContent().data(), dwSize, NULL)) &&
	      WinHttpReceiveResponse(hRequest, NULL)
	      ) {

	    DWORD headerSize = 0;

	    if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &headerSize, WINHTTP_NO_HEADER_INDEX)) {
	      // Allocate buffer by header length
	      // If the function fails and ERROR_INSUFFICIENT_BUFFER is returned, lpdwBufferLength specifies the number of bytes that the application must allocate to receive the string.
	      if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
		unique_ptr<wchar_t[]> lpHeadBuffer(new wchar_t[dwSize / sizeof(wchar_t)]);
		if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, lpHeadBuffer.get(), &headerSize, WINHTTP_NO_HEADER_INDEX)) {

  		}
	      }
	    }

	    callback.handleDisconnect();
	  }

	  WinHttpCloseHandle(hRequest);
	}
	WinHttpCloseHandle(hConnect);
      }
      WinHttpCloseHandle(hSession);
    }
  }

  void clearCookies() override {
  }

 protected:

 };

std::unique_ptr<HTTPClient>
WinHTTPClientFactory::createClient2(const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive) {
  return std::unique_ptr<WinHTTPClient>(new WinHTTPClient(_user_agent, _enable_cookies, _enable_keepalive));
}
<commit_msg>add data reception<commit_after>#include "WinHTTPClient.h"

#include "BasicAuth.h"

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cctype>

#if defined(WIN32) || defined(WIN64)
#include <windows.h>
#include <sysinfoapi.h>

#define strcasecmp _stricmp
#endif

#include <iostream>
#include <URI.h>
#include <winhttp.h>

using namespace std;

static wstring from_utf8(const std::string & s) {
  std::unique_ptr<wchar_t[]> tmp(new wchar_t[s.size() + 1]);
  auto r = MultiByteToWideChar(CP_UTF8, 0, s.data(), -1, tmp.get(), s.size() + 1);
  return wstring(tmp.get(), static_cast<size_t>(r));
}

static string to_utf8(const wstring & s) {
  std::unique_ptr<char[]> tmp(new char[4 * s.size() + 1]);
  auto r = WideCharToMultiByte(CP_UTF8,
			       0,
			       s.data(),
			       -1,
			       tmp.get(),
			       4*s.size() + 1,
			       NULL,
			       NULL);
  return string(tmp.get(), static_cast<size_t>(r));
}

static long long get_current_time_ms() {
#ifdef WIN32
  FILETIME ft_now;
  GetSystemTimeAsFileTime(&ft_now);
  long long ll_now = (LONGLONG)ft_now.dwLowDateTime + (((LONGLONG)ft_now.dwHighDateTime) << 32LL);
  ll_now /= 10000;
  return ll_now - 116444736000000000LL;
#else
  struct timeval tv;
  int r = gettimeofday(&tv, 0);
  if (r == 0) {
    return (long long)1000 * tv.tv_sec + tv.tv_usec / 1000;
  } else {
    return 0;
  }
#endif
}

class WinHTTPClient : public HTTPClient {
 public:
  WinHTTPClient(const std::string & user_agent, bool enable_cookies = true, bool enable_keepalive = true)
    : HTTPClient(user_agent, enable_cookies, enable_keepalive)
  {
  }

  ~WinHTTPClient() {
  }

  void request(const HTTPRequest & req, const Authorization & auth, HTTPClientInterface & callback) override {
    URI uri(req.getURI());

    auto ua_text = from_utf8(getUserAgent());
    auto target_scheme = uri.getScheme();
    auto target_host = from_utf8(uri.getDomain());
    auto target_port = uri.getPort();
    auto target_path = from_utf8(uri.getPath());

    bool is_secure = false;
    if (!target_port) {
      if (target_scheme == "http") {
	target_port = INTERNET_DEFAULT_HTTP_PORT;
      } else if (target_scheme == "https") {
	target_port = INTERNET_DEFAULT_HTTPS_PORT;
	is_secure = true;
      } else {
	return;
      }
    }

    // Use WinHttpOpen to obtain a session handle.
    auto hSession = WinHttpOpen(ua_text.c_str(),
				WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
				WINHTTP_NO_PROXY_NAME,
				WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession) {
      // Specify an HTTP server.
      auto hConnect = WinHttpConnect(hSession, target_host.c_str(), target_port, 0);
      if (hConnect) {
	wstring request_type;
	bool has_data = true;
	switch (req.getType()) {
	case HTTPRequest::GET:
	  request_type = L"GET";
	  has_data = false;
	  break;
	case HTTPRequest::POST:
	  request_type = L"POST";
	  break;
	case HTTPRequest::OPTIONS:
	  request_type = L"OPTIONS";
	  break;
	}

	DWORD dwFlags = WINHTTP_FLAG_ESCAPE_DISABLE | WINHTTP_FLAG_ESCAPE_DISABLE_QUERY;
	if (is_secure) dwFlags |= WINHTTP_FLAG_SECURE;

	auto hRequest = WinHttpOpenRequest(hConnect, request_type.c_str(), target_path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags);
	if (hRequest) {
	  map<string, string> combined_headers;
	  for (auto & hd : default_headers) {
	    combined_headers[hd.first] = hd.second;
	  }
	  for (auto & hd : req.getHeaders()) {
	    combined_headers[hd.first] = hd.second;
	  }

	  if (req.getType() == HTTPRequest::POST) {
	    if (!req.getContentType().empty()) {
	      combined_headers["Content-Type"] = req.getContentType();
	    }
	  }

	  for (auto & [ name, value ] : combined_headers) {
	    auto header = from_utf8(name + ": " + value + "\r\n");
	    WinHttpAddRequestHeaders(hRequest, header.c_str(), header.length(), WINHTTP_ADDREQ_FLAG_ADD);
	  }

	  DWORD dwSize = 0;

	  if (has_data) {
	    dwSize = req.getContent().size();
	  }

	  if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, dwSize, 0) &&
	      (!has_data || WinHttpWriteData(hRequest, req.getContent().data(), dwSize, NULL)) &&
	      WinHttpReceiveResponse(hRequest, NULL)
	      ) {

	    DWORD headerSize = 0;

	    DWORD dwStatusCode = 0;
	    DWORD dwSize = sizeof(dwStatusCode);

	    if (WinHttpQueryHeaders(hRequest,
				    WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
				    WINHTTP_HEADER_NAME_BY_INDEX,
				    &dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX)) {
	      callback->handleResultCode(dwStatusCode);
	    }

	    if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &headerSize, WINHTTP_NO_HEADER_INDEX)) {
	      // Allocate buffer by header length
	      // If the function fails and ERROR_INSUFFICIENT_BUFFER is returned, lpdwBufferLength specifies the number of bytes that the application must allocate to receive the string.
	      if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
		unique_ptr<wchar_t[]> lpHeadBuffer(new wchar_t[dwSize / sizeof(wchar_t)]);
		if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, lpHeadBuffer.get(), &headerSize, WINHTTP_NO_HEADER_INDEX)) {

  		}
	      }
	    }

	    while ( 1 ) {
	      // Check for available data to get data size in bytes
	      dwSize = 0;
	      if (!WinHttpQueryDataAvailable(hRequest, &dwSize)){
                bResults = FALSE;
                break;
	      }
	      if (!dwSize) {
                break;  //data size 0
	      }

	      // Allocate buffer by data size
	      unique_ptr<char[]> pszOutBuffer(new char[dwSize + 1]);
	      // Read data from server
	      DWORD dwDownloaded = 0;
	      if (WinHttpReadData(hRequest, pszOutBuffer, dwSize, &dwDownloaded)) {
		bool r = callback.handleChunk(dwDownloaded, outBuffer.get());
		if (!r) break;
	      } else {
		break;
	      }

	      if (!dwDownloaded) break;
	    }

	    callback.handleDisconnect();
	  }

	  WinHttpCloseHandle(hRequest);
	}
	WinHttpCloseHandle(hConnect);
      }
      WinHttpCloseHandle(hSession);
    }
  }

  void clearCookies() override {
  }

 protected:

 };

std::unique_ptr<HTTPClient>
WinHTTPClientFactory::createClient2(const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive) {
  return std::unique_ptr<WinHTTPClient>(new WinHTTPClient(_user_agent, _enable_cookies, _enable_keepalive));
}
<|endoftext|>