//
// Copyright (c) 2009, Markus Rickert
// 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.
//
// 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 HOLDER 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 <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include <random>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <rl/hal/Coach.h>
#include <rl/mdl/Kinematic.h>
#include <rl/mdl/JacobianInverseKinematics.h>
#include <rl/mdl/UrdfFactory.h>
#include <rl/mdl/XmlFactory.h>
#include <rl/math/Unit.h>

#ifdef RL_MDL_NLOPT
#include <rl/mdl/NloptInverseKinematics.h>
#endif

#define COACH

int
main(int argc, char** argv)
{
	if (argc < 2)
	{
		std::cout << "Usage: rlInversePositionDemo MODELFILE Q1 ... Qn" << std::endl;
		return EXIT_FAILURE;
	}

	try
	{
		std::function<rl::math::Real()> rand = std::bind(
			std::uniform_real_distribution<rl::math::Real>(0, 1),
			std::mt19937(std::random_device()())
		);

		std::string filename(argv[1]);
		std::shared_ptr<rl::mdl::Kinematic> kinematic;

		if ("urdf" == filename.substr(filename.length() - 4, 4))
		{
			rl::mdl::UrdfFactory factory;
			kinematic = std::dynamic_pointer_cast<rl::mdl::Kinematic>(factory.create(filename));
		}
		else
		{
			rl::mdl::XmlFactory factory;
			kinematic = std::dynamic_pointer_cast<rl::mdl::Kinematic>(factory.create(filename));
		}

		rl::math::Vector q(kinematic->getDof());

		for (std::ptrdiff_t i = 0; i < q.size(); ++i)
		{
			q(i) = boost::lexical_cast<rl::math::Real>(argv[i + 2]);
		}

		kinematic->setPosition(q);

		kinematic->forwardPosition();
		rl::math::Vector3 position = kinematic->getOperationalPosition(0).translation();
		rl::math::Vector3 orientation = kinematic->getOperationalPosition(0).rotation().eulerAngles(2, 1, 0).reverse();
		std::cout << "x: " << position.x() << " m, y: " << position.y() << " m, z: " << position.z() << " m, a: " << orientation.x() * rl::math::RAD2DEG << " deg, b: " << orientation.y() * rl::math::RAD2DEG << " deg, c: " << orientation.z() * rl::math::RAD2DEG << " deg" << std::endl;

#ifdef RL_MDL_NLOPT
		rl::mdl::NloptInverseKinematics ik(kinematic.get());
		std::cout << "IK using rl::mdl::NloptInverseKinematics";
#else
		rl::mdl::JacobianInverseKinematics ik(kinematic.get());
		std::cout << "IK using rl::mdl::JacobianInverseKinematics";
#endif
		ik.setDuration(std::chrono::seconds(1));
		std::cout << " with timeout of " << std::chrono::duration_cast<std::chrono::milliseconds>(ik.getDuration()).count() << " ms" << std::endl;
		ik.addGoal(kinematic->getOperationalPosition(0), 0);

		rl::math::Vector min = kinematic->getMinimum();
		rl::math::Vector max = kinematic->getMaximum();

		for (std::size_t i = 0; i < kinematic->getDofPosition(); ++i)
		{
			q(i) = min(i) + rand() * (max(i) - min(i));
		}

		kinematic->setPosition(q);

		std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
		bool result = ik.solve();
		std::chrono::steady_clock::time_point stop = std::chrono::steady_clock::now();
		std::cout << (result ? "true" : "false") << " " << std::chrono::duration_cast<std::chrono::duration<double>>(stop - start).count() * 1000 << " ms" << std::endl;

		kinematic->forwardPosition();
		position = kinematic->getOperationalPosition(0).translation();
		orientation = kinematic->getOperationalPosition(0).rotation().eulerAngles(2, 1, 0).reverse();
		std::cout << "x: " << position.x() << " m, y: " << position.y() << " m, z: " << position.z() << " m, a: " << orientation.x() * rl::math::RAD2DEG << " deg, b: " << orientation.y() * rl::math::RAD2DEG << " deg, c: " << orientation.z() * rl::math::RAD2DEG << " deg" << std::endl;

		std::cout << "q: " << kinematic->getPosition().transpose() << std::endl;

#ifdef COACH
		rl::hal::Coach controller(kinematic->getDof(), std::chrono::milliseconds(1), 0, "localhost");
		controller.open();
		controller.start();
		controller.setJointPosition(kinematic->getPosition());
		controller.step();
		controller.stop();
		controller.close();
#endif // COACH
	}
	catch (const std::exception& e)
	{
		std::cout << e.what() << std::endl;
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}


#include <iostream>
#include <fstream>
#include <sstream>

#include <vector>
#include <unordered_map>
#include <tuple>
#include <string>
#include <boost/dynamic_bitset.hpp>

#include <chrono>
#include <time.h>
#include <omp.h>

#include "vertex.hpp"
#include "rule.hpp"
#include "switch.hpp"

#include "topological_sort.hpp"
#include "transitive_closure.hpp"
#include "maximum_matching.hpp"
#include "trace_path.hpp"
#include "test_packet.hpp"
#include "hopcroft_karp.hpp"

#include "utils.hpp"

#define DEBUG false
#define DEBUG_TEST 2

void construct_rule_graph(std::unordered_map<int, Vertex> *rule_graph, int switch_number, std::unordered_map<int, int> *switch_index_id,
							std::unordered_map<int, Switch> *switches, std::unordered_map<int, Rule> *rules){
	int match_count = 0;
	#pragma omp parallel for
	for ( int u = 0; u < switch_number; u ++ ){
		Switch *s1 = &switches->at(switch_index_id->at(u));
		for ( int i = 0; i < s1->getNeighborsSize(); i++ ){
			Switch *s2 = &switches->at(s1->getNeighbors()->at(i));
			for ( int j = 0; j < s1->getRulesSize(); j++  ){
				Rule *r1 = &rules->at(s1->getRules()->at(j));
				if ( r1->getOutPort() != s2->getID() ) continue;
				for ( int k = 0; k < s2->getRulesSize(); k++ ){
					Rule *r2 = &rules->at(s2->getRules()->at(k));
					if ( r2->getInPort() != s1->getID() || !is_match(r1->getOutHeaderSpace(), r2->getInHeaderSpace()) ) continue;
					(&rule_graph->at(r1->getID()))->addOutNeighbor(r2->getID());
					(&rule_graph->at(r2->getID()))->addInNeighbor(r1->getID());
					match_count++;
				}
			}
		}
	}
}

void construct_rule_graph(int vertex_number, std::unordered_map<int, Vertex> *rule_graph,
							std::unordered_map<int, int> *rule_graph_index_id, std::unordered_map<int, Rule> *rules){
	int match_count = 0;
	#pragma omp parallel for
	for ( int i = 0; i < vertex_number; i++ ){
		for ( int j = 0; j < vertex_number; j++ ){
			if ( i == j ) continue;
			Rule *r1 = &rules->at(rule_graph_index_id->at(i));
			Rule *r2 = &rules->at(rule_graph_index_id->at(j));
			if ( r1->getOutPort() == r2->getSwitchID() && r2->getInPort() == r1->getSwitchID() && is_match(r1->getOutHeaderSpace(), r2->getInHeaderSpace()) ){
				(&rule_graph->at(r1->getID()))->addOutNeighbor(r2->getID());
				(&rule_graph->at(r2->getID()))->addInNeighbor(r1->getID());
				match_count++;
			}
		}
	}
}

void print_rule_graph(int vertex_number, std::unordered_map<int, int> *rule_graph_index_id, std::unordered_map<int, Vertex> *rule_graph){
	for ( int i = 0; i < vertex_number; i++ ){
		int rule_id = rule_graph_index_id->at(i);
		rule_graph->at(rule_id).getRule()->print();
		rule_graph->at(rule_id).print();
	}
}

bool debug_test(std::vector<int> *path_indices, int path_index){
	if ( path_indices->size() < DEBUG_TEST ){
		path_indices->push_back(path_index);
		return true;
	}
	bool is_exist = false;
	for ( unsigned int i = 0; i < path_indices->size(); i++ ){
		if ( path_indices->at(i) == path_index ){
			is_exist = true;
		}
	}
	return is_exist;
}

void output_testpackests(std::string output_filename, std::vector<TestPacket> *test_packets, std::unordered_map<int, Rule> *rules){
	std::ofstream outfile(output_filename);
	outfile << test_packets->size() << std::endl;
	for ( unsigned int i = 0; i < test_packets->size(); i++ ){
		outfile << test_packets->at(i).getIndex() << " ";
		outfile << test_packets->at(i).getPrefix() << " ";
		outfile << test_packets->at(i).getPath()->size() << " ";
		for ( auto &rule_id : *test_packets->at(i).getPath() ){
			outfile << rule_id << " ";
		}
		outfile << std::endl;
		for ( auto &rule_id : *test_packets->at(i).getPath() ){
			outfile << rules->at(rule_id).getID() << " ";
			outfile << rules->at(rule_id).getSwitchID() << " ";
			outfile << rules->at(rule_id).getPrefix() << " ";
			outfile << rules->at(rule_id).getInPort() << " ";
			outfile << rules->at(rule_id).getOutPort() << " ";
			outfile << rules->at(rule_id).getPriority() << " ";
			outfile << std::endl;
		}
	}
}

int main(int argc, char *argv[]){
	std::string filename_prefix;
	std::string input_filename;
	std::string output_filename;
	if ( argc == 2 ){
		input_filename = std::string(argv[1]);
		output_filename = input_filename + ".testpackets";
	}
	else{
		std::cout << "Wrong parameters" << std::endl;
		return 0;
	}

	//std::cout << filename_prefix << " " << input_filename << " " << output_filename << std::endl;

	std::ifstream infile(input_filename);

	std::unordered_map<int, int> rule_graph_index_id;
	std::unordered_map<int, int> switch_index_id;
	std::unordered_map<int, Switch> switches;
	std::unordered_map<int, Rule> rules;
	std::unordered_map<int, Vertex> rule_graph;
	rule_graph.reserve(1024000);

	std::vector<int> path_indices; //for test

	int switch_number;
	int vertex_number = 0;
	infile >> switch_number;
	switch_index_id.reserve(switch_number);
	//std::cout << switch_number << std::endl;

	for ( int i = 0; i < switch_number; i++ ){
		std::string line;
		int switch_id, neighbor_id;
		infile >> switch_id;
		Switch s(i, switch_id);
		std::getline(infile, line);
		std::stringstream ss(line);
		while ( ss >> neighbor_id ){
			s.addNeighbor(neighbor_id);
		}
		switches[switch_id] = s;
		switch_index_id[i] = switch_id;
	}

	for ( int i = 0; i < switch_number; i++ ){
		std::string line;
		int switch_id, rule_number;
		infile >> switch_id >> rule_number;
		//std::cout << switch_id << " " << rule_number << std::endl;
		std::getline(infile, line);
		for ( int j = 0; j < rule_number; j++ ){
			std::getline(infile, line);
			std::istringstream iss(line);
			int rule_id, in_port, out_port, priority, path_index;
			std::string header, prefix;
			iss >> rule_id >> switch_id >> prefix >> header >> in_port >> out_port >> priority >> path_index;
			//std::cout << rule_id << " " << switch_id << " " << prefix << " " << header << " " << in_port << " " << out_port << std::endl;
			if ( DEBUG && !debug_test(&path_indices, path_index) ) continue;
			rules[rule_id] = Rule(rule_id, switch_id, prefix, header, in_port, out_port, priority, path_index);

			rule_graph[rule_id] = Vertex(&rules[rule_id]);

			rule_graph_index_id[vertex_number] = rule_id;
			switches[switch_id].addRule(rule_id);
			vertex_number++;
		}
	}

	//for ( int i = 0; i < switch_number; i++ ){
	//	std::cout << switches[switch_index_id[i]].getID() << std::endl;
	//}
	auto start_time2 =std::chrono::high_resolution_clock::now();

	//Construct rulegraph
	std::cout << "Constructing Rule Graph" << std::endl;
	construct_rule_graph(&rule_graph, switch_number, &switch_index_id, &switches, &rules);

	std::cout << "  Rule number: " << vertex_number << std::endl;
	std::cout << std::endl;

	//Topological sorting
	auto start_time = std::chrono::high_resolution_clock::now();
	std::cout << "Topological Sorting" << std::endl;

	TopologicalSort topological_sort(vertex_number, &rule_graph, &rule_graph_index_id);
	topological_sort.start(&rule_graph, &rule_graph_index_id);
	std::cout << std::endl;

	//Transitive closure
	std::cout << "Transitive Closure" << std::endl;

	TransitiveClosure transitive_closure(vertex_number, topological_sort.getRuleGraph(), &rule_graph_index_id);
	transitive_closure.start(topological_sort.getRuleGraph(), &rule_graph_index_id);
	std::cout << std::endl;

	//Matching
	std::cout << "Maximum Matching" << std::endl;
	HopcroftKarp maximum_matching(vertex_number, transitive_closure.getRuleGraph(), &rule_graph_index_id, topological_sort.getSortingList());
	maximum_matching.start(transitive_closure.getRuleGraph());
	std::cout << "  Maximum matching number: " << maximum_matching.getMatchingNumber() << std::endl;
	//std::cout << "Path number: " << vertex_number - maximum_matching.getMatchingNumber() << std::endl;
	std::cout << std::endl;

	//Trace Path
	std::cout << "Trace Path" << std::endl;
	TracePath trace_path(vertex_number, &rule_graph_index_id);
	trace_path.start(maximum_matching.getMatchX(), transitive_closure.getP(), topological_sort.getSortingList());

	std::cout << "  Path number: " << trace_path.getAllPaths()->size() << std::endl;
	std::cout << std::endl;

	std::vector<TestPacket> test_packets;
	test_packets.reserve(trace_path.getAllPaths()->size());
	for ( unsigned int i = 0; i < trace_path.getAllPaths()->size(); i++ ){
		TestPacket packet(i, trace_path.getAllPaths()->at(i), &rules);
		test_packets.push_back(packet);
	}

	//Output testpackets
	//
	//
	std::cout << "Output test packets" << std::endl;
	output_testpackests(output_filename, &test_packets, &rules);
	std::cout << "  Test packet number: " << test_packets.size() << std::endl;

	auto end_time = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double, std::milli> diff = end_time - start_time;
	std::chrono::duration<double, std::milli> diff2 = end_time - start_time2;
	std::cout << "  Test packet generation time including construct rule graph (sec): ";
	std::cout << (float)diff2.count() / 1000.0 << std::endl;
	std::cout << "  Test packet generation time (sec): " << (float)diff.count() / 1000.0 << std::endl;

	std::cout << "Output filename is: " << output_filename << std::endl;
	return 0;
}


#ifndef DEOM_H_
#define DEOM_H_

#include "hashmap.hpp"
#include "json11.hpp"
#include <Eigen/Dense>
#include <atomic>
#include <chrono>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <omp.h>
#include <random>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
using Eigen::Matrix2cd;
using Eigen::Matrix3cd;
using Eigen::Matrix4cd;
using Eigen::MatrixXcd;
using Eigen::MatrixXi;
using Eigen::VectorXcd;
using Eigen::VectorXi;

#define NSYS 2

using namespace json11;
using namespace std;

typedef unsigned long long int ullint;
typedef long long int llint;
typedef Eigen::Matrix<complex<double>, NSYS, NSYS> MatrixNcd;
typedef Eigen::Matrix<ullint, Eigen::Dynamic, Eigen::Dynamic> nullmat;
typedef std::vector<MatrixNcd, Eigen::aligned_allocator<MatrixNcd>> nNNmat;
typedef std::vector<MatrixXcd, Eigen::aligned_allocator<MatrixXcd>> nXXmat;
typedef Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> XXimat_r;
typedef struct {
  ullint nmax;
  int nsys;
  int nind;
  int nddo;
  float ferr;
  int combmax;
  int lmax;
  int nmod;
  int nmodmax;
  int nham;
  nullmat comb_list;
  VectorXi modLabel;
  mutex nddo_mutex;
  Matrix2cd ham1;
  Matrix2cd hamt;
  VectorXcd delt_res;
  nNNmat qmd1;
  nNNmat qmdt;
  nNNmat qmdl;
  nNNmat qmdl_s;
  nNNmat qmdr;
  nNNmat qmdr_s;
  VectorXcd coef_abs;
  nXXmat coef_lft;
  nXXmat coef_rht;
  VectorXcd expn;
  nNNmat sdip;
  nNNmat pdip;
  VectorXcd bdip;
} DEOM;

// Please note that due to the convenience of inspection, we always keep the non-parallel version.
#endif


/*
* LEGAL NOTICE
* This computer software was prepared by Battelle Memorial Institute,
* hereinafter the Contractor, under Contract No. DE-AC05-76RL0 1830
* with the Department of Energy (DOE). NEITHER THE GOVERNMENT NOR THE
* CONTRACTOR MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY
* LIABILITY FOR THE USE OF THIS SOFTWARE. This notice including this
* sentence must appear on any copies of this computer software.
*
* EXPORT CONTROL
* User agrees that the Software will not be shipped, transferred or
* exported into any country or used in any manner prohibited by the
* United States Export Administration Act or any other applicable
* export laws, restrictions or regulations (collectively the "Export Laws").
* Export of the Software may require some form of license or other
* authority from the U.S. Government, and failure to obtain such
* export control license may result in criminal liability under
* U.S. laws. In addition, if the Software is identified as export controlled
* items under the Export Laws, User represents and warrants that User
* is not a citizen, or otherwise located within, an embargoed nation
* (including without limitation Iran, Syria, Sudan, Cuba, and North Korea)
*     and that User is not otherwise prohibited
* under the Export Laws from receiving the Software.
*
* Copyright 2011 Battelle Memorial Institute.  All Rights Reserved.
* Distributed as open-source under the terms of the Educational Community
* License version 2.0 (ECL 2.0). http://www.opensource.org/licenses/ecl2.php
*
* For further details, see: http://www.globalchange.umd.edu/models/gcam/
*
*/

/*!
 * wind_technology.cpp
 * Created: 03/20/2007
 * Version: 04/16/2007
 *
 * This software, which is provided in confidence, was prepared by employees
 * of Pacific Northwest National Laboratory operated by Battelle Memorial
 * Institute. Battelle has certain unperfected rights in the software
 * which should not be copied or otherwise disseminated outside your
 * organization without the express written authorization from Battelle.
 * All rights to the software are reserved by Battelle.   Battelle makes no
 * warranty, express or implied, and assumes no liability or responsibility
 * for the use of this software.
 */

// include files ***********************************************************

#include "util/base/include/definitions.h"
#include "technologies/include/wind_technology.h"
#include "technologies/include/marginal_profit_calculator.h"
#include "technologies/include/iproduction_state.h"
#include "marketplace/include/marketplace.h"
#include "containers/include/iinfo.h"
#include "util/base/include/TValidatorInfo.h"
#include "util/base/include/util.h"
#include "util/base/include/xml_helper.h"
#include "technologies/include/ioutput.h"
#include "functions/include/non_energy_input.h"

#include <sstream>
#include <algorithm>
#include <cmath>

#include <boost/math/special_functions/erf.hpp>
using boost::math::erf;

// namespaces **************************************************************

// WindTechnology::kWhrtoGJ ************************************************

const double WindTechnology::kWhrtoGJ = 0.0036;

// WindTechnology::sXMLTagNames ********************************************

std::string WindTechnology::sXMLTagNames[] =
{
   std::string( "air-density" ),
   std::string( "average-wind-speed" ),
   std::string( "capital-cost" ),
   std::string( "cutout-speed" ),
   std::string( "fcr" ),
   std::string( "grid-connection-cost" ),
   std::string( "om" ),
   std::string( "reference-height" ),
   std::string( "rotor-diameter" ),
   std::string( "turbine-density" ),
   std::string( "turbine-derating" ),
   std::string( "turbine-hub-height" ),
   std::string( "turbine-rating" ),
   std::string( "wind-farm-loss" ),
   std::string( "wind-velocity-exponent" )
};

// Constructor: WindTechnology *********************************************

/*! Default constructor
 *  \param aName the name of the technology
 *  \param aYear the year
 */
WindTechnology::WindTechnology(
   const std::string& aName,
   const int          aYear )
   : IntermittentTechnology( aName, aYear )
{
    mCapitalCost = 261.83;
    mConnectCost = -1;
    mCutOutSpeed = -1;
    mFCR = 0.0856;
    mGenerationCost = -1;
    mGridConnectionCost = 392.75;
    mOM = 2.62;
    mRealizedTurbineOutput = -1;
    mRotorDiameter = -1;
    mTurbineDensity = -1;
    mTurbineDerating = -1;
    mTurbineHubHeight = -1;
    mTurbineRating = -1;
    mWindCapacityFactor = -1;
    mWindFarmLoss = -1;
}

// Destructor: WindTechnology **********************************************

WindTechnology::~WindTechnology(void)
{
}

void WindTechnology::copy( const WindTechnology& aOther ) {
    IntermittentTechnology::copy( aOther );
    mCapitalCost = aOther.mCapitalCost;
    mConnectCost = aOther.mConnectCost;
    mCutOutSpeed = aOther.mCutOutSpeed;
    mFCR = aOther.mFCR;
    mGenerationCost = aOther.mGenerationCost;
    mGridConnectionCost = aOther.mGridConnectionCost;
    mOM = aOther.mOM;
    mRealizedTurbineOutput = aOther.mRealizedTurbineOutput;
    mRotorDiameter = aOther.mRotorDiameter;
    mTurbineDensity = aOther.mTurbineDensity;
    mTurbineDerating = aOther.mTurbineDerating;
    mTurbineHubHeight = aOther.mTurbineHubHeight;
    mTurbineRating = aOther.mTurbineRating;
    mWindCapacityFactor = aOther.mWindCapacityFactor;
    mWindFarmLoss = aOther.mWindFarmLoss;
}

// WindTechnology::calcCost ************************************************

// Documentation is inherited
void WindTechnology::calcCost(
   const std::string& aRegionName,
   const std::string& aSectorName,
   const int          aPeriod )
{
    if( !mProductionState[ aPeriod ]->isOperating() ){
        return;
    }

   // Var to hold capital and operating costs
   double totalTechCapOMCost = 0;

   // Get marketplace and calculate costs
   Marketplace*       pMarketplace = scenario->getMarketplace();
   const IInfo*       pInfo        = pMarketplace->getMarketInfo( ( *mResourceInput )->getName(), aRegionName, aPeriod, true );

   // Equation 4:
   mRealizedTurbineOutput = calcRealizedTurbineOutput( pInfo );

   // Equation 3:
   // WindCapacityFactor = RealizedTurbineOutput / TurbineRating
   mWindCapacityFactor = mRealizedTurbineOutput / mTurbineRating;

   // Equation 6:
   // CConnect = FCR * DConnect * ( gridConnectionCost / ( WindCapacityFactor * 1000.0 * kWhrtoGJ * 24 * 365 * 1 ) )
   double dConnect = pMarketplace->getPrice( ( *mResourceInput )->getName(), aRegionName, aPeriod );
   mConnectCost    = mFCR * dConnect * ( mGridConnectionCost / ( mWindCapacityFactor * 1000.0 * kWhrtoGJ * 24.0 * 365.0 ) );

   // Equation 2:
   // CGeneration = ( FCR * CapitalCost + OM ) / ( 24 * 365 * WindCapacityFactor * kWhrtoGJ )
   mGenerationCost = ( mFCR * mCapitalCost + mOM ) / ( 24.0 * 365.0 * mWindCapacityFactor * kWhrtoGJ );

   if( !mProductionState[ aPeriod ]->isNewInvestment() ||
       mFixedOutput != IProductionState::fixedOutputDefault() )
   {
      // If an existing stock, only have O&M costs.
      totalTechCapOMCost = mOM /  ( 24.0 * 365.0 * mWindCapacityFactor * kWhrtoGJ );
   }
   else {
      // Generation plus connection costs.
      totalTechCapOMCost = std::max( mGenerationCost + mConnectCost, util::getSmallNumber() );
   }

   // Set tech capital and operating costs to input object
   ( *mTechCostInput )->setPrice( aRegionName, totalTechCapOMCost, aPeriod );

   // Call parent function to set costs and coefficients
   IntermittentTechnology::calcCost( aRegionName, aSectorName, aPeriod );

}

// WindTechnology::calcIdealTurbineOutput **********************************

/*!
 *  \param aAveWindSpeed the average Wind Speed
 *  \param aDiameter the turbine blade diameter (in meters)
 *  \param aAirDensity the average Air density (in g/m^3)
 */
double WindTechnology::calcIdealTurbineOutput(
   double aAveWindSpeed,
    double aDiameter,
    double aAirDensity )
{
   /*
    * The ideal maximum extractable power, known as the Betz limit, has a
    * coefficient of Cp = 16/27. At this limit, the integral evaluates to
    * (Equation A-6):
    *   Prb = p * ( 2 / 3 * D )^2 * V^3
    * where:
    *   p = the average Air density (in g/m^3)
    *   D = the turbine blade diameter (in meters)
    *   V = the average Wind Speed
    */
   return aAirDensity * std::pow( 2.0 / 3.0 * aDiameter, 2.0 ) * std::pow( aAveWindSpeed, 3.0 );
}

// WindTechnology::calcRealizedTurbineOutput *******************************
/*! Compute the realized turbine output
 *  \param apInfo pointer to the market info
 */
double WindTechnology::calcRealizedTurbineOutput( const IInfo* apInfo ) const
{
   // Equation 5:
   // aveWindSpeedAtHub = aveWindSpeed * ( turbineHubHeight / referenceHeight ) ^ windVelocityExponent
   double aveWindSpeed = apInfo->getDouble( sXMLTagNames[ AVERAGE_WIND_SPEED_KEY ], true );
   double referenceHeight = apInfo->getDouble( sXMLTagNames[ REFERENCE_HEIGHT_KEY ], true );
   double windVelocityExponent = apInfo->getDouble( sXMLTagNames[ WIND_VELOCITY_EXPONENT_KEY ], true );
   double aveWindSpeedAtHub = aveWindSpeed * std::pow( mTurbineHubHeight / referenceHeight, windVelocityExponent );

   // Equation 4:
   // RealizedTurbineOutput = ( IdealTurbineOutput / 10^6 ) * TurbineCoefficient * ( 1 - Derating ) * ( 1 - WindFarmLoss )
   double airDensity = apInfo->getDouble( sXMLTagNames[ AIR_DENSITY_KEY ], true );
   double realizedTurbineOutput = ( calcIdealTurbineOutput( aveWindSpeedAtHub, mRotorDiameter, airDensity ) / 1.0e6 ) * calcTurbineCoefficient( aveWindSpeedAtHub, mTurbineRating, mRotorDiameter, airDensity, mCutOutSpeed ) * ( 1.0 - mTurbineDerating ) * ( 1.0 - mWindFarmLoss );
   mWindPowerVariance = computeWindPowerVariance( aveWindSpeedAtHub, mTurbineRating, mRotorDiameter, airDensity, mCutOutSpeed );
   return realizedTurbineOutput;
}

// WindTechnology::calcResourceArea ****************************************

/*! Calculate the resource area in km^2
 *  NOTE: Unlike solar techs, wind does not exclusively use this land. This need to be
 *  accounted for when setting land costs.
 *  \param aRegionName the region name
 *  \param aSectorName the sector name
 *  \param aVariableDemand the variable demand
 *  \param aPeriod the period
 *  \return the resource area
 */
double WindTechnology::calcResourceArea(
   const std::string& aRegionName,
   const std::string& aSectorName,
   double             aVariableDemand,
   const int          aPeriod )
{
   // Get info object for this resource
   Marketplace*       pMarketplace = scenario->getMarketplace();
   const IInfo*       pInfo        = pMarketplace->getMarketInfo( ( *mResourceInput )->getName(), aRegionName, aPeriod, true );

   // Equation 4:
   mRealizedTurbineOutput = calcRealizedTurbineOutput( pInfo );

   // Equation 3:
   // WindCapacityFactor = RealizedTurbineOutput / TurbineRating
   mWindCapacityFactor = mRealizedTurbineOutput / mTurbineRating;

   // Equation 7:
   // WindGeneration / ( kWhrtoGJ * 10^-9 ) = TurbineDensity * ResourceArea * WindCapacityFactor * 1000 * 24 * 365
   // ResourceArea = ( WindGeneration / ( kWhrtoGJ * 10^-9 ) ) / ( TurbineDensity * WindCapacityFactor * 1000 * 24 * 365 )
   double windGeneration = aVariableDemand;
   double resourceArea = ( windGeneration / ( kWhrtoGJ * 1.0e-9 ) ) / ( mTurbineDensity * mWindCapacityFactor * 1000.0 * 24.0 * 365.0 );

   return resourceArea;
}

/*! \brief Return amount of resource needed per unit of energy output.
*  This method should be used when a technology uses a resource that is
*  not in energy units.
*  NOTE: Unlike solar techs, wind does not exclusively use this land. This need to be
*  accounted for when setting land costs.
* \author Steve Smith
* \param aPeriod Model period.
*/
double WindTechnology::getResourceToEnergyRatio( const std::string& aRegionName,
                                                 const std::string& aSectorName,
                                                 const int aPeriod )
{
    // Default assumpion is that resource is in energy units
   // Need to return the amount of resource (in km^2) needed per EJ of energy output
   return calcResourceArea( aRegionName, aSectorName, 1.0, aPeriod );
}

// WindTechnology::calcTurbineCoefficient **********************************

/*! Compute the capture coefficient for a turbine with a finite power rating.
 *  \param aAveWindSpeed the average Wind Speed
 *  \param aRating the turbine Rating (in MW)
 *  \param aDiameter the turbine Blade Diameter (in meters)
 *  \param aAirDensity the average Air density (in g/m^3)
 *  \param aCutoutSpeed the cut-out Speed (m/s)
 */
double WindTechnology::calcTurbineCoefficient(
   double aAveWindSpeed,
    double aRating,
    double aDiameter,
    double aAirDensity,
    double aCutoutSpeed )
{
   static const double PI     = 2.0 * std::asin( 1.0 );
   static const double sqrtPI = std::sqrt( PI );

   /*
    * Equation A-8
    * Calculate the final cutout speed (Xf)
    *   Xs = ( sqrt( pi ) / 2) * ( Vs / Vave )
    */
   double Xf = ( sqrtPI / 2.0 ) * ( aCutoutSpeed / aAveWindSpeed );

   /*
    * Equation A-10
    * Calculate the velocity (solve for v)
    * 1/8pD^2(16/27)v^3 = Prated
    * v = cube-root( Prated / ( 1/8pD^2(16/27) )
    */
   double aRatingInWatts = aRating * 1.0e6;
   double velocity = std::pow( aRatingInWatts / ( 1.0 / 8.0 * aAirDensity * PI * std::pow( aDiameter, 2.0 ) * 16.0 / 27.0 ), 1.0 / 3.0 );

   /*
    * Equation A-11
    * Calculate the finite power rating (Xr)
    * Xr = ( sqrt( pi ) / 2 ) * V / Vave
    */
   double Xr = ( sqrtPI / 2.0 ) * velocity / aAveWindSpeed;

   /*
    * Equation A-7:
    * Compute the power up to a finite cutout for Xr
    * speed is ():
    *
    *   CCfinite_cutout( Xs ) =
    *      Erf( Xs ) - ( 4 / ( 3 * sqrt( pi ) ) ) * Xs * ( Xs^2 + 3 / 2 ) * e^-Xs^2
    */
   double CCfiniteCutout = erf( Xr ) - ( 4.0 / ( 3.0 * sqrtPI ) ) * Xr * ( std::pow( Xr, 2.0 ) + 3.0 / 2.0 ) * std::exp( -std::pow( Xr, 2.0 ) );

   /*
    * Equation A-9:
    * Finally, compute the capture coefficient
    * CCfinite_power( Xr, Xf ) =
    *    CCfinite_cutout( Xr ) + ( Xr^3 / ( 3 / 4 * sqrt( pi ) ) ) * ( e ^-Xr^2 - e^-Xf^2 )
    */
   double CCfinitePower = CCfiniteCutout + ( std::pow( Xr, 3.0 ) / ( 3.0 / 4.0 * sqrtPI ) ) * ( std::exp( -std::pow( Xr, 2.0 ) ) - std::exp( -std::pow( Xf, 2.0 ) ) );

   return CCfinitePower;
}

// WindTechnology::clone ***************************************************

// Documentation is inherited
WindTechnology* WindTechnology::clone( void ) const
{
    WindTechnology* clone = new WindTechnology( mName, mYear );
    clone->copy( *this );
    return clone;
}

// WindTechnology::completeInit ********************************************

// Documentation is inherited
void WindTechnology::completeInit(
   const std::string&              aRegionName,
   const std::string&              aSectorName,
   const std::string&              aSubsectorName,
   const IInfo*                    aSubsectorIInfo,
   ILandAllocator*                 aLandAllocator )
{
   // Initialize a non-energy input to hold technology costs
   if( util::searchForValue( mInputs, getTechCostName() ) == mInputs.end() ){
        mInputs.push_back( new NonEnergyInput( getTechCostName() ) );
    }

   IntermittentTechnology::completeInit( aRegionName, aSectorName, aSubsectorName, aSubsectorIInfo, aLandAllocator );

   // Validate input parameters
   typedef ObjECTS::TValidatorInfo<> validator_type;
   validator_type   validator[] =
   {
      validator_type( mCapitalCost, sXMLTagNames[ CAPITAL_COST_KEY ], mCapitalCost > 0 ),
      validator_type( mCutOutSpeed, sXMLTagNames[ CUTOUT_SPEED_KEY ], mCutOutSpeed > 0 ),
      validator_type( mFCR, sXMLTagNames[ FCR_KEY ], mFCR > 0 ),
      validator_type( mRotorDiameter, sXMLTagNames[ ROTOR_DIAMETER_KEY ], mRotorDiameter > 0 ),
      validator_type( mTurbineDensity, sXMLTagNames[ TURBINE_DENSITY_KEY ], mTurbineDensity > 0 ),
      validator_type( mTurbineDerating, sXMLTagNames[ TURBINE_DERATING_KEY ], mTurbineDerating > 0 ),
      validator_type( mTurbineHubHeight, sXMLTagNames[ TURBINE_HUB_HEIGHT_KEY ], mTurbineHubHeight > 0 ),
      validator_type( mTurbineRating, sXMLTagNames[ TURBINE_RATING_KEY ], mTurbineRating > 0 ),
      validator_type( mWindFarmLoss, sXMLTagNames[ WIND_FARM_LOSS_KEY ], mWindFarmLoss > 0 )
   };

   unsigned short numParams = sizeof( validator ) / sizeof( validator[0] );
   std::string    msg       = ObjECTS::getInvalidNames(
      &validator[0],
      &validator[numParams] );

   if ( msg.length() )
   // Invalid input parameter
   {
      ILogger& mainLog = ILogger::getLogger( "main_log" );
      mainLog.setLevel( ILogger::ERROR );
      mainLog << "Invalid input parameter(s) to "
         << getXMLNameStatic()
         << " in sector " << aSectorName
         << ": " << msg << std::endl;
      exit( -1 );
   }
}

// WindTechnology::getXMLName1D ********************************************

// Documentation is inherited
const std::string& WindTechnology::getXMLName1D( void ) const
{
   return getXMLNameStatic();
}

// WindTechnology::getXMLNameStatic1D **************************************

// Documentation is inherited
const std::string& WindTechnology::getXMLNameStatic1D( void )
{
   static const std::string XML_NAME1D = "wind-technology";

   return XML_NAME1D;
}

/*! \brief Return name to be used for input object containing technology costs.
*
* This input object will contain technology capital, operation, and any other costs
* exclusive of backup or fuel costs. Setting to blank indicates that this object does
* not use this cost.
*
* \author Steve Smith
* \return The constant XML_NAME as a static.
*/
const std::string& WindTechnology::getTechCostName( ) const {
   const static std::string TECH_COST_NAME = "turbine-and-connect-costs";
   return TECH_COST_NAME;
}

// WindTechnology::initCalc ************************************************

// Documentation is inherited
void WindTechnology::initCalc(
   const std::string& aRegionName,
   const std::string& aSectorName,
   const IInfo*       aSubsectorIInfo,
   const Demographic* aDemographics,
   PreviousPeriodInfo& aPrevPeriodInfo,
   const int          aPeriod )
{
   IntermittentTechnology::initCalc( aRegionName, aSectorName, aSubsectorIInfo, aDemographics,
       aPrevPeriodInfo, aPeriod );

   // Get marketplace and make sure we have the correct type of resource
   Marketplace*       pMarketplace = scenario->getMarketplace();
   const IInfo*       pInfo        = pMarketplace->getMarketInfo( ( *mResourceInput )->getName(), aRegionName, aPeriod, true );
   std::string        msg          = "";

   if ( !pInfo )
   {
      std::ostringstream ostr;
      ostr << "Error getting marketplace info for ( "
           << ( *mResourceInput )->getName() << ", "
           << aRegionName << ", "
           << aPeriod << " )"
           << std::ends;
      msg = ostr.str();
   }
   else
   {
      typedef ObjECTS::TValidatorInfo<> validator_type;
      double           notUsed=0.0;
      validator_type   validator[] =
      {
         validator_type( notUsed, sXMLTagNames[ AVERAGE_WIND_SPEED_KEY ], pInfo->hasValue( sXMLTagNames[ AVERAGE_WIND_SPEED_KEY ] ) ),
         validator_type( notUsed, sXMLTagNames[ AIR_DENSITY_KEY ], pInfo->hasValue( sXMLTagNames[ AIR_DENSITY_KEY ] ) ),
         validator_type( notUsed, sXMLTagNames[ REFERENCE_HEIGHT_KEY ], pInfo->hasValue( sXMLTagNames[ REFERENCE_HEIGHT_KEY ] ) ),
         validator_type( notUsed, sXMLTagNames[ WIND_VELOCITY_EXPONENT_KEY ], pInfo->hasValue( sXMLTagNames[ WIND_VELOCITY_EXPONENT_KEY ] ) )
      };

      unsigned short numParams = sizeof( validator ) / sizeof( validator[0] );
      msg = ObjECTS::getInvalidNames(
         &validator[0],
         &validator[numParams] );
      if ( msg.length() )
      {
         std::ostringstream ostr;
         ostr << "Invalid input parameter(s) to "
              << getXMLNameStatic()
              << " in sector " << aSectorName
              << ": " << msg << std::ends;
         msg = ostr.str();
      }
   }

   if ( msg.length() )
      // Invalid input parameter
   {
      ILogger& mainLog = ILogger::getLogger( "main_log" );
      mainLog.setLevel( ILogger::ERROR );
      mainLog << msg << std::endl;
   }
}

// WindTechnology::production **********************************************

double WindTechnology::getCalibrationOutput( const bool aHasRequiredInput,
                                         const std::string& aRequiredInput,
                                         const int aPeriod ) const
{
   return IntermittentTechnology::getCalibrationOutput( aHasRequiredInput, aRequiredInput, aPeriod );
}

// WindTechnology::toDebugXMLDerived ***************************************

// Documentation is inherited
void WindTechnology::toDebugXMLDerived(
   const int     period,
   std::ostream& out,
   Tabs*         tabs ) const
{
   IntermittentTechnology::toDebugXMLDerived( period, out,  tabs );
   XMLWriteElement( mCapitalCost, sXMLTagNames[ CAPITAL_COST_KEY ], out, tabs );
   XMLWriteElement( mConnectCost, "connection-cost", out, tabs );
   XMLWriteElement( mCutOutSpeed, sXMLTagNames[ CUTOUT_SPEED_KEY ], out, tabs );
   XMLWriteElement( mFCR, sXMLTagNames[ FCR_KEY ], out, tabs );
   XMLWriteElement( mGenerationCost, "generation-cost", out, tabs );
   XMLWriteElement( mGridConnectionCost, sXMLTagNames[ GRID_CONNECTION_COST_KEY ], out, tabs );
   XMLWriteElement( mOM, sXMLTagNames[ OM_KEY ], out, tabs );
   XMLWriteElement( mRealizedTurbineOutput, "realized-turbine-output", out, tabs );
   XMLWriteElement( mRotorDiameter, sXMLTagNames[ ROTOR_DIAMETER_KEY ], out, tabs );
   XMLWriteElement( mTurbineDensity, sXMLTagNames[ TURBINE_DENSITY_KEY ], out, tabs );
   XMLWriteElement( mTurbineDerating, sXMLTagNames[ TURBINE_DERATING_KEY ], out, tabs );
   XMLWriteElement( mTurbineHubHeight, sXMLTagNames[ TURBINE_HUB_HEIGHT_KEY ], out, tabs );
   XMLWriteElement( mTurbineRating, sXMLTagNames[ TURBINE_RATING_KEY ], out, tabs );
   XMLWriteElement( mWindCapacityFactor, "wind-capacity-factor", out, tabs );
   XMLWriteElement( mWindFarmLoss, sXMLTagNames[ WIND_FARM_LOSS_KEY ], out, tabs );
   XMLWriteElement( mWindPowerVariance, "wind-power-variance", out, tabs );
}

// WindTechnology::toInputXMLDerived ***************************************

// Documentation is inherited
void WindTechnology::toInputXMLDerived(
   std::ostream& out,
   Tabs*         tabs ) const
{
   IntermittentTechnology::toInputXMLDerived( out,  tabs );
   XMLWriteElementCheckDefault( mCapitalCost, sXMLTagNames[ CAPITAL_COST_KEY ], out, tabs, double( 1000.0 ) );
   XMLWriteElement( mCutOutSpeed, sXMLTagNames[ CUTOUT_SPEED_KEY ], out, tabs );
   XMLWriteElementCheckDefault( mFCR, sXMLTagNames[ FCR_KEY ], out, tabs, double( 0.0856 ) );
   XMLWriteElementCheckDefault( mGridConnectionCost, sXMLTagNames[ GRID_CONNECTION_COST_KEY ], out, tabs, double( 1500.0 ) );
   XMLWriteElementCheckDefault( mOM, sXMLTagNames[ OM_KEY ], out, tabs, double( 10.0 ) );
   XMLWriteElement( mRotorDiameter, sXMLTagNames[ ROTOR_DIAMETER_KEY ], out, tabs );
   XMLWriteElement( mTurbineDensity, sXMLTagNames[ TURBINE_DENSITY_KEY ], out, tabs );
   XMLWriteElement( mTurbineDerating, sXMLTagNames[ TURBINE_DERATING_KEY ], out, tabs );
   XMLWriteElement( mTurbineHubHeight, sXMLTagNames[ TURBINE_HUB_HEIGHT_KEY ], out, tabs );
   XMLWriteElement( mTurbineRating, sXMLTagNames[ TURBINE_RATING_KEY ], out, tabs );
   XMLWriteElement( mWindFarmLoss, sXMLTagNames[ WIND_FARM_LOSS_KEY ], out, tabs );
}

// WindTechnology::XMLDerivedClassParse ************************************

// Documentation is inherited
bool WindTechnology::XMLDerivedClassParse(
   const std::string&      nodeName,
   const xercesc::DOMNode* curr )
{
   if ( nodeName == sXMLTagNames[ CAPITAL_COST_KEY ] )
   {
      mCapitalCost = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ CUTOUT_SPEED_KEY ] )
   {
      mCutOutSpeed = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ FCR_KEY ] )
   {
      mFCR = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ GRID_CONNECTION_COST_KEY ] )
   {
      mGridConnectionCost = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ OM_KEY ] )
   {
      mOM = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ ROTOR_DIAMETER_KEY ] )
   {
      mRotorDiameter = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ TURBINE_DENSITY_KEY ] )
   {
      mTurbineDensity = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ TURBINE_DERATING_KEY ] )
   {
      mTurbineDerating = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ TURBINE_HUB_HEIGHT_KEY ] )
   {
      mTurbineHubHeight = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ TURBINE_RATING_KEY ] )
   {
      mTurbineRating = XMLHelper<double>::getValue( curr );
   }
   else if ( nodeName == sXMLTagNames[ WIND_FARM_LOSS_KEY ] )
   {
      mWindFarmLoss = XMLHelper<double>::getValue( curr );
   }
   else if ( IntermittentTechnology::XMLDerivedClassParse( nodeName, curr ) ) {
   }
   else
   {
      return false;
   }

   return true;
}

/*
   The one parameter Weibull distribution is (Equation A-1):
   f( x ) = ( B / c ) * ( x / c )^(B - 1) * e^-( x / c )^B

   The mean of the distribution is (Equation A-2):
   X = c * gamma * ( 1 / B + 1)

   The mean wind speed is (Equation A-3):
   X = c * gamma * 1.5 = ( sqrt( pi ) / 2 ) * c
   or
   c = X / a = 2X / sqrt( pi ) * a = sqrt( pi ) / 2 = 0.88623

   The wind distribution for a mean wind speed of is (Equation A-4):
   f( v ) = ( pi * v / 2X^2 ) * e^( -pi * ( v / 2X )^2

   Equation A-8:
   Xf = ( sqrt( pi ) / 2 ) * ( Vf / Vave )

   Equation A-10:
   1/2 * p * A * Cp * v^3 = 1 / 8 * p * D^2 * ( 16 / 27 ) * v^3 = Prated

   Equation A-18:
   (16 / 9 * pi) * Prb^2 * { 6 - [ Xr^6 + 3 Xr^4 + 6Xr^2 + 6 ]e^(-(Xr)^2) + Xr^6[ e^(-(Xr)^2) - e^(-(Xf)^2)]} = (16 / 9 * pi) * G( Xr, Xf ) * Prb^2

   Equation A-19:
   Var(P^2) = [ ( 16 / 9 * pi ) * G( Xr, Xf ) - CCfinite-power^2( Xr, Xf ) ] * Prb^2
*/
double WindTechnology::computeWindPowerVariance(
   double aAveWindSpeed,
    double aRating,
    double aDiameter,
    double aAirDensity,
    double aCutoutSpeed ) const
{
   static const double PI     = 2.0 * std::asin( 1.0 );

   // Equation A-18:
   // (16 / 9 * pi) * Prb^2 * { 6 - [ Xr^6 + 3 Xr^4 + 6Xr^2 + 6 ]e^(-(Xr)^2) + Xr^6[ e^(-(Xr)^2) - e^(-(Xf)^2)]} = (16 / 9 * pi) * G( Xr, Xf ) * Prb^2
   // Xr = rating
   // Xf = cutout speed
   double G = ( 6.0 - ( std::pow( aRating, 6.0 ) + 3.0 * std::pow( aRating, 4.0 ) + 6.0 * std::pow( aRating, 2.0 ) + 6.0 ) * std::exp( -std::pow( aRating, 2.0) ) + std::pow( aRating, 6.0 ) * ( std::exp( -std::pow( aRating, 2.0) ) - std::exp( -std::pow( aCutoutSpeed, 2.0 ) ) ) );

   // Equation A-19:
   // Var(P^2) = [ ( 16 / 9 * pi ) * G( Xr, Xf ) - CCfinite-power^2( Xr, Xf ) ] * Prb^2
   // Xr = rating
   // Xf = cutout speed
   double windPowerVariance = ( 16.0 / 9.0 * PI * G - std::pow( WindTechnology::calcTurbineCoefficient( aAveWindSpeed, aRating, aDiameter, aAirDensity, aCutoutSpeed ), 2.0 ) ) * std::pow( WindTechnology::calcIdealTurbineOutput( aAveWindSpeed, aDiameter, aAirDensity ), 2.0 );

   return windPowerVariance;
}

// end of wind_technology.cpp **********************************************


// redist.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include <istream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <map>

//#include <chrono>
//#include <ctime>
//#include <utility>

#include <boost/filesystem.hpp>
#include <boost/mpi.hpp>

// Timing
#include <boost/timer/timer.hpp>
#include <boost/chrono.hpp>

//boost polygon includes
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

/**
 * PntVel is a structure that holds information for a single point.
 */
struct PntVel {
    // x coordinate
    double x;
    // y coordinate
    double y;
    // z coordinate
    double z;
    // x velocity
    //double vx;
    // y velocity
    //double vy;
    // z velocity
    //double vz;
    // the diameter of the mesh element that this point belonged in the original mesh
    //double diam;
    // the ratio of the xy length / z length of the mesh element
    //double ratio;
    // the processor that owns this point in the new distribution
    int proc;
    std::vector<double> data;
};

// The boost definition for 2D points
typedef boost::geometry::model::d2::point_xy<double> bpoint;
// The boost definition for polygons that use 2D boost points
typedef boost::geometry::model::polygon<bpoint> bpoly;

// The definition for a list of boost 2D polygons
typedef std::map<int, bpoly> DomainListPoly;

/**
 * A structure that contains all the input parameters.
 *
 * The input parameters are listed in a single file. Each line holds one parameter
 * The format of the input file is not flexible at all. No comments allowed or any literals with spaces or any weird looking characters
 *
 * They must be listed in the following order
 */
struct inputs {
    // The number of subdomains that the problem was originaly solved. This is the number of processors used in the simulation step
    int NinitDom;
    /**
     * The velocity information is usually printed into files with the following format:
     *
     * f:\UCDAVIS\ichnos\NPSAT_example\example1__0000.ich.
     *
     * The above name essentially consists of three parts.
     *
     * 1. prefix :  f:\UCDAVIS\ichnos\NPSAT_example\example1__
     * 2. processor number : which must have identical zero padding for all processors.
     * 3 suffix : .ich
     *
     * Nzeros is the width of the processor number
     */
    int Nzeros;
    // This is the file name along with the path if it lives in a different folder than the program
    std::string prefix;
    /**
     * The characters after the processor id.
     * If the file is example1__0000_test01.ich then the suffix is _test01.ich
     */
    std::string suffix;
    /**
     * The file name that containts the new expanded domain boundaries.
     */
    std::string ExpadnedDomainFile;
    /**
     * The file name that containts the new actual domain boundaries.
     */
    std::string ActualDomainFile;
    // This is the prefix where the redistributed velocity fields will be printed
    std::string NewPrefix;
    //std::vector<int> InfoType;
    int Ninfo;
    std::vector<int> prec;
    int Nprint;
    std::vector<int> printOrder;

};

/**
 * readInputFile parse the input file.
 *
 * See the documentation of the #input for details about the format
 *
 * \param filename
 * \param input
 * \return true if the reading was succefull.
 */
bool readInputFile(std::string filename, inputs& input) {
    bool outcome = false;
    std::ifstream datafile(filename.c_str());
    if (!datafile.good()) {
        std::cout << "Can't open the file " << filename << std::endl;
    }
    else {
        std::string line;
        {// Number of domains that the initial velocity field is split
            getline(datafile, line);
            std::istringstream inp(line.c_str());
            inp >> input.NinitDom;
            // File name prefix of the existing velocity field
            inp >> input.prefix;
            // Number of zeros
            inp >> input.Nzeros;
            // Ending of file name
            inp >> input.suffix;
        }

        {// get the information of the points
            getline(datafile, line);
            std::istringstream inp(line.c_str());
            inp >> input.Ninfo;
            inp >> input.Nprint;
            for (int i = 0; i < input.Nprint; i++){
                int t;
                inp >> t;
                input.printOrder.push_back(t);
            }
        }

        {// Get the precision for the floating numbers
            getline(datafile, line);
            std::istringstream inp(line.c_str());
            for (int i = 0; i < input.Nprint; i++) {
                int t;
                inp >> t;
                input.prec.push_back(t);
            }
        }

        {
            getline(datafile, line);
            std::istringstream inp(line.c_str());
            inp >> input.ActualDomainFile;
            inp >> input.ExpadnedDomainFile;
        }
        {
            getline(datafile, line);
            std::istringstream inp(line.c_str());
            inp >> input.NewPrefix;
        }
        outcome = true;
        datafile.close();
    }
    return outcome;
}

/**
 * Reads the original velocity files and selects the points that belong to myRank new subdomain.
 *
 *
 * \param filename is the original velocity file name.
 * \param points is a vector to holds the points that live on the new subdomain with id myRank
 * \param myRank is the current processor id
 * \param expanded is a list of the expanded domains
 * \param actual is a list of the actual domains
 * \return true if everything was succesfull
 */
bool readVelocityFiles(std::string filename, std::vector< PntVel>& points, int myRank,
    DomainListPoly& expanded, DomainListPoly& actual, inputs& input) {
    bool outcome = false;
    std::ifstream datafile(filename.c_str());
    if (!datafile.good()) {
        std::cout << "Can't open the file " << filename << std::endl;
    }
    else {
        std::cout << "Processor " << myRank << " reads " << filename << std::endl;
        // Start timing
        //auto start = std::chrono::high_resolution_clock::now();
        boost::timer::cpu_timer timer;

        std::string line;
        double x, y;
        int count_lines = 0;
        while (getline(datafile, line)) {
            PntVel pv;
            if (line.size() > 1) {
                count_lines++;
                std::istringstream inp(line.c_str());
                inp >> x;
                inp >> y;
                if (boost::geometry::within(bpoint(x, y), expanded[myRank])) {
                    int proc = -9;
                    if (boost::geometry::within(bpoint(x, y), actual[myRank])) {
                        proc = myRank;
                    }
                    else {
                        DomainListPoly::iterator it = actual.begin();
                        for (; it != actual.end(); ++it) {
                            if (it->first == myRank)
                                continue;
                            if (boost::geometry::within(bpoint(x, y), it->second)) {
                                proc = it->first;
                                break;
                            }
                        }
                    }
                    if (proc == -9) {
                        std::cout << "Cant find actual domain for point (" << x << "," << y << ") in my expanded domain with id " << myRank <<  std::endl;
                    }
                    pv.x = x;
                    pv.y = y;
                    inp >> pv.z;
                    pv.proc = proc;
                    double dd;
                    int ii;
                    for (int i = 0; i < input.Ninfo; i++)
                    {
                        inp >> dd;
                        pv.data.push_back(dd);
                    }
                    points.push_back(pv);
                }
            }
        }
        datafile.close();

        //auto finish = std::chrono::high_resolution_clock::now();
        //std::chrono::duration<double> elapsed = finish - start;
        boost::chrono::duration<double> seconds = boost::chrono::nanoseconds(timer.elapsed().user);
        std::cout << ". . . .Proc " << myRank << " spend " << seconds.count() / 60 << " min to read " << count_lines << " lines" << std::endl;
        outcome = true;
    }
    return outcome;

}

/**
 * Reads the 2D polygons that define the new subdomains.
 *
 * \param filename is the name of the file
 * \param dpoly is a list of 2D polygons where each polygon is one domain.
 * \return true if reading was wihtout any issues
 *
 * The fromat of the file is the following:
 *
 * Line 1: Npoly Number of polygons listed in this file
 *
 * Line 2: ID Nverts, ID is the processor ID and Nverts is the number of vertices of the polygon
 *
 * Repeat Nverts times
 * X Y
 *
 * Repeat Npoly times the above starting from Line 2
 *
 */
bool readDomain(std::string filename, DomainListPoly& dpoly) {
    bool outcome = false;
    std::ifstream datafile(filename.c_str());
    if (!datafile.good()) {
        std::cout << "Can't open the file" << filename << std::endl;
    }
    else {
        std::string line;
        getline(datafile, line);
        int Npoly, id, nVerts;
        double x, y;
        {
            std::istringstream inp(line.c_str());
            inp >> Npoly;
        }
        for (int i = 0; i < Npoly; ++i) {
            std::vector<bpoint> pnts;
            getline(datafile, line); {
                std::istringstream inp(line.c_str());
                inp >> id;
                inp >> nVerts;
            }
            for (int j = 0; j < nVerts; ++j) {
                getline(datafile, line);
                std::istringstream inp(line.c_str());
                inp >> x;
                inp >> y;
                pnts.push_back(bpoint(x, y));
            }
            bpoly poly;
            boost::geometry::assign_points(poly, pnts);
            boost::geometry::correct(poly);
            dpoly.insert(std::make_pair(id, poly));
        }
        datafile.close();
        outcome = true;
    }
    return outcome;
}

/**
 * This converts an integer to string with specified number of zero padding.
 *
 * \param i is the integer number to convert to string
 * \param n is the number of zeros to fill in
 * \return the integer in a string format
 */
std::string num2Padstr(int i, int n) {
    std::stringstream ss;
    ss << std::setw(n) << std::setfill('0') << i;
    return ss.str();
}

int main(int argc, char* argv[])
{
    boost::mpi::environment env(argc, argv);
    boost::mpi::communicator world;

    if (world.rank() == 0){
        std::cout << "Redist version 1.2" << std::endl;
    }

    //std::cout << argc << std::endl;
    //std::cout << argv[0] << std::endl;
    inputs inp;
    std::cout << "Current path: " << boost::filesystem::current_path() << std::endl;
    bool tf = readInputFile(argv[1], inp);
    if (!tf)
        return 0;

    DomainListPoly actualDom, expandedDom;
    tf = readDomain(inp.ActualDomainFile, actualDom);
    if (!tf)
        return 0;
    tf = readDomain(inp.ExpadnedDomainFile, expandedDom);
    if (!tf)
        return 0;

    std::vector< PntVel> my_data;
    for (int iproc = 0; iproc < inp.NinitDom; ++iproc) {
        std::string filename = inp.prefix + num2Padstr(iproc, inp.Nzeros) + inp.suffix;
        tf = readVelocityFiles(filename, my_data, world.rank(), expandedDom, actualDom, inp);
    }

    std::cout << "____ Processor " << world.rank() << " is printing..." << std::endl;
    std::string outfilename = inp.NewPrefix + num2Padstr(world.rank(), inp.Nzeros) + inp.suffix;
    std::ofstream outstream;
    outstream.open(outfilename.c_str());
    for (std::vector<PntVel>::iterator it = my_data.begin(); it != my_data.end(); ++it) {
        outstream << std::setprecision(3) << std::fixed
            << it->x << " " << it->y << " " << it->z << " " << it->proc << " ";

        for (int i = 0; i < inp.Nprint; i++){
            outstream << std::setprecision(inp.prec[i]) << std::fixed << it->data[ inp.printOrder[i] - 1 ] << " ";
        }
        outstream << std::endl;
    }
    outstream.close();
    world.barrier();
    return 0;
}


#include <boost/foreach.hpp>
#include "RecoTauTag/RecoTau/interface/RecoTauIsolationMasking.h"
#include "DataFormats/ParticleFlowCandidate/interface/PFCandidateFwd.h"
#include "DataFormats/ParticleFlowCandidate/interface/PFCandidate.h"
#include "DataFormats/Math/interface/deltaR.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "RecoParticleFlow/PFClusterTools/interface/PFEnergyResolution.h"

namespace reco { namespace tau {

namespace {
class DRSorter {
  public:
    DRSorter(const reco::Candidate::LorentzVector& axis):axis_(axis){}
    template<typename T>
    bool operator()(const T& t1, const T& t2) const {
      return reco::deltaR(t1->p4(), axis_) < reco::deltaR(t2->p4(), axis_);
    }
  private:
    const reco::Candidate::LorentzVector axis_;
};
// Sort by descending pt
class PtSorter {
  public:
    PtSorter(){}
    template<typename T>
    bool operator()(const T& t1, const T& t2) const {
      return t1->pt() > t2->pt();
    }
};

// Check if an object is within DR of a track collection
class MultiTrackDRFilter {
  public:
  MultiTrackDRFilter(double deltaR, const std::vector<reco::PFCandidatePtr>& trks)
      :deltaR_(deltaR),tracks_(trks){}
    template <typename T>
    bool operator()(const T& t) const {
      BOOST_FOREACH(const reco::PFCandidatePtr& trk, tracks_) {
        if (reco::deltaR(trk->p4(), t->p4()) < deltaR_)
          return true;
      }
      return false;
    }
  private:
    double deltaR_;
  const std::vector<reco::PFCandidatePtr>& tracks_;
};

double square(double x) { return x*x; }

template<typename T>
std::vector<reco::PFCandidatePtr> convertRefCollection(const T& coll) {
  std::vector<reco::PFCandidatePtr> output;
  output.reserve(coll.size());
  BOOST_FOREACH(const reco::PFCandidatePtr cand, coll) {
    output.push_back(cand);
  }
  return output;
}
}

RecoTauIsolationMasking::RecoTauIsolationMasking(
    const edm::ParameterSet& pset):resolutions_(new PFEnergyResolution) {
  ecalCone_ = pset.getParameter<double>("ecalCone");
  hcalCone_ = pset.getParameter<double>("hcalCone");
  finalHcalCone_ = pset.getParameter<double>("finalHcalCone");
  maxSigmas_ = pset.getParameter<double>("maxSigmas");
}

// Need to explicitly define this in the .cc so we can use auto_ptr + forward
RecoTauIsolationMasking::~RecoTauIsolationMasking(){}

RecoTauIsolationMasking::IsoMaskResult
RecoTauIsolationMasking::mask(const reco::PFTau& tau) const {
  IsoMaskResult output;

  typedef std::list<reco::PFCandidatePtr> PFCandList;
  // Copy original iso collections.
  std::copy(tau.isolationPFGammaCands().begin(),
      tau.isolationPFGammaCands().end(), std::back_inserter(output.gammas));
  std::copy(tau.isolationPFNeutrHadrCands().begin(),
      tau.isolationPFNeutrHadrCands().end(),
      std::back_inserter(output.h0s));

  std::vector<PFCandList*> courses;
  courses.push_back(&(output.h0s));
  courses.push_back(&(output.gammas));
  // Mask using each one of the tracks
  BOOST_FOREACH(const reco::PFCandidatePtr& track,
      tau.signalPFChargedHadrCands()) {
    double trackerEnergy = track->energy();
    double linkedEcalEnergy = track->ecalEnergy();
    double linkedHcalEnergy = track->hcalEnergy();
    math::XYZPointF posAtCalo = track->positionAtECALEntrance();
    // Get the linked calo energies & their errors
    double linkedSumErrSquared = 0;
    linkedSumErrSquared += square(
        resolutions_->getEnergyResolutionEm(linkedEcalEnergy, posAtCalo.eta()));
    linkedSumErrSquared += square(
        resolutions_->getEnergyResolutionHad(
          linkedHcalEnergy, posAtCalo.eta(), posAtCalo.phi()));

    // energyDelta is the difference between associated Calo and tracker energy
    double energyDelta = linkedEcalEnergy + linkedHcalEnergy - trackerEnergy;

    // Sort the neutral hadrons by DR to the track
    //DRSorter sorter(track->p4());
    PtSorter sorter;

    BOOST_FOREACH(PFCandList* course, courses) {
      // Sort by deltaR to the current track
      course->sort(sorter);
      PFCandList::iterator toEatIter = course->begin();
      // While there are still candidates to eat in this course and they are
      // within the cone.
      while (toEatIter != course->end()) {
        const reco::PFCandidate& toEat = **toEatIter;
        double toEatEnergy = toEat.energy();
        double toEatErrorSq = square(resolution(toEat));
        // Check if we can absorb this candidate into the track.
        if (inCone(*track, **toEatIter) &&
            (energyDelta + toEatEnergy)/std::sqrt(
              linkedSumErrSquared + toEatErrorSq) < maxSigmas_ ) {
          energyDelta += toEatEnergy;
          linkedSumErrSquared += toEatErrorSq;
          toEatIter = course->erase(toEatIter);
        } else {
          // otherwise skip to the next one
          ++toEatIter;
        }
      }
    }
  }
  // Filter out any final HCAL objects with in cones about the tracks.
  // This removes upward fluctuating HCAL objects
  if (finalHcalCone_ > 0) {
    MultiTrackDRFilter hcalFinalFilter(finalHcalCone_,
        tau.signalPFChargedHadrCands());
    std::remove_if(output.h0s.begin(), output.h0s.end(), hcalFinalFilter);
  }
  return output;
}

double RecoTauIsolationMasking::resolution(
    const reco::PFCandidate& cand) const {
  if (cand.particleId() == reco::PFCandidate::h0) {
    // NB for HCAL it returns relative energy
    return cand.energy()*resolutions_->getEnergyResolutionHad(cand.energy(),
        cand.eta(), cand.phi());
  } else if (cand.particleId() == reco::PFCandidate::gamma) {
    return resolutions_->getEnergyResolutionEm(cand.energy(), cand.eta());
  } else if (cand.particleId() == reco::PFCandidate::e) {
    // FIXME what is the electron resolution??
    return 0.15;
  } else {
    edm::LogWarning("IsoMask::res (bad pf id)")
      << "Unknown PF ID: " << cand.particleId();
  }
  return -1;
}

bool RecoTauIsolationMasking::inCone(const reco::PFCandidate& track,
    const reco::PFCandidate& cand) const {
  double openingDR = reco::deltaR(track.positionAtECALEntrance(), cand.p4());
  if (cand.particleId() == reco::PFCandidate::h0) {
    return (openingDR < hcalCone_);
  } else if (cand.particleId() == reco::PFCandidate::gamma ||
      cand.particleId() == reco::PFCandidate::e) {
    return openingDR < ecalCone_;
  } else {
    edm::LogWarning("IsoMask::inCone (bad pf id)")
      << "Unknown PF ID: " << cand.particleId()
      << " " <<  reco::PFCandidate::e;
  }
  return -1;
}

}} // end namespace reco::tau


#include <boost/hana/fwd/concept/monad.hpp>


#include <time.h>
#include <cstdlib>
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <random>
#include <boost/program_options.hpp>

#include "../src/cell.h"
#include "../src/util.h"
#include "../src/bitmap_image.hpp"

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

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

  try {

    // Declare the supported options.
    po::options_description opts("Options");
    opts.add_options()
      ("help,h", "display this help message")
      ("xsize,x", po::value<int>()->default_value(64), "size of board in X dimension")
      ("ysize,y", po::value<int>()->default_value(64), "size of board in Y dimension")
      ("zsize,z", po::value<int>()->default_value(1), "size of board in Z dimension")
      ("au",  po::value<double>(), "specify energy of A-U bond pairs")
      ("gc",  po::value<double>(), "specify energy of G-C bond pairs")
      ("gu",  po::value<double>(), "specify energy of G-U bond pairs")
      ("stack",  po::value<double>(), "specify energy of base-pair stacking interactions")
      ("init,i",  po::value<string>(), "specify initial template sequence")
      ("density,d",  po::value<double>(), "specify initial density of monomers")
      ("bond,B",  po::value<double>(), "specify polymerization rate (bond-formation probability)")
      ("rnd,r",  po::value<int>(), "seed random number generator")
      ("total-moves,t",  po::value<long>()->default_value(0), "total number of moves")
      ("unit-moves,u",  po::value<long>()->default_value(0), "number of moves per unit")
      ("folds,f",  "periodically log move count, fold string, energy, radius of gyration, and centroid (single-chain simulations only)")
      ("seqs,S",  "periodically log sequences (for replication simulations)")
      ("monochrome,m",  "no ANSI color codes in logging, please")
      ("period,p", po::value<long>()->default_value(1000), "logging period")
      ("temp,T",  po::value<double>(), "specify temperature")
      ("load,l", po::value<string>(), "load board state from file")
      ("save,s", po::value<string>(), "save board state to file")
      ("json,j", po::value<string>(), "save base-pairing posterior probabilities to JSON file")
      ("bitmap,b", po::value<string>(), "save base-pairing probabilities to bitmap image file")
      ("csv,c", po::value<string>(), "save base-pairing probabilities to CSV file")
      ;

    po::variables_map vm;
    po::parsed_options parsed = po::command_line_parser(argc,argv).options(opts).allow_unregistered().run();
    po::store (parsed, vm);
    po::notify(vm);

    // parse args
    if (vm.count("help")) {
      cout << opts << endl;
      return 1;
    }

    time_t timer;
    time (&timer);
    const int seed = vm.count("rnd") ? vm.at("rnd").as<int>() : timer;
    mt19937 mt (seed);
    cerr << "Random seed is " << seed << endl;

    // create Board
    Board board;
    if (vm.count("load")) {
      ifstream infile (vm.at("load").as<string>());
      if (!infile)
	throw runtime_error ("Can't load board file");
      json j;
      infile >> j;
      board = Board::fromJson (j);
    } else {
      board = Board (vm["xsize"].as<int>(),
		     vm["ysize"].as<int>(),
		     vm["zsize"].as<int>());
    }

    // initialization
    if (vm.count("init"))
      board.addSeq (vm.at("init").as<string>());

    if (vm.count("density"))
      board.addBases (vm.at("density").as<double>(), mt);

    // parameters
    if (vm.count("temp"))
      board.params.temp = vm.at("temp").as<double>();

    if (vm.count("bond"))
      board.params.bondProb = vm.at("bond").as<double>();

    if (vm.count("stack"))
      board.params.stackEnergy = vm.at("stack").as<double>();

    if (vm.count("au"))
      board.params.auEnergy = vm.at("au").as<double>();

    if (vm.count("gc"))
      board.params.gcEnergy = vm.at("gc").as<double>();

    if (vm.count("gu"))
      board.params.guEnergy = vm.at("gu").as<double>();

    // logging
    const long logPeriod = vm.at("period").as<long>();
    const bool logColors = !vm.count("monochrome");
    const bool logFolds = vm.count("folds");
    const bool logSeqs = vm.count("seqs");
    const bool countPairs = vm.count("bitmap") || vm.count("csv") || vm.count("json");
    if (logFolds)
      board.assertLinear();

    // do the simulation
    const long moves = vm.at("total-moves").as<long>() + board.unit.size() * vm.at("unit-moves").as<long>();
    long succeeded = 0, samples = 0;
    map<Board::IndexPair,long> pairCount;
    for (long move = 0; move < moves; ++move) {
      if (board.tryMove (mt))
	++succeeded;
      if (move % logPeriod == 0) {
	if (logFolds)
	  cout << succeeded
	       << " (" << fixed << setprecision(1) << (100. * move / moves) << "%) "
	       << (logColors ? board.coloredFoldString() : board.foldString())
	       << " " << setw(5) << board.foldEnergy()
	       << " " << setw(5) << board.unitRadiusOfGyration()
	       << " (" << to_string_join (board.unitCentroid()) << ")"
	       << endl;
	if (logSeqs) {
	  const auto seqFreqs = board.sequenceFreqs();
	  cout << succeeded
	       << " (" << fixed << setprecision(1) << (100. * move / moves) << "%)";
	  for (auto& sf: seqFreqs)
	    cout << " " << sf.first << "(" << sf.second << ")";
	  cout << endl;
	}
	if (countPairs)
	  for (const auto& ij: board.indexPairs())
	    ++pairCount[ij];
	++samples;
      }
    }

    // report results
    if (moves)
      cerr << "Tried " << moves << " moves, " << succeeded << " succeeded" << endl;

    if (vm.count("bitmap")) {
      bitmap_image image (board.unit.size(), board.unit.size());
      for (const auto& ij_n: pairCount) {
	const int level = (255 * ij_n.second + 128) / samples;
	image.set_pixel (ij_n.first.first, ij_n.first.second, level, level, level);
      }
      image.save_image (vm.at("bitmap").as<string>().c_str());
    }

    if (vm.count("csv")) {
      vguard<vguard<string> > pp (board.unit.size(), vguard<string> (board.unit.size()));
      for (const auto& ij_n: pairCount)
	pp[ij_n.first.first][ij_n.first.second] = to_string (ij_n.second / (double) samples);
      ofstream outfile (vm.at("csv").as<string>());
      if (!outfile)
	throw runtime_error ("Can't save basepair probabilities to CSV file");
      outfile << "*," << join (board.sequence(), ",") << endl;
      for (size_t n = 0; n < pp.size(); ++n)
	outfile << board.unit[n].baseChar() << "," << to_string_join (pp[n], ",") << endl;
    }

    if (vm.count("json")) {
      json js;
      js["samples"] = samples;
      js["sequence"] = board.sequence();
      for (const auto& ij_n: pairCount) {
	const string i = to_string(ij_n.first.first), j = to_string(ij_n.first.second);
	js["prob"][i][j] = ((double) ij_n.second) / samples;
      }
      ofstream outfile (vm.at("json").as<string>());
      if (!outfile)
	throw runtime_error ("Can't save basepair probabilities to JSON file");
      outfile << js << endl;
    }

    if (vm.count("save")) {
      json j = board.toJson();
      ofstream outfile (vm.at("save").as<string>());
      if (!outfile)
	throw runtime_error ("Can't save board file");
      outfile << j << endl;
    } else if (!logFolds)
      cout << board.toJson() << endl;

  } catch (const exception& e) {
    cerr << e.what() << endl;
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}


#include "framework/builder/cfem_framework_builder.h"

#include <functional>
#include <numeric>
#include <vector>

#include <deal.II/fe/fe_q.h>
#include <solver/gmres.h>

#include "convergence/reporter/mpi_noisy.h"
#include "formulation/cfem_diffusion_stamper.h"
#include "data/cross_sections.h"
#include "material/tests/mock_material.h"
#include "problem/tests/parameters_mock.h"
#include "domain/definition.h"
#include "domain/finite_element/finite_element_gaussian.h"
#include "domain/finite_element/tests/finite_element_mock.h"
#include "domain/tests/definition_mock.h"
#include "test_helpers/gmock_wrapper.h"
#include "formulation/tests/cfem_stamper_mock.h"
#include "formulation/tests/angular_stamper_mock.h"
#include "formulation/cfem_saaf_stamper.h"
#include "iteration/initializer/set_fixed_terms_once.h"
#include "iteration/updater/source_updater_gauss_seidel.h"
#include "iteration/updater/angular_source_updater_gauss_seidel.h"
#include "iteration/updater/fixed_updater.h"
#include "iteration/updater/angular_fixed_updater.h"
#include "convergence/final_checker_or_n.h"
#include "convergence/parameters/single_parameter_checker.h"
#include "convergence/moments/single_moment_checker_l1_norm.h"
#include "solver/group/single_group_solver.h"
#include "quadrature/quadrature_set.h"
#include "quadrature/tests/quadrature_set_mock.h"
#include "test_helpers/dealii_test_domain.h"

namespace  {

using namespace bart;

using ::testing::DoDefault, ::testing::NiceMock, ::testing::Return;

template <typename DimensionWrapper>
class IntegrationTestCFEMFrameworkBuilder : public ::testing::Test {
 public:
  static constexpr int dim = DimensionWrapper::value;

  using FrameworkBuilder = framework::builder::CFEM_FrameworkBuilder<dim>;
  using ProblemParameters = NiceMock<problem::ParametersMock>;
  using Material = NiceMock<btest::MockMaterial>;

  IntegrationTestCFEMFrameworkBuilder()
      : mock_material() {}

  FrameworkBuilder test_builder;
  ProblemParameters parameters;
  Material mock_material;

  // Test Parameters
  const int polynomial_degree = 2;
  std::vector<double> spatial_max;
  std::vector<int> n_cells;
  const int n_energy_groups = 3;
  std::array<int, 4> dofs_per_cell_by_dim_{1, 3, 9, 27};

  void SetUp() override;

};

template <typename DimensionWrapper>
void IntegrationTestCFEMFrameworkBuilder<DimensionWrapper>::SetUp() {
  for (int i = 0; i < this->dim; ++i) {
    spatial_max.push_back(10);
    n_cells.push_back(2);
  }

  std::map<problem::Boundary, bool> reflective_bcs{
      {problem::Boundary::kXMin, true},
      {problem::Boundary::kXMax, true},
      {problem::Boundary::kYMin, false},
      {problem::Boundary::kYMax, false},
      {problem::Boundary::kZMin, false},
      {problem::Boundary::kZMax, false},
  };

  ON_CALL(parameters, NEnergyGroups())
      .WillByDefault(Return(n_energy_groups));
  ON_CALL(parameters, NCells())
      .WillByDefault(Return(n_cells));
  ON_CALL(parameters, SpatialMax())
      .WillByDefault(Return(spatial_max));
  ON_CALL(parameters, FEPolynomialDegree())
      .WillByDefault(Return(polynomial_degree));
  ON_CALL(parameters, TransportModel())
      .WillByDefault(Return(problem::EquationType::kDiffusion));
  ON_CALL(parameters, ReflectiveBoundary())
      .WillByDefault(Return(reflective_bcs));
}

TYPED_TEST_CASE(IntegrationTestCFEMFrameworkBuilder,
                bart::testing::AllDimensions);

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildLSAngularQuadratureSet) {
  constexpr int dim = this->dim;
  const int order = 4;
  EXPECT_CALL(this->parameters, AngularQuad())
      .WillOnce(Return(problem::AngularQuadType::kLevelSymmetricGaussian));
  EXPECT_CALL(this->parameters, AngularQuadOrder())
      .WillOnce(Return(order));

  if (dim == 3) {

    auto quadrature_set = this->test_builder.BuildAngularQuadratureSet(
        &this->parameters);

    ASSERT_NE(nullptr, quadrature_set);
    using ExpectedType = quadrature::QuadratureSet<dim>;
    ASSERT_NE(nullptr,
              dynamic_cast<ExpectedType *>(quadrature_set.get()));
    EXPECT_EQ(quadrature_set->size(), order * (order + 2));
  } else {
    EXPECT_ANY_THROW({
         auto quadrature_set = this->test_builder.BuildAngularQuadratureSet(
             &this->parameters);
    });
  }
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildConvergenceReporterTest) {
  using ExpectedType = convergence::reporter::MpiNoisy;

  auto convergence_reporter_ptr = this->test_builder.BuildConvergenceReporter();

  ASSERT_NE(nullptr,
      dynamic_cast<ExpectedType*>(convergence_reporter_ptr.get()));
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildFiniteElementTest) {
  EXPECT_CALL(this->parameters, FEPolynomialDegree())
      .WillOnce(DoDefault());

  auto finite_element_ptr =
      this->test_builder.BuildFiniteElement(&this->parameters);

  // Test that correct type was returned
  auto gaussian_ptr = dynamic_cast<domain::finite_element::FiniteElementGaussian<this->dim>*>(
      finite_element_ptr.get());
  EXPECT_NE(gaussian_ptr, nullptr);

  // Test that correct values were used in instantiation
  EXPECT_EQ(finite_element_ptr->polynomial_degree(), this->polynomial_degree);
  auto dealii_finite_element_ptr = dynamic_cast<dealii::FE_Q<this->dim>*>(
      finite_element_ptr->finite_element());
  EXPECT_NE(dealii_finite_element_ptr, nullptr);
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildDomainTest) {
  constexpr int dim = this->dim;
  auto finite_element_ptr =
      std::make_shared<NiceMock<domain::finite_element::FiniteElementMock<dim>>>();

  EXPECT_CALL(this->parameters, NCells())
      .WillOnce(DoDefault());
  EXPECT_CALL(this->parameters, SpatialMax())
      .WillOnce(DoDefault());

  auto test_domain_ptr =
      this->test_builder.BuildDomain(
          &this->parameters, finite_element_ptr, "1 1 2 2");

  EXPECT_NE(dynamic_cast<domain::Definition<this->dim>*>(test_domain_ptr.get()),
      nullptr);
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildDiffusionStamper) {
  constexpr int dim = this->dim;

  auto finite_element_ptr = std::make_shared<NiceMock<domain::finite_element::FiniteElementMock<dim>>>();
  auto cross_sections_ptr =
      std::make_shared<data::CrossSections>(this->mock_material);
  auto domain_ptr = std::make_shared<NiceMock<domain::DefinitionMock<dim>>>();

  EXPECT_CALL(this->parameters, TransportModel())
      .WillOnce(DoDefault());
  EXPECT_CALL(this->parameters, ReflectiveBoundary())
      .WillOnce(DoDefault());

  auto test_stamper_ptr =
      this->test_builder.BuildStamper(
          &this->parameters,
          domain_ptr,
          finite_element_ptr,
          cross_sections_ptr);

  ASSERT_NE(test_stamper_ptr, nullptr);
  EXPECT_NE(nullptr,
            dynamic_cast<formulation::CFEM_DiffusionStamper<this->dim>*>(
                test_stamper_ptr.get()));

  std::unordered_set<problem::Boundary> boundaries{problem::Boundary::kXMin,
                                                   problem::Boundary::kXMax};
  EXPECT_EQ(test_stamper_ptr->reflective_boundaries(), boundaries);
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildAngularStamperBadEquation) {
  constexpr int dim = this->dim;

  auto finite_element_ptr = std::make_shared<NiceMock<domain::finite_element::FiniteElementMock<dim>>>();
  auto cross_sections_ptr =
      std::make_shared<data::CrossSections>(this->mock_material);
  auto domain_ptr = std::make_shared<NiceMock<domain::DefinitionMock<dim>>>();
  auto quadrature_set_ptr = std::make_shared<quadrature::QuadratureSetMock<dim>>();

  EXPECT_CALL(this->parameters, TransportModel())
      .WillOnce(Return(problem::EquationType::kDiffusion));

  EXPECT_ANY_THROW({
  this->test_builder.BuildAngularStamper(
      &this->parameters,
      domain_ptr,
      finite_element_ptr,
      cross_sections_ptr,
      quadrature_set_ptr);
  });
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder,
    BuildScalarFormulationBadEquationTypes) {
  using EquationType = problem::EquationType;
  std::vector<EquationType> bad_types{EquationType::kEvenParity,
                                      EquationType::kNone,
                                      EquationType::kSelfAdjointAngularFlux};

  constexpr int dim = this->dim;
  auto finite_element_ptr = std::make_shared<NiceMock<domain::finite_element::FiniteElementMock<dim>>>();
  auto cross_sections_ptr =
      std::make_shared<data::CrossSections>(this->mock_material);
  auto domain_ptr = std::make_shared<NiceMock<domain::DefinitionMock<dim>>>();

  for (auto bad_type : bad_types) {
    EXPECT_CALL(this->parameters, TransportModel())
        .WillOnce(Return(bad_type));
    EXPECT_ANY_THROW({
      auto test_stamper_ptr =
          this->test_builder.BuildStamper(&this->parameters,
              domain_ptr, finite_element_ptr, cross_sections_ptr);});
  }
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildSingleGroupSolver) {
  using ExpectedType = solver::group::SingleGroupSolver;

  auto solver_ptr = this->test_builder.BuildSingleGroupSolver(100, 1e-12);

  ASSERT_NE(nullptr, solver_ptr);

  auto dynamic_ptr = dynamic_cast<ExpectedType*>(solver_ptr.get());
  ASSERT_NE(nullptr, dynamic_ptr);

  using ExpectedLinearSolverType = solver::GMRES;

  auto linear_solver_ptr = dynamic_cast<ExpectedLinearSolverType*>(
      dynamic_ptr->linear_solver_ptr());

  ASSERT_NE(nullptr, linear_solver_ptr);
  EXPECT_EQ(linear_solver_ptr->convergence_tolerance(), 1e-12);
  EXPECT_EQ(linear_solver_ptr->max_iterations(), 100);
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildSourceUpdater) {

  auto stamper_ptr = std::make_shared<NiceMock<formulation::CFEM_StamperMock>>();

  auto test_source_updater =
      this->test_builder.BuildSourceUpdater(&this->parameters,
                                            stamper_ptr);
  using SourceUpdater =
      iteration::updater::SourceUpdaterGaussSeidel<formulation::CFEMStamperI>;

  ASSERT_NE(test_source_updater, nullptr);
  EXPECT_NE(nullptr,
            dynamic_cast<SourceUpdater*>(test_source_updater.get()));
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildAngularSourceUpdater) {
  constexpr int dim = this->dim;
  auto stamper_ptr = std::make_shared<NiceMock<formulation::AngularStamperMock<dim>>>();
  auto quadrature_ptr = std::make_shared<quadrature::QuadratureSetMock<dim>>();

  auto test_source_updater =
      this->test_builder.BuildSourceUpdater(&this->parameters,
                                            stamper_ptr,
                                            quadrature_ptr);
  using SourceUpdater = iteration::updater::AngularSourceUpdaterGaussSeidel<formulation::AngularStamperI<dim>>;

  ASSERT_NE(test_source_updater, nullptr);
  EXPECT_NE(nullptr,
            dynamic_cast<SourceUpdater*>(test_source_updater.get()));
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildConvergenceChecker) {
  const double max_delta = 1e-4;
  const int max_iterations = 100;

  auto convergence_ptr =
      this->test_builder.BuildParameterConvergenceChecker(
          max_delta,
          max_iterations);

  using ParameterConvergenceChecker =
      convergence::FinalCheckerOrN<double, convergence::parameters::SingleParameterChecker>;

  ASSERT_NE(convergence_ptr, nullptr);
  EXPECT_NE(nullptr,
            dynamic_cast<ParameterConvergenceChecker*>(convergence_ptr.get()));
  EXPECT_EQ(convergence_ptr->max_iterations(), max_iterations);

}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildMomentConvergenceChecker) {
  const double max_delta = 1e-4;
  const int max_iterations = 100;

  auto convergence_ptr =
      this->test_builder.BuildMomentConvergenceChecker(
          max_delta,
          max_iterations);

  using MomentConvergenceChecker =
  convergence::FinalCheckerOrN<system::moments::MomentVector,
                               convergence::moments::SingleMomentCheckerI>;

  ASSERT_NE(convergence_ptr, nullptr);
  EXPECT_NE(nullptr,
            dynamic_cast<MomentConvergenceChecker*>(convergence_ptr.get()));
  EXPECT_EQ(convergence_ptr->max_iterations(), max_iterations);

}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildInitializer) {

  auto stamper_ptr = std::make_shared<formulation::CFEM_StamperMock>();

  EXPECT_CALL(this->parameters, NEnergyGroups())
      .WillOnce(DoDefault());
  EXPECT_CALL(this->parameters, TransportModel())
      .WillOnce(DoDefault());
  auto iteration_ptr = this->test_builder.BuildInitializer(&this->parameters,
                                                           stamper_ptr);

  ASSERT_NE(nullptr, iteration_ptr);

  using ExpectedInitializerType = iteration::initializer::SetFixedTermsOnce;
  using ExpectedUpdaterType =
  iteration::updater::FixedUpdater<formulation::CFEMStamperI>;

  auto dynamic_iteration_ptr =
      dynamic_cast<ExpectedInitializerType*>(iteration_ptr.get());

  ASSERT_NE(nullptr, dynamic_iteration_ptr);
  ASSERT_NE(nullptr,
      dynamic_cast<ExpectedUpdaterType*>(dynamic_iteration_ptr->fixed_updater_ptr()));

  EXPECT_EQ(dynamic_iteration_ptr->total_groups(), this->n_energy_groups);
  EXPECT_EQ(dynamic_iteration_ptr->total_angles(), 1);
}

TYPED_TEST(IntegrationTestCFEMFrameworkBuilder, BuildAngularInitializer) {
  constexpr int dim = this->dim;
  auto stamper_ptr = std::make_shared<formulation::AngularStamperMock<dim>>();
  auto quadrature_set_ptr = std::make_shared<quadrature::QuadratureSetMock<dim>>();
  const int n_angles = 5;

  EXPECT_CALL(this->parameters, NEnergyGroups())
      .WillOnce(DoDefault());
  EXPECT_CALL(this->parameters, TransportModel())
      .WillOnce(Return(problem::EquationType::kSelfAdjointAngularFlux));
  EXPECT_CALL(*quadrature_set_ptr, size())
      .WillOnce(Return(n_angles));

  auto iteration_ptr = this->test_builder.BuildInitializer(&this->parameters,
                                                           stamper_ptr,
                                                           quadrature_set_ptr);

  ASSERT_NE(nullptr, iteration_ptr);

  using ExpectedInitializerType = iteration::initializer::SetFixedTermsOnce;
  using ExpectedUpdaterType = iteration::updater::AngularFixedUpdater<formulation::AngularStamperI<dim>>;

  auto dynamic_iteration_ptr =
      dynamic_cast<ExpectedInitializerType*>(iteration_ptr.get());

  ASSERT_NE(nullptr, dynamic_iteration_ptr);
  ASSERT_NE(nullptr,
            dynamic_cast<ExpectedUpdaterType*>(dynamic_iteration_ptr->fixed_updater_ptr()));

  EXPECT_EQ(dynamic_iteration_ptr->total_groups(), this->n_energy_groups);
  EXPECT_EQ(dynamic_iteration_ptr->total_angles(), n_angles);
}

template <typename DimensionWrapper>
class IntegrationTestCFEMFrameworkBuilderDealiiDomain :
    public IntegrationTestCFEMFrameworkBuilder<DimensionWrapper>,
    public bart::testing::DealiiTestDomain<DimensionWrapper::value>{
 public:
  void SetUp() override;
};

template <typename DimensionWrapper>
void IntegrationTestCFEMFrameworkBuilderDealiiDomain<DimensionWrapper>::SetUp() {
  IntegrationTestCFEMFrameworkBuilder<DimensionWrapper>::SetUp();
  this->SetUpDealii();
}

TYPED_TEST_SUITE(IntegrationTestCFEMFrameworkBuilderDealiiDomain,
                 bart::testing::AllDimensions);

TYPED_TEST(IntegrationTestCFEMFrameworkBuilderDealiiDomain, BuildSAAFStamper) {
  constexpr int dim = this->dim;

  auto finite_element_ptr = std::make_shared<NiceMock<domain::finite_element::FiniteElementMock<dim>>>();
  auto cross_sections_ptr =
      std::make_shared<data::CrossSections>(this->mock_material);
  auto domain_ptr = std::make_shared<NiceMock<domain::DefinitionMock<dim>>>();
  auto quadrature_set_ptr = std::make_shared<quadrature::QuadratureSetMock<dim>>();

  EXPECT_CALL(this->parameters, TransportModel())
      .WillOnce(Return(problem::EquationType::kSelfAdjointAngularFlux));
  EXPECT_CALL(*domain_ptr, Cells())
      .WillOnce(Return(this->cells_));

  auto test_stamper_ptr =
      this->test_builder.BuildAngularStamper(
          &this->parameters,
          domain_ptr,
          finite_element_ptr,
          cross_sections_ptr,
          quadrature_set_ptr);

  ASSERT_NE(test_stamper_ptr, nullptr);
  EXPECT_NE(nullptr,
            dynamic_cast<formulation::CFEM_SAAF_Stamper<this->dim>*>(
                test_stamper_ptr.get()));
}

} // namespace


#ifndef VPYTHON_PYTHON_FACES_HPP
#define VPYTHON_PYTHON_FACES_HPP

// Copyright (c) 2000, 2001, 2002, 2003 by David Scherer and others.
// See the file license.txt for complete license terms.
// See the file authors.txt for a complete list of contributors.

#include "renderable.hpp"
#include "python/arrayprim.hpp"

#include <boost/python/object.hpp>

namespace cvisual { namespace python {

class faces : public arrayprim_color
{
 protected:
	arrayprim_array<double> normal; // An array of normal vectors for the faces.

	virtual void set_length(size_t);

	bool degenerate() const;
	virtual void gl_render(view&);
	virtual void gl_pick_render(view&);
	virtual vector get_center() const;
	virtual void grow_extent( extent&);
	virtual void get_material_matrix( const view&, tmatrix& );

 public:
	faces();

	// Add another vertex, normal, and color to the faces.
	void append_rgb( const vector&, const vector&, float red=-1, float green=-1, float blue=-1);
	void append( const vector&, const vector&, const rgb& );
	void append( const vector&, const vector& );
	void append( const vector& );

	// This routine was adapted from faces_heightfield.py.  It averages the normal
	// vectors at coincident vertices to smooth out boundaries between facets,
	// for normals that are within about 15 degrees of each other (cos > .95).
	// The intent is to smooth what should be smooth but preserve sharp corners.
	void smooth();
	void smooth_d(const float);

	void make_normals();
	void make_twosided();

	boost::python::object get_normal();
	void set_normal( const double_array& normal);
	void set_normal_v( const vector);
};

} } // !namespace cvisual::python

#endif // !defined VPYTHON_PYTHON_FACES_HPP


// Copyright (c) 2019 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE PipelineAnchorRecomputedTensor0

#include <../random_util.hpp>
#include <algorithm>
#include <boost/test/unit_test.hpp>
#include <fstream>
#include <map>
#include <tuple>
#include <vector>

#include <filereader.hpp>
#include <popart/builder.hpp>
#include <popart/dataflow.hpp>
#include <popart/devicemanager.hpp>
#include <popart/inputshapeinfo.hpp>
#include <popart/ndarraywrapper.hpp>
#include <popart/op/identity.hpp>
#include <popart/op/ipucopy.hpp>
#include <popart/op/l1.hpp>
#include <popart/op/restore.hpp>
#include <popart/op/sigmoid.hpp>
#include <popart/op/stash.hpp>
#include <popart/session.hpp>
#include <popart/sgd.hpp>
#include <popart/tensorinfo.hpp>
#include <popart/tensornames.hpp>
#include <popart/testdevice.hpp>

BOOST_AUTO_TEST_CASE(PipelineAnchorRecomputedTensor0) {

  //  The test:
  //  Since, for pipelined models, the streaming of anchors to host occurs
  //  right at the end, we need to apply some transform in the Ir to ensure
  //  that the anchored tensor is not overwritten by recomputation.
  //  We check here that this works as expected.
  //
  //
  //  The model :
  //
  //         Input
  //           |
  //        sigmoid
  //           |
  //         scale
  //           |            IPU 0
  // - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  //           |            IPU 1
  //          L1 loss
  //
  //
  //  Sigmoid grad uses the output of sigmoid.
  //
  //  Without recomputation, this tensor is stashed.
  //
  //  With pipelining and recomputation, the Input is stashed, and for the
  //  Sigmoid grad in the backwards pass, the Input is restored, and the
  //  Sigmoid activation is recomputed

  enum class RunType {
    SingleDevice = 0,     // exact SGD on a single device
    ContinuousRecompPipe, // Continuous pipelining with recomputation
  };

  using namespace popart;

  auto getRecomputedResult = [](RunType rt) {
    int batchesPerStep     = 3;
    int64_t microBatchSize = 2;
    int64_t sampleSize     = 8;
    std::vector<int64_t> sampleShape{sampleSize};
    std::vector<int64_t> microBatchShape{microBatchSize, sampleSize};
    std::vector<int64_t> stepDataShape{
        batchesPerStep, microBatchSize, sampleSize};
    TensorInfo sampleInfo{"FLOAT", sampleShape};
    TensorInfo microBatchInfo{"FLOAT", microBatchShape};
    int64_t microBatchElms = sampleSize * microBatchSize;

    auto builder     = Builder::create();
    auto aiOnnx      = builder->aiOnnxOpset9();
    auto aiGraphcore = builder->aiGraphcoreOpset1();
    auto input0      = builder->addInputTensor(microBatchInfo);

    auto sig          = aiOnnx.sigmoid({input0});
    auto scale        = aiGraphcore.scale({sig}, 2.0f);
    auto l1           = aiGraphcore.l1loss({scale}, 0.1);
    TensorId actFinal = l1;

    int nIPUs = (rt != RunType::SingleDevice ? 2 : 1);
    std::map<std::string, std::string> deviceOpts{
        {"numIPUs", std::to_string(nIPUs)}};

    SessionOptions userOptions;
    if (rt == RunType::ContinuousRecompPipe) {
      userOptions.virtualGraphMode  = VirtualGraphMode::Manual;
      userOptions.enablePipelining  = true;
      userOptions.autoRecomputation = RecomputationType::Pipeline;

      builder->virtualGraph(sig, 0);
      builder->virtualGraph(scale, 0);
      builder->virtualGraph(l1, nIPUs - 1);
    }
    auto art      = AnchorReturnType("All");
    auto inGradId = reservedGradientPrefix() + input0;
    auto dataFlow = DataFlow(batchesPerStep, {{sig, art}, {inGradId, art}});

    auto session = popart::TrainingSession::createFromOnnxModel(
        builder->getModelProto(),
        dataFlow,
        actFinal,
        ConstSGD(0.01),
        createTestDevice(TEST_TARGET, nIPUs),
        InputShapeInfo(),
        userOptions,
        popart::Patterns(PatternsLevel::Default));

    if (rt == RunType::ContinuousRecompPipe) {
      // Check our assumption, that the Sigmoid op is
      // annoted attribute "recompute: YES"
      auto opSchedule =
          session->getIr().getOpSchedule({}, RequireOptimalSchedule::Yes);
      for (auto op : opSchedule) {
        if (dynamic_cast<SigmoidOp *>(op)) {
          BOOST_CHECK(op->settings.recomputeType == RecomputeType::Recompute);
        }
      }
    }

    session->prepareDevice();

    int64_t stepDataElms = microBatchElms * batchesPerStep;
    std::vector<float> sigData(stepDataElms);
    std::vector<float> inGradData(stepDataElms);
    popart::NDArrayWrapper<float> sigWrapper(sigData.data(), stepDataShape);
    popart::NDArrayWrapper<float> inGradWrapper(inGradData.data(),
                                                stepDataShape);
    std::map<popart::TensorId, popart::IArray &> anchors = {
        {sig, sigWrapper}, {inGradId, inGradWrapper}};

    // generate new samples
    DefaultRandomEngine eng(101);
    UniformRealDistribution<float> fdis(-1.f, +1.f);
    int64_t samplesPerStep = batchesPerStep * microBatchSize;
    std::vector<float> v_input_x(stepDataElms);
    for (int i = 0; i < samplesPerStep; ++i) {
      for (int j = 0; j < sampleSize; ++j) {
        auto stepIndex       = i * sampleSize + j;
        v_input_x[stepIndex] = fdis(eng);
      }
    }
    popart::NDArrayWrapper<float> input_x_wrapper(v_input_x.data(),
                                                  {"FLOAT", stepDataShape});
    std::map<popart::TensorId, popart::IArray &> inputs = {
        {input0, input_x_wrapper}};
    popart::StepIO stepio(inputs, anchors);

    // process the samples
    session->run(stepio);

    return sigData;
  };

  BOOST_CHECK(getRecomputedResult(RunType::SingleDevice) ==
              getRecomputedResult(RunType::ContinuousRecompPipe));
}