#include "Bundle.h"

Bundle::Bundle(
               const Object* const hostObject,
               unordered_map<int64, Object*>& mobileObjects)
{
    for (int i = 0; i < POINTS; ++i) {
        if (hostObject->getNumber(i) != 0) {
            lines[i] = new OneLine(hostObject, i, mobileObjects);
        }
        else {
            lines[i] = nullptr;
        }
    }
}

Bundle::~Bundle()
{
    for (int i = 0; i < POINTS; ++i) {
        if (lines[i] != nullptr) {
            delete lines[i];
        }
    }
}


#include "CascadeDamage.h"

CascadeDamage::~CascadeDamage()
{
    cleanDamage();
}

void CascadeDamage::generateNeutronDamage(const double &energy, int & ndef)
{
    const double fcli = 0.5; // Fraction of interstitials in clusters
    const double fclv = 0.2; // Fraction of vacancies in clusters.
    /* Both from Fikar et al (2009), Troev et al (2011). */
    const double fmd = 1.0; // kMC escape probabilty.
    const int types = 2; // Only vacancies and SIAs in a cascade.
    
    double nrt, eta;
    double psia, pv;
    int n = 0;
    int i, j, k = 0;
    
    while (n < 1) // Repeat until at least one stable Frenkel pair produced.
        n = rint(1.49*pow(energy, 0.82)); // Relation from Fikar et al (2009).
    /* Dynamically allocate first dimension of pointer array: */
    damage.resize(types, nullptr); // [0]: vacancy defects; [1]: SIA defects.
    /* Dynamically allocate second dimension of pointer array: */
    for (i = 0; i<types; i++) {
        damage[i] = new int[n];
    }
    // Initialize array:
    for (j = 0; j<types; j++)
        for (i = 0; i<n; i++)
            damage[j][i] = 0;
    /* damage[2][n] is an integer array containing the counts (from 0 to the total number of defects)
     of defects and clusters ([0]: vacancy; [1]: SIA) produced by the cascade
     cascade. For example, damage[1][3]=2 means that two I3 clusters were formed. */
    ndef = n;
    if (n > 1) {
        n *= fmd;
        psia = 1.0 - pow((1 - fcli), 1.0 / ((double)types * (double)n - 1.0));
        // Parameter for the binomial sampling of SIA-clusters.
        pv = 1.0 - pow((1 - fclv), 1.0 / ((double)types * (double)n - 1.0));
        // Parameter for the binomial sampling of V-clusters.
        
        // Randomly sample SIA-clusters from the Binomial distribution:
        int nv = 0;
        while (nv <= n) {
            k = Binomial((n - 1), pv) + 1;
            damage[0][k - 1]++;
            nv += k;
        }
        int nsia = 0;
        while (nsia <= n) {
            k = Binomial((n - 1), psia) + 1;
            damage[1][k - 1]++;
            nsia += k;
        }
        // Complete with remaining point defects:
        if (nv < nsia) damage[0][0] += (nsia - nv);
        if (nsia < nv) damage[1][0] += (nv - nsia);
    }
    else {
        double xi = rand()/RAND_MAX;
        if (xi<fmd) {
            damage[0][0] = 1;
            damage[1][0] = 1;
        }
    }
}

void CascadeDamage::generateIonDamage(const double & energy, int & ndef)
{
    const double fcli = 0.55; // Fraction of interstitials in clusters
    const double fclv = 0.25; // Fraction of vacancies in clusters, both from: [L Malerba, JNM 351 (2006) 28].
    const double fmd = 0.65; // kMC escape probabilty
    // from [Soneda and Diaz de la Rubia, Phil Mag A 78 (1998) 995].
    const int types = 2; // Only vacancies and SIAs in a cascade.
    
    double nrt, eta;
    double psia, pv;
    int n = 0;
    int k, j, i;
    
    while (n < 1) { // Repeat until at least one stable Frenkel pair produced.
        eta = exp( -3.57 * energy ) + 0.3; // from: [L Malerba, JNM 351 (2006) 28].
        nrt = eta * 0.8 * energy / TDE / 2;
        n = rint(Poisson(nrt)); // Given the average number of defects, sample  from some statistical
        // distribution to give some variability (Poisson distribution only.
    }
    
    // Dynamically allocate first dimension of pointer array:
    damage.resize(types, nullptr); // [0]: vacancy defects; [1]: SIA defects.
    
    // Dynamically allocate second dimension of pointer array:
    for (i = 0; i<types; i++) {
        damage[i] = new int[n];
    }
    
    // This allocation overdimensions damage[][].
    
    // Initialize array:
    for (j = 0; j < types; ++j)
        for (i = 0; i<n; ++i)
            damage[j][i] = 0;
    
    /* damage[2][n] is an integer array containing the counts (from 0 to the total number of defects)
     of defects and clusters ([0]: vacancy; [1]: SIA) produced by the cascade
     cascade. For example, damage[1][3]=2 means that two I3 clusters were formed. */
    
    ndef = n;
    if (n > 1) {
        n *= fmd;
        psia = 1.0 - pow((1.0 - fcli), 1.0 / ((double)types * (double)n - 1.0));
        // Parameter for the binomial sampling of SIA-clusters.
        pv = 1.0 - pow((1.0 - fclv), 1.0 / ((double)types * (double)n - 1.0));
        // Parameter for the binomial sampling of V-clusters.
        
        // Randomly sample SIA-clusters from the Binomial distribution:
        int nv = 0;
        while (nv < n) {
            k = Binomial((n - 1), pv) + 1;
            damage[0][k - 1]++;
            nv += k;
        }
        int nsia = 0;
        while (nsia < n) {
            k = Binomial((n - 1), psia) + 1;
            damage[1][k - 1]++;
            nsia += k;
        }
        
        // Complete with remaining point defects:
        if (nv < nsia) damage[0][0] += (nsia - nv);
        else if (nsia < nv) damage[1][0] += (nv - nsia);
    }
    else {
        double xi = (double)rand()/RAND_MAX;
        if (xi < fmd) {
            damage[0][0] = 1;
            damage[1][0] = 1;
        }
    }
}

const int CascadeDamage::getDamage(const int & n, const int & m) const
{
    return damage[n][m];
}

void CascadeDamage::cleanDamage()
{
    if (!damage.empty()) {
        for (int i = 0; i < damage.size(); ++i) {
            delete[] damage[i];
        }
        damage.clear();
    }
}

int CascadeDamage::size()
{
    return damage.size();
}


#include "cpdf.h"

Cpdf::Cpdf()
{
    
    char cpdf[20];
    ifstream fc;
    /*initialize max Possibility*/
    for(int i=0; i<POINTS; i++){
        maxPossibility[i] = 0;
    }
    for(int fileNumber = 1; fileNumber < POINTS; fileNumber++){
    	/* first point is surface */
        vector<double> tempEnergy, tempCumul;
        double energy= 0.0 , cumul = 0.0;
        sprintf(cpdf,"cpdf%d.txt",fileNumber);
        fc.open(cpdf);
        string oneLine;
        stringstream lineHold;
        while (getline(fc, oneLine)) {
            lineHold.str(oneLine);
            lineHold >> energy >> cumul;
            tempEnergy.push_back(energy);
            tempCumul.push_back(cumul);
            lineHold.clear();
        } //read a file and store/create corresponding vectors
        pair<int, vector<double> > newNodeOne(fileNumber, tempEnergy);
        pair<int, vector<double> > newNodeTwo(fileNumber, tempCumul);
        recoilEnergy.insert(newNodeOne);
        cumulPossibility.insert(newNodeTwo);
        /* add new nodes to members */
        size[fileNumber] = (int)tempEnergy.size();
        maxPossibility[fileNumber] = tempCumul[(int)tempEnergy.size()-1];
        /* store size and possibility for SCD usage in case */
        tempEnergy.clear();
        tempCumul.clear();
        /* clean two vectors and start next loop */
        fc.close();
    }
}

double Cpdf::samplePkaEnergy(const double & xi, const int & n) const
{
    int  i, index = 0;
    double slope;
    unordered_map<int, vector<double> >::const_iterator gotEnergy = recoilEnergy.find(n);
    unordered_map<int, vector<double> >::const_iterator gotCumul = cumulPossibility.find(n);
    vector<double> energy = gotEnergy->second;
    vector<double> cumul = gotCumul->second;
    for (i = 1; i < size[n] ; ++i) {
        if ((xi > cumul[i - 1]) && (xi < cumul[i])) {
            index = i;
            break;
        }
    }
    /* Interpolation: */
    slope = (energy[index] - energy[index - 1]) / (cumul[index] - cumul[index - 1]);
    return slope*(xi - cumul[index - 1]) + energy[index - 1];
}

double Cpdf::getMaxPossibility(const int& n){
    return maxPossibility[n];
}


#include "Damage.h"
// Damage.cpp --  implementations of the damage class

Damage::Damage()
{
    int index;
    for (index = 0; index < POINTS; ++index) {
        totalRate[index] = 0.0;
    }
    readFile();
    //for(index=0; index<1; ++index){
    for (index = 0; index < POINTS; ++index) {
        computeDamageZero(index);
        if (CHANNELS > 1) {
            computeDamageOne(index);
        }
        if (CHANNELS > 2) {
            computeDamageTwo(index);
        }
        if (CHANNELS > 3) {
            int iindex;
            for (iindex = 3; iindex < CHANNELS; ++iindex) {
                computeDamageOther(index, iindex);
            }
        }
    }
}


Reaction Damage::selectDamage(const int & n, double & randRate)
{
    int index = 0;
    double tempRate = randRate;
    if (totalRate[n] < randRate) {
        randRate -= totalRate[n];
        return NONE;
        /* this block will never be excuted */
    }
    while (index < CHANNELS) {
        if (damage[n][index] >= tempRate) {
            if (index == 0) {
                return PARTICLE;
            }
            else if (index == 1) {
                return HE;
            }
            else if (index == 2) {
                return H;
            }
        }
        else {
            tempRate -= damage[n][index];
            ++index;
        }
    }
    return NONE;
}

void Damage::display(const int &count) const
{
    cout << "Damage information in element " << count << endl;
    for (int i = 0; i < CHANNELS; i++) {
        cout << "Damage[" << i << "]: " << damage[count][i] << endl;
    }
    cout << "Total Damage Rate: " << totalRate[count] << endl;
}

const double Damage::getTotalDamage(const int & n) const
{
    return totalRate[n];
}

/* private method implementation */
void Damage::readFile()
{
    int index = 0;
    ifstream fd;
    fd.open("damage.txt");
    string oneLine;
    stringstream lineHold;
    while (getline(fd, oneLine)&& index < POINTS) {
        lineHold.str(oneLine);
        lineHold >> DPA_RATE[index] >> NRT[index];
        lineHold.clear();
        ++index;
    }
    fd.close();
}

void Damage::computeDamageZero(const int & n)
{
    //damage[n][0] = 0.0; /* after damage */
    
    if(n != 0){
        if( NRT[n] == 0.0 ){
            
            damage[n][0] = 0.0;
        }else{
            damage[n][0] = (DPA_RATE[n] * DENSITY*VOLUME / NRT[n]);
        }
        
    }else{
        damage[n][0] = 0.0;
    }
    //damage[n][0] = DPA_RATE[n] * DENSITY*VOLUME / NRT[n];
    totalRate[n] += damage[n][0];
}

void Damage::computeDamageOne(const int & n)
{
    damage[n][1]= RATIO_HE*1.0e-06*DPA_RATE[n]*DENSITY*VOLUME;
    totalRate[n] += damage[n][1];
}

void Damage::computeDamageTwo(const int & n)
{
    double volume = VOLUME/36 * SURFACE_THICKNESS;
    double concentration_H = 1.34e+4;
    double flux_H = 4.00e+16;
    if (n == 0) {
        //damage[n][2] = concentration_H * flux_H * volume;
        damage[n][2] = 0.0; /* no hydrogen insertion */
    }
    else {
        damage[n][2] = 0.0;
    }
    //damage[n][2] = RATIO_H*1.0e-06*DPA_RATE[n] * DENSITY*VOLUME;
    totalRate[n] += damage[n][2];
}

void Damage::computeDamageOther(const int & n, const int & m)
{
    damage[n][m] = 0.0;
    totalRate[n] += damage[n][m];
}

double Damage::getDpaRate(const int& n){
    return DPA_RATE[n];
}


// SRSCD -- Spatially Resolved Stochastic Cluster Dynamics
// Q Yu, January 2017.

#include<ctime>
#include<unistd.h>
#include<time.h>
#include"SCDWrapper.h"

int main() {
    //fork();
    SCDWrapper* srscd = new SCDWrapper(); /* establish spatially resolved scd */
    int64 theOtherKey = 0;
    Object* hostObject = nullptr;
    Reaction reaction = ERROR;
    int pointIndex = -1;
    int iStep = 0;
    double random;
    double advTime = 0.0;
    double dt = 0.0;
    double accTime = 0.0;
    long double bulkRate = 0.0; /* total rate of the whole bulk */
    int inputH = 0;
    double totalDPA = 0.2;
    double dpa = 0.0;
    fstream st;
    /* check whether to restart*/
    restart(iStep, advTime, srscd);
    srscd->getAndExamineRate();
    /* check ended */
    srand(time(0));
    /*display damage*/
    srscd->displayDamage();
    srscd->displayAllObject();
    srscd->drawSpeciesAndReactions(advTime);
    clock_t t0, t1;
    t0 = clock();
    //srscd->drawHD(advTime);
    //while (advTime < TOTAL_TIME) {
    while(dpa < totalDPA){
        hostObject = srscd->selectReaction(theOtherKey, reaction, pointIndex);/* choose an event */
        srscd->processEvent(reaction, hostObject, pointIndex, theOtherKey, advTime, accTime); /* process event */
        if(reaction == 6){
            accTime = 0.0;
        }
        
        if(iStep%PSTEPS == 0){
            t1 = clock()-t0;
            st.open("st.txt", ios::app);
            st << (float)t1/CLOCKS_PER_SEC << "  "<< dpa << endl;
            
            /*
            cout << "t = " << advTime << endl;
            cout <<"iStep = "<< iStep << endl;
            cout<<"dt= " << dt <<endl;
            cout<<"BulkRate = "<<bulkRate<<endl;
             */
            srscd->drawSpeciesAndReactions(advTime);
            srscd->drawDamage(advTime);
            srscd->writeFile(advTime, iStep);
            //srscd->writeVacancy();
            //srscd->drawHD(advTime);
            st.close();
        }
        bulkRate = srscd->getAndExamineRate(); /* calculate the bulk rate */
        ++iStep;
        do {
            random = (double)rand() / RAND_MAX;
        } while (random == 0);
        dt = (-1) / bulkRate*log(random);
        accTime +=dt;
        advTime += dt;
        dpa = srscd->getTotalDpa();
    }
    srscd->drawSpeciesAndReactions(advTime);
    srscd->drawDamage(advTime);
    srscd->writeVacancy();
    srscd->writeSinkFile(advTime, iStep);
    cout<<"dpa = "<<dpa<<endl;
    cout << "Finished, Bye" << endl;
    return 0;
}



#include "Object.h"
#include<cmath>
// Object.cpp -- implementations of Object class

/* public method implementations */
Object::Object(
               const int64 & key,
               const int & count,
               const int& n) :oKey(key), totalNumber(0), bindSH(0.0)
{
    setAttributes(key);
    setProperties(count, n);
}

Object::Object(const int * attr,
               const int& count,
               const int& n): totalNumber(0)
{
    for (int i = 0; i < LEVELS; i++) {
        attributes[i] = attr[i];
    }
    oKey = 0;
    setKey();
    setProperties(count, n);
}

Object::Object(const int64 &key, const int *number):oKey(key), totalNumber(0)
{
    setAttributes(key);
    dimensionality = setDimensionality();
    computeDiffCoeff();
    computeBindTerm();
    computeR1R1e();
    computeSinks();
    setNumber();
    for (int i = 0; i < POINTS; i++) {
        addNumber(i, number[i]);
    }
    
}


void Object::addNumber(const int & count, const int& n)
{
    number[count] += n;
    totalNumber += n;
}

void Object::reduceNumber(const int & count)
{
    --number[count];
    --totalNumber;
}

int Object::signof(const int64 & key) const
{
    return (key < 0) ? -1 : 1;
}

double Object::zero(const int & defect)
{
    return (abs(defect) > 1) ? 1.0 : 0.0;
}

int64 Object::getKey() const
{
    return oKey;
}

double Object::getDiff() const
{
    return diffusivity;
}


int Object::getNumber(const int & count) const
{
    return number[count];
}

int Object::getTotalNumber() const
{
    return totalNumber;
}

int Object::getAttri(const int & index) const
{
    return attributes[index];
}

double Object::getSink() const
{
    return sinkStrength;
}

long double Object::getBind(const int & index) const
{
    return bind[index];
}

double Object::getR1() const
{
    return r1;
}

double Object::getR1e() const
{
    return r1e;
}

int Object::getDim() const
{
    return dimensionality;
}

double Object::getBindSH() const
{
	return bindSH;
}

void Object::getThreeNumber(const int & count, int* objectN) const
{
    /*
     * objectN[0] = object number in this element
     * objectN[1] = object number in the previous element
     * objectN[2] = object number in the next element
     */
    objectN[0] = number[count];
    if (count == 0) {
        /* when this is the surface element */
        objectN[1] = 0; /* Question, ask Jaime */
        objectN[2] = number[count + 1];
    }
    else if (count == POINTS - 1) {
        /* when this is the last element*/
        objectN[1] = number[count - 1];
        objectN[2] = 0; /* Question, ask Jaime */
    }
    else {
        objectN[1] = number[count - 1];
        objectN[2] = number[count + 1];
    }
}

void Object::display()
{
    cout << "Information of Object " << oKey << ": " << endl;
    cout << "Attributes: ";
    for (int i = 0; i < LEVELS; ++i) {
        cout << attributes[i] << "    ";
    }
    cout << endl;
    cout << "number: ";
    for (int i = 0; i < POINTS; ++i) {
        cout << number[i] << "    ";
    }
    cout << endl;
    cout << "total number: " << totalNumber << endl;
    cout << "dimensionality: " << dimensionality << endl;
    cout << "diffusivity: " << diffusivity << endl;
    cout << "bind term: ";
    for (int i = 0; i < LEVELS; ++i) {
        cout << bind[i] << "    ";
    }
    cout << endl;
    cout << "r1 = " << r1 << endl;
    cout << "r1e = " << r1e << endl;
    cout << "sink strength: " << sinkStrength << endl;
    cout << endl;
}

/* private method impementations */
void Object::setKey()
{
    for (int i = 0; i < LEVELS; ++i) {
        oKey += labs(attributes[i])*((int64)pow(10.0, (double)EXP10*(LEVELS - 1 - i)));
    }
    oKey *= signof(attributes[0]);
}

void Object::setAttributes(const int64 & key)
{
    int64 tempKey = abs(key);
    for (int i = 0; i < LEVELS; i++) {
        attributes[i] = double(tempKey) / pow(10.0, (double)EXP10*(LEVELS - 1 - i));
        tempKey -= ((int64)attributes[i])*((int64)pow(10.0, (double)EXP10*(LEVELS - 1 - i)));
    }
    attributes[0] *= signof(key);
}

void Object::setNumber()
{
    for (int i = 0; i < POINTS; i++) {
        number[i] = 0;
        totalNumber += number[i];
    }
}

int Object::setDimensionality()
{
    return attributes[0] > 4 ? 1 : 3;
}

void Object::computeR1R1e()
{
    int ndef = attributes[0];
    if (ndef <= 0) {
        r1 = zero(ndef)*pow(3.0*fabs((double)ndef)*avol / 4.0 / PI, 0.333333333333333333) + jumped;
        if (ndef != 0)
            r1e = pow(3.0*(fabs((double)ndef) - 1)*avol / 4.0 / PI, 0.333333333333333333) + jumped;
        else
            r1e = jumped;
    }
    else if (ndef > 0) {
        r1 = zero(ndef)*sqrt((double)ndef*avol / jumped / PI) + jumped;
        r1e = zero(ndef)*sqrt(((double)ndef - 1)*avol / jumped / PI) + jumped;
    }
}

void Object::computeDiffCoeff()
{
    const double fi = 0.9, fv = 0.7; // Diffusion correlationm factors.
    const double gi = 0.5, gv = 0.125; // Geometric factor for diffusion.
    double prefactor = 0, energy_m = 0;
    int check_all = 0;
    int check_He = 0;
    int check_H = 0;
    // int check_C = 0;
    int i;
    
    for (i = 1; i < LEVELS; i++) {
        check_all |= attributes[i];
        if (i >= 2) check_He |= attributes[i];
        if (i >= 3) check_H |= attributes[i];
    }
    /* check_all: 0 when this is a pure defect cluster.
     *  check_He:  0 when this is a cluster with He.
     *  check_H:   0 when this is a cluster with H.
     */
    
    // Pure defect clusters:
    if (!check_all) {
        
        if (attributes[0] > 0) { // SIAs
            if (abs(attributes[0]) == 1) { // 1I
                prefactor = 8.744e-4;
                energy_m = 0.009;
            }else if (abs(attributes[0]) == 2) { // 1I
                prefactor = 7.97e-4;
                energy_m = 0.024;
            }else if (abs(attributes[0]) == 3) { // 1I
                prefactor = 3.92e-4;
                energy_m = 0.033;
            }
            else if(abs(attributes[0]) > 3) { // >1I
                prefactor = gi*jumped*jumped*fi*NU0*pow(fabs(attributes[0]), -0.5);
                energy_m = 0.013;
            }
        }
        else if (attributes[0] < 0) { // Vacancies.
            if (abs(attributes[0]) == 1) { // 1V
                
                prefactor = 1.77e-2;
                energy_m = 1.29;
            }
            else if (abs(attributes[0]) == 2) { // >1V
                
                prefactor = 2.91e-5;
                energy_m = 1.66;
            }else if (abs(attributes[0]) > 2) { // >1V
                
                prefactor = gv*jumped*jumped*fv*NU0*pow(0.001, fabs(attributes[0]) - 1.0);
                energy_m = 1.66;
            }
        }
    }
    else if (!check_He) {
        
        if (attributes[0] != 0) { // SIA-He and V-He clusters immobile.
            prefactor = 0.0;
        }
        else if (attributes[1] == 1) { // He1.
            prefactor = gi*jumped*jumped*NU0;
            energy_m = 0.01;
        }
        else  if (attributes[1] == 2) { // He2.
            prefactor = gi*jumped*jumped*NU0*0.01;
            energy_m = 0.03;
        }
        else  if (attributes[1] == 3) { // He3.
            prefactor = gi*jumped*jumped*NU0*0.01;
            energy_m = 0.05;
        }
        else  if (attributes[1] > 3) { // He>3.
            prefactor = gi*jumped*jumped*NU0*0.01;
            energy_m = 0.06;
        }
        else prefactor = 0.0;
    }
    else if (!check_H) {
        if (attributes[0] != 0) { // SIA-H and V-H clusters immobile.
            prefactor = 0.0;
        }
        else if (attributes[2] == 1 || attributes[2] == 2) { // H1, H2.
            /*
            //number below is from ppt Liu(2015) at 300K D_H=10e-9 m^2/s = 10e-5 cm^2/s
            prefactor = 3.8e-3;
            energy_m = 0.41;
             */
            prefactor = 1.58e-3;
            energy_m = 0.25;
        }
        else
            prefactor = 0.0;
    }
    /* All data from [CS Becquart et al., J Nucl Mater 403 (2010) 75] */
    diffusivity = prefactor*exp(-energy_m / KB / TEMPERATURE);
}

void Object::computeBindTerm()
{
    long double energy_d[LEVELS] = { 0.0 };
    long double energy_b = 0.0, energy_m = 0.0;
    double attfreq = 1.0;
    double efi = 9.96, emi = 0.013; // Ab initio migration and formation energies of V and SIA in pure W.
    double efv = 3.23, emv = 1.66;
    double eb2v = -0.1, eb2i = 2.12, eb2he = 1.03;
    double efhe = 4.0, emhe = 0.01;
    double emh = 0.39;
    int check_all = 0, check_He = 0, check_H = 0;
    int i;
    
    for (i = 0; i < LEVELS; i++) {
        bind[i] = 0.0;
        if (i >= 1) check_all |= attributes[i];
        if (i >= 2) check_He |= attributes[i];
        if (i >= 3) check_H |= attributes[i];
    }
    /**
     * bind energy positive(eg. 5 eV) means easy to get together, hard to dissociate, when dissociate, absorb 5eV energy
     * bind energy negative(eg. -5 eV) means hard to get together, easy to dissociate, when dissociate, release 5eV energy
     **/
    // Pure defect clusters:
    if (!check_all) {
        if (attributes[0]>0) { // SIAs
            energy_m = emi;
            if (abs(attributes[0]) == 1) { // 1I
                attfreq = 0.0;
            }
            else if (abs(attributes[0]) == 2) { // 2I
                energy_b = 2.12;
            }
            else if (abs(attributes[0]) == 3) { // 3I
                energy_b = 3.02;
            }
            else if (abs(attributes[0]) == 4) { // 4I
                energy_b = 3.60;
            }
            else if (abs(attributes[0]) == 5) { // 5I
                energy_b = 3.98;
            }
            else if (abs(attributes[0]) == 6) { // 6I
                energy_b = 4.27;
            }
            else if (abs(attributes[0]) == 7) { // 7I
                energy_b = 5.39;
            }
            else if (abs(attributes[0])>7) // > 7I
                energy_b = efi + (eb2i - efi)*(pow(fabs((double)attributes[0]), 0.6666667) - pow((fabs((double)attributes[0]) - 1.0), 0.6666667)) / 0.5847;
        }
        else if (attributes[0]<0) { // Vacancies.
            energy_m = emv;
            if (abs(attributes[0]) == 1) { // 1V
                attfreq = 0.0;
            }
            else if (abs(attributes[0]) == 2) { // 2V
                energy_b = eb2v;
            }
            else if (abs(attributes[0]) == 3) { // 3V
                energy_b = 0.04;
            }
            else if (abs(attributes[0]) == 4) { // 4V
                energy_b = 0.64;
            }
            else if (abs(attributes[0]) == 5) { // 5V
                energy_b = 0.72;
            }
            else if (abs(attributes[0]) == 6) { // 6V
                energy_b = 0.89;
            }
            else if (abs(attributes[0]) == 7) { // 7V
                energy_b = 0.72;
            }
            else if (abs(attributes[0]) == 8) { // 8V
                energy_b = 0.88;
            }
            else if (abs(attributes[0])>8) // > 8V
                energy_b = efv + (eb2v - efv)*(pow(fabs((double)attributes[0]), 0.6666667) - pow((fabs((double)attributes[0]) - 1.0), 0.6666667)) / 0.5874;
        }
        energy_d[0] = energy_b; // + energy_m
        bind[0] = attfreq*exp(-energy_d[0] / KB / TEMPERATURE);
    }
    // He-defect clusters:
    else if (!check_He) {
        
        if (attributes[0]<0) { // He-V clusters:
            double ratio = fabs(((double)attributes[1]) / ((double)attributes[0]));
            printf("%dV - %dHe\n", abs(attributes[0]), abs(attributes[1]));
            if (abs(attributes[0]) == 1 && abs(attributes[1]) == 1) { // 1V-1He
                energy_d[0] = 4.6;
                energy_d[1] = energy_d[0];
            }
            else {
                energy_d[0] = 2.4 + 3.5*log10(ratio) + 1.7*log10(ratio)*log10(ratio);
                // binding energy of V to cluster.
                energy_d[1] = 4.6 - 1.1*log10(ratio) - 0.3*log10(ratio)*log10(ratio);
                // binding energy of He to cluster.
            }
            bind[0] = attfreq*exp(-energy_d[0] / KB / TEMPERATURE);
            bind[1] = attfreq*exp(-energy_d[1] / KB / TEMPERATURE);
        }
        else if (attributes[0]>0) // He-SIA clusters.
            attfreq = 0.0; // No dissociation between He and SIA clusters.
        else if (attributes[0] == 0) { // pure He clusters.
            if (attributes[1] == 1) { // He1.
                attfreq = 0;
            }
            else if (attributes[1] == 2) { // He2.
                energy_b = 1.03;
            }
            else if (attributes[1] == 3) { // He3.
                energy_b = 1.36;
            }
            else if (attributes[1] == 4) { // He4.
                energy_b = 1.52;
            }
            else { // He>4.
                energy_b = efhe + (eb2he - efhe)*(pow(fabs((double)attributes[0]), 0.6666667) - pow((fabs((double)attributes[0]) - 1.0), 0.6666667)) / 0.5874;
            }
            energy_d[1] = energy_b + emhe;
            bind[1] = attfreq*exp(-energy_d[1] / KB / TEMPERATURE);
        }
    }
    
    // H-defect clusters:
    // H-defect clusters:
    else if (!check_H){
        if (attributes[0]<0) { // H-V clusters:
            double ratio = fabs( ((double) attributes[2])/((double) attributes[0]) );
            //printf("%dV - %dH\n", abs(attr[0]), abs(attr[2]));
            /*this part is for binding energy of mV-nH that try to dissociate a V from Li Xiaochun(2015)*/
            if (ratio==1 /*&& attributes[0]> -5 && attributes[2]< 15*/) { // V-H.
                energy_b = 1.60;
            } else if (ratio==2) { // V-H2
                energy_b = 2.10;
            } else if (ratio==3) { // V-H3
                energy_b = 2.40;
            } else if (ratio==4) { // V-H4
                energy_b = 3.90;
            } else if (ratio==5) { // V-H5
                energy_b = 4.35;
            } else if (ratio==6) { // V-H6
                energy_b = 5.80;
            } else if (ratio==7) { // V-H7
                energy_b = 7.0;
            } else if(ratio == 8){ // V-H8
                energy_b = 8.25;
            } else if(ratio == 9){ // V-H9
                energy_b = 9.80;
            } else if(ratio == 10){ // V-H10
                energy_b = 11.25;
            } else if(ratio>10){
                
                energy_d[0] = 1.91 + 0.0974 * ratio * ratio; //extrapolation
                // if happened form this, dissociate as soon as possible
            }
            energy_d[0] = energy_b + emv;
            /*this part is for binding energy of mV-nH+1H from Ohsawa(2015)*/
            if (ratio == 1 ) { // V-H.
                energy_b = 1.223;
            } else if (ratio==2 ) { // V-H2
                energy_b = 1.192;
            } else if (ratio==3 ) { // V-H3
                energy_b = 1.077;
            } else if (ratio==4 ) { // V-H4
                energy_b = 0.958;
            } else if (ratio==5 ) { // V-H5
                energy_b = 0.862;
            } else if (ratio==6 ) { // V-H6
                energy_b = 0.645;
            } else if (ratio==7 ) { // V-H7
                energy_b = 0.243;
            } else if(ratio == 8 ){ // V-H8
                energy_b = 0.306;
            } else if(ratio == 9 ){ // V-H9
                energy_b = 0.191;
            } else if(ratio == 10){ // V-H10
                energy_b = 0.184;
            } else if(ratio == 11){ // V-H11
                energy_b = 0.004;
            } else if(ratio == 12){ // V-H12
                energy_b = 0.367;
            } else if(ratio == 13){ // V-H13
                energy_b = -0.889;
            } else if(ratio == 14){ // V-H14
                energy_b = 0.747;
            } else if(ratio == 15){ // V-H15
                energy_b = -1.242;
            } else{
                energy_b = 1.45 - 0.125*ratio-0.00159*ratio*ratio; //extrapolation
               // energy_b = (numeric_limits<double>::min)(); // if happenedly get this large make it to disappear
            }
            energy_d[2] = energy_b + emh;
            bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
            bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
        }
        else if (attributes[0]>0){ // H-SIA clusters.
            energy_d[0] = 0.67 + emi;
            if (attributes[0]==1 && attributes[2]==1){ // SIA-H
                energy_b = 0.67;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[0]==1 && attributes[2]==2){ // SIA-H2
                energy_b = 0.40;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[0]==1 && attributes[2]==3){ // SIA-H3
                energy_b = 0.40;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[0]==1 && attributes[2]==4){ // SIA-H4
                energy_b = 0.05;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
                
            } else if (attributes[0]==1 && attributes[2]==5){ // SIA-H5
                energy_b = 0.20;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[0]==2 && attributes[2]==1){ // SIA2-H
                energy_d[0] = 2.12;
                energy_b = 0.57;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
                
            } else if (attributes[0]==2 && attributes[2]==2){ // SIA2-H2
                energy_d[0] = 2.12;
                energy_b = 0.45;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[0]==2 && attributes[2]==3){ // SIA2-H3
                energy_d[0] = 2.12;
                energy_b = 0.1;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[0]==2 && attributes[2]==4){ // SIA2-H4
                energy_d[0] = 2.12;
                energy_b = 0.3;
                energy_d[2] = energy_b + emh;
                bind[0] = attfreq*exp(-energy_d[0]/KB/TEMPERATURE);
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else{
                int H = attributes[2];
                energy_d[0] = 2.12;
                energy_d[2] = H + 0.013 * H * H * H * H - 0.44 * H * H;
                /* this part is unknown */

            }

        }else if (attributes[0]== 0) { // nH clusters, this is binding energy of nH cluster dissociating 1 H from Qin(2015)
            /*
             if (attr[2]==1) { // H
             energy_b = 0;
             } else if (attr[2]==2) { // 2H
             energy_b = 0.02;
             } else if (attr[2]==3) { // 3H
             energy_b = 0.08;
             } else if (attr[2]==4) { // 4H
             energy_b = 0.20;
             } else if (attr[2]==5) { // 5H
             energy_b = 0.27;
             }
             energy_d[2] = energy_b + emh;
             if (attr[2] > 5) // nH(n>5) or higher
             energy_d[2] = 0.0;
             */
            bind[0] = 0; //because there's no V/SIA in cluster
            if (attributes[2]==1) { // H   data from Xiaochun Li(2015)
                bind[2] = 0;
                
            } else if (attributes[2]==2) { // 2H
                bindSH = attfreq * exp(-(0.01 + emh)/KB/TEMPERATURE);
            		/* on average, binding energy at surface is 0.01 eV*/
                energy_b = -0.12;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[2]==3) { // 3H
                energy_b = -0.1;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[2]==4) { // 4H
                energy_b = 0.20;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if (attributes[2]==5) { // 5H
                energy_b = -0.20;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            } else if(attributes[2]==6){
                energy_b = -0.30;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            }else if(attributes[2]==7){
                energy_b = -0.45;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            }else if(attributes[2]==8){
                energy_b = -0.15;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            }else if(attributes[2]==9){
                energy_b = 0.2;
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            }else if(attributes[2]==10){
                energy_b = -1.0; /* at this point SAV happen */
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                
            }else{
                int a = attributes[2];
                energy_b = 0.119 + 0.0407/(sin(7.94*a)) + 0.004*a*a*sin(7.93*a) - 0.104*a*sin(7.99*a)*sin(7.94*a);
                energy_d[2] = energy_b + emh;
                bind[2] = attfreq*exp(-energy_d[2]/KB/TEMPERATURE);
                /*extrapolation*/
                
            }
            
        }
    }
}

void Object::computeSinks()
{
    /* The total sink strength for all defects are stored in the array s.
     [0] for vacancies; [1] for SIAs; */
    double Zdv = 1.0, Zdi = 1.1;
    double Zodsv = 0.0, Zodsi = 0.0;
    double Zgbv = 1.0, Zgbi = 1.0;
    double Zsv = 1.0, Zsi = 1.1;
    double Sd, Sods, Sgbv, Sgbi, Sf;
    double s[2] = { 0 };
    /* 1. Dislocation sink strength: */
    Sd = DISLOCATION*exp(-1*KB*(TEMPERATURE - 300));
    
    /* 2. ODS-particle sink strength: */
    Sods = 4 * PI * ODS_R * ODS_DENSITY;
    
    /* 3. Grain boundary sink strength: */
    Sgbv = 6*sqrt(Zdv*Sd + Zodsv*Sods)/GRAIN_SIZE;
    Sgbi = 6*sqrt(Zdi*Sd + Zodsi*Sods)/GRAIN_SIZE;
    
    /* when internal sinks are weak */
    Sgbi = 24 / GRAIN_SIZE / GRAIN_SIZE;
    Sgbv = 24 / GRAIN_SIZE / GRAIN_SIZE;
    
    /* 4. Thin foil/interface: */
    // Sf = (2*sqrt(Zdi*5d + Zodsi*Sods)/FOIL_THICKNESS)*coth(sqrt(Zdi*5d + Zodsi*Sods)*FOIL_THINKNESS/4); */
    /* When internal sinks are weak */
    //Sf = 8 / FOIL_THICKNESS / FOIL_THICKNESS;
    //s[0] = Zdv * Sd + Zgbv * Sgbv + Zodsv * Sods + Zsv * Sf;
    //s[1] = Zdi * Sd + Zgbi * Sgbi + Zodsi * Sods + Zsi * Sf;
    
    s[0] = Zdv * Sd + Zgbv * Sgbv + Zodsv * Sods;
    s[1] = Zdi * Sd + Zgbi * Sgbi + Zodsi * Sods;
    //s[0] = Zdv * Sd;
    //s[1] = Zdi * Sd;
    sinkStrength = attributes[0] < 0 ? s[0] : s[1];
}

void Object::setProperties(const int & count, const int & n)
{
    setNumber();
    addNumber(count, n);
    dimensionality = setDimensionality();
    computeDiffCoeff();
    computeBindTerm();
    computeR1R1e();
    computeSinks();
}


// OneLine.cpp -- implementations of class OneLine
#include<cmath>
#include<fstream>
#include<iostream>
#include<sstream>
#include<string>
#include "OneLine.h"
using namespace std;

/* public function implementations */
OneLine::OneLine(
                 const Object* const hostObject,
                 const int & count,
                 unordered_map<int64, Object*>& mobileObjects) :totalRate(0.0)
{
    setOneLine(hostObject, count, mobileObjects);
    
}

Reaction OneLine::selectReaction(
                                 const Object* const hostObject,
                                 int64& theOtherKey,
                                 double& randRate)
{
    int index = 0;
    double tempRate = randRate;
    std::unordered_map<int64, long double>::iterator iter = secondR.begin();
    if (totalRate < tempRate) {
        randRate -= totalRate;
        return NONE;
    } // the reaction is not positioned in this line
    if(diffRToF > tempRate){
        return DIFFUSETOF;
    }
    else {
        tempRate -= diffRToF;
    }
    if (diffRToB > tempRate) {
        return DIFFUSETOB;
    }
    else {
        tempRate -= diffRToB;
    }
    if (sinkR > tempRate) {
        return SINK;
    }
    else {
        tempRate -= sinkR;
    }
    while (index < LEVELS) {
        if (dissociationR[index] > tempRate) {
            /* generate the other cluster key for monomer! */
            int attribute = hostObject->getAttri(index);
            theOtherKey = hostObject->signof(attribute)*((int64)pow(10.0, (double)EXP10*(LEVELS - index - 1)));
            return DISSOCIATION;
        }
        else {
            tempRate -= dissociationR[index];
            ++index;
        }
    }
    while (iter != secondR.end()) {
        if (iter->second > tempRate) {
            theOtherKey = iter->first;
            return COMBINATION;
        }
        else {
            tempRate -= iter->second;
            ++iter;
        }
    }
    return ERROR;
}

void OneLine::addReaction(
                          const Object* const hostObject,
                          const Object* const newObject,
                          const int& count)
{
    double rate = computeCombReaction(hostObject, newObject, count);
    std::pair<int64, double> oneReaction(newObject->getKey(), rate);
    secondR.insert(oneReaction);
}

void OneLine::removeReaction(const int64 & deleteKey)
{
    secondR.erase(deleteKey);
}

void OneLine::updateReaction(
                             Object const * const hostObject,
                             Object const * const mobileObject,
                             const int & n)
{
    double rate = computeCombReaction(hostObject, mobileObject, n);
    secondR[mobileObject->getKey()] = rate;
}

void OneLine::updateLine(
                         const Object* const hostObject,
                         const int & count,
                         unordered_map<int64, Object*>& mobileObjects)
{
    secondR.clear();
    setOneLine(hostObject, count, mobileObjects);
}

const long double OneLine::computeTotalRate()
{
    int i;
    unordered_map<int64,long double>::iterator iter;
    totalRate = 0.0;
    totalRate += diffRToF; /* add one diffusion rate */
    totalRate += diffRToB; /* add another diffusion rate*/
    totalRate += sinkR; /* add sink rate */
    for (i = 0; i < LEVELS; i++) {
        totalRate += dissociationR[i];
    }
    for (iter = secondR.begin(); iter != secondR.end(); ++iter) {
        totalRate += iter->second;
    }
    return totalRate;
}

const double OneLine::getDiffRateF() const
{
    return diffRToF;
}

const double OneLine::getDiffRateB() const
{
    return diffRToB;
}


void OneLine::display(Object const * const hostObject)
{
    ofstream fs;
    fs.open("lines.txt", ios::app);
    fs << "Line for Oject" << hostObject->getKey() << ":    ";
    fs << "(diff)" << diffRToF << ", " << diffRToB << "    " << "(sink)" << sinkR << "    ";
    for (int i = 0; i < LEVELS; ++i) {
        fs <<"(diss)"<< dissociationR[i] << "    ";
    }
    unordered_map<int64, long double>::iterator iter;
    for (iter = secondR.begin(); iter != secondR.end(); ++iter) {
        fs << "(" << iter->first << ")" << iter->second << "    ";
    }
    fs << endl;
    fs.close();
}

/* private function implementations */
void OneLine::setOneLine(
                         const Object* const hostObject,
                         const int & count,
                         unordered_map<int64, Object*>& mobileObjects)
{
    computeDiffReaction(hostObject, count);
    computeSinkReaction(hostObject, count);
    for (int index = 0; index < LEVELS; index++) {
        computeDissReaction(hostObject, index, count);
    }
    unordered_map<int64, Object*>::iterator iter;
    for (iter = mobileObjects.begin(); iter != mobileObjects.end(); ++iter) {
        double rate = computeCombReaction(hostObject, iter->second, count);
        std::pair<int64, double> oneReaction(iter->first, rate);
        secondR.insert(oneReaction);
    }
    computeTotalRate();
}

void OneLine::computeDiffReaction(const Object* const hostObject, const int& count)
{/* by having two diffusion rates, this rate will never be less than 0 */
	double lengthf = 0.0, lengthb = 0.0;
	if(count == 0){
		lengthf = 2.74e-8; /* thickness of W surface is 0.544nm, this length is centroid to vacuum */
		lengthb = 1.03e-6; /* surface to first element distance: (10+0.274) nm */
	}else if(count == 1){
		lengthf = 1.03e-6;
		lengthb = 2.0e-6; /* first element to second element distance (20nm) */
	}else{
		lengthf = lengthb = 2.0e-6; /* other element distances */
	}
    int ratio  = round(20/SURFACE_THICKNESS);
    double prefactor = 0.0;
    int objectN[3];
    hostObject->getThreeNumber(count, objectN);
    if(count == 0){
        objectN[0] *= ratio;
    }else if(count == 1){
        objectN[1] *= ratio;
    }
    /* 1. compute diffusion rate to the front element */
    if (objectN[0] > objectN[1]) {
        /* if diffusable, surface objects diffusing into vacuum is considered */
        prefactor = hostObject->getDiff() / lengthf / lengthf;
        diffRToF = prefactor*(objectN[0] - objectN[1]);
        //diffRToF = 0.0;
        
        if(count ==0){
            diffRToF = 0.0;
        } //during damage, surface-->vacuum diffusion closes
    }
    else {
        diffRToF = 0.0;
    }
    /* 2. compute diffusion rate to the back element*/
    if (objectN[0] > objectN[2]) {
        /* if diffusable */
        prefactor = hostObject->getDiff() / lengthb / lengthb;
        diffRToB = prefactor*(objectN[0] - objectN[2]);
    }
    else {
        diffRToB = 0.0;
    }
    /*
    if (count == POINTS - 1) {
        //objects at bottom is not allowed to diffuse into vacuum
        diffRToB = 0.0;
    }
    */
    /*
    diffRToB = 0.0;
    diffRToF = 0.0;
    */
}

void OneLine::computeSinkReaction(const Object* const hostObject, const int & count)
{
    sinkR = hostObject->getNumber(count)*hostObject->getDiff()*hostObject->getSink();
    //sinkR = 0.0;
}

void OneLine::computeDissReaction(
                                  const Object* const hostObject,
                                  const int & index,
                                  const int& count)
{
    if (hostObject->getAttri(index) != 0) {
        int attr[LEVELS] = { 0 };
        attr[index] = hostObject->signof(hostObject->getAttri(index));
        Object tempObject(attr, count);
        if(count == 0 && hostObject->getKey() == 2){
            dissociationR[index] = 4.0 * PI * hostObject->getR1e() / avol * tempObject.getDiff() * hostObject->getBindSH() * hostObject->getNumber(count);
            
        }else{
            dissociationR[index] = 4.0 * PI * hostObject->getR1e() / avol * tempObject.getDiff() * hostObject->getBind(index) * hostObject->getNumber(count);
            
        }
    }
    else {
        dissociationR[index] = 0.0;
        
    }
    //dissociationR[index] = 0.0;
}

double OneLine::computeCombReaction(
                                    const Object* const hostObject,
                                    const Object* const mobileObject,
                                    const int& count)
{
    
    double concentration;
    double r12;
    double dimensionTerm;
    double volume;
    if(count == 0){
        volume = (VOLUME/36) * SURFACE_THICKNESS;
        // volume on surface volume/36nm * 0.54nm
        
    }else{
        volume = VOLUME;
    }
    
    if (hostObject->getKey() != mobileObject->getKey()) {
        concentration = hostObject->getNumber(count)*mobileObject->getNumber(count) / volume;
        
    }else {
        concentration = hostObject->getNumber(count)*(hostObject->getNumber(count) - 1) / volume;
        
    }

    // H+H-->2H
    if(count != 0 && hostObject->getKey() == 1 && mobileObject->getKey() == 1){
        return 0.0;
    }
    
    if(hostObject->getKey() == 1 && mobileObject->getKey() == 2){
        return 0.0;
    }
    if(hostObject->getKey() == 2 && mobileObject->getKey() == 1){
        return 0.0;
    }
    r12 = hostObject->getR1() + mobileObject->getR1();
    dimensionTerm = computeDimensionTerm(r12, hostObject, mobileObject, count);
    int64 key1 = hostObject->getKey();
    int64 key2 = mobileObject->getKey();
    return 4.0*PI*concentration*r12*dimensionTerm;
    
    //return 0.0;
    
}

double OneLine::computeDimensionTerm(
                                     const double & r12,
                                     const Object* const hostObject,
                                     const Object* const mobileObject,
                                     const int& count)
{
    double term = 0.0;
    double hostDiff = hostObject->getDiff(), mobileDiff = mobileObject->getDiff();
    int hostDim = hostObject->getDim(), mobileDim = mobileObject->getDim();
    int hostN = hostObject->getNumber(count), mobileN = mobileObject->getNumber(count);
    /* int dimsum = hostDim + mobileDim; */
    int dimsum = 6;
    double alpha_a = -log(PI*PI*pow(r12, 3.0) / VOLUME / hostN);
    double alpha_b = -log(PI*PI*pow(r12, 3.0) / VOLUME / mobileN);
    switch (dimsum) {
        case 6: // 3D + 3D
            term = hostDiff + mobileDiff;
            break;
        case 4: // 3D + 1D
            if (hostDim == 1 && mobileDim == 3)
                term = hostObject->getDiff()*(mobileN / VOLUME)*(2.0*PI*pow(r12, 3.0)) + mobileDiff;
            else
                term = mobileDiff*(mobileN / VOLUME)*(2.0*PI*pow(r12, 3.0)) + hostDiff;
            break;
        case 2: // 1D + 1D
            term = hostDiff / alpha_b + mobileDiff / alpha_a;
            break;
    }
    return term;
}



// rvgs.cpp -- cpp file of rvgs.c
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include"rvgs.h"


int Bernoulli(double p)
/* ========================================================
 * Returns 1 with probability p or 0 with probability 1 - p.
 * NOTE: use 0.0 < p < 1.0
 * ========================================================
 */
{
    return (((double)rand() / RAND_MAX < (1 - p)) ? 0 : 1);
}

int Binomial(int n, double p)
/* ================================================================
 * Returns a binomial distributed integer between 0 and n inclusive.
 * NOTE: use n > 0 and 0.0 < p < 1.0
 * ================================================================
 */
{
    int i, x = 0;
    
    for (i = 0; i < n; i++)
        x += Bernoulli(p);
    return (x);
}

int Equilikely(int a, int b)
/*===================================================================
 * Returns an equilikely distributed integer between a and b inclusive.
 * NOTE: use a < b
 * ===================================================================
 */
{
    return (a + (int)((b - a + 1) * (double)rand() / RAND_MAX));
}

int Geometric(double p)
/* ====================================================
 * Returns a geometric distributed non-negative integer.
 * NOTE: use 0.0 < p < 1.0
 * ====================================================
 */
{
    return ((int)(log(1.0 - (double)rand() / RAND_MAX) / log(p)));
}

int Pascal(int n, double p)
/* =================================================
 * Returns a Pascal distributed non-negative integer.
 * NOTE: use n > 0 and 0.0 < p < 1.0
 * =================================================
 */
{
    int i, x = 0;
    
    for (i = 0; i < n; i++)
        x += Geometric(p);
    return (x);
}

int Poisson(double m)
/* ==================================================
 * Returns a Poisson distributed non-negative integer.
 * NOTE: use m > 0
 * ==================================================
 */
{
    double t = 0.0;
    int   x = 0;
    
    while (t < m) {
        t += Exponential(1.0);
        x++;
    }
    return (x - 1);
}

double Uniform(double a, double b)
/* ===========================================================
 * Returns a uniformly distributed real number between a and b.
 * NOTE: use a < b
 * ===========================================================
 */
{
    return (a + (b - a) * (double)rand() / RAND_MAX);
}

double Exponential(double m)
/* =========================================================
 * Returns an exponentially distributed positive real number.
 * NOTE: use m > 0.0
 * =========================================================
 */
{
    double k = -m * log(1.0 - (double)rand() / RAND_MAX);
    return k;
}

double Erlang(int n, double b)
/* ==================================================
 * Returns an Erlang distributed positive real number.
 * NOTE: use n > 0 and b > 0.0
 * ==================================================
 */
{
    int   i;
    double x = 0.0;
    
    for (i = 0; i < n; i++)
        x += Exponential(b);
    return (x);
}

double Normal(double m, double s)
/* ========================================================================
 * Returns a normal (Gaussian) distributed real number.
 * NOTE: use s > 0.0
 *
 * Uses a very accurate approximation of the normal idf due to Odeh & Evans,
 * J. Applied Statistics, 1974, vol 23, pp 96-97.
 * ========================================================================
 */
{
    const double p0 = 0.322232431088;     const double q0 = 0.099348462606;
    const double p1 = 1.0;                const double q1 = 0.588581570495;
    const double p2 = 0.342242088547;     const double q2 = 0.531103462366;
    const double p3 = 0.204231210245e-1;  const double q3 = 0.103537752850;
    const double p4 = 0.453642210148e-4;  const double q4 = 0.385607006340e-2;
    double u, t, p, q, z;
    
    u = (double)rand()/RAND_MAX;
    if (u < 0.5)
        t = sqrt(-2.0 * log(u));
    else
        t = sqrt(-2.0 * log(1.0 - u));
    p = p0 + t * (p1 + t * (p2 + t * (p3 + t * p4)));
    q = q0 + t * (q1 + t * (q2 + t * (q3 + t * q4)));
    if (u < 0.5)
        z = (p / q) - t;
    else
        z = t - (p / q);
    return (m + s * z);
}

double Lognormal(double a, double b)
/* ====================================================
 * Returns a lognormal distributed positive real number.
 * NOTE: use b > 0.0
 * ====================================================
 */
{
    return (exp(a + b * Normal(0.0, 1.0)));
}

double Chisquare(int n)
/* =====================================================
 * Returns a chi-square distributed positive real number.
 * NOTE: use n > 0
 * =====================================================
 */
{
    int   i;
    double z, x = 0.0;
    
    for (i = 0; i < n; i++) {
        z = Normal(0.0, 1.0);
        x += z * z;
    }
    return (x);
}

double Student(int n)
/* ===========================================
 * Returns a student-t distributed real number.
 * NOTE: use n > 0
 * ===========================================
 */
{
    return (Normal(0.0, 1.0) / sqrt(Chisquare(n) / n));
}


#include<ctime>
#include<cstdlib>
#include "SCDWrapper.h"
// SCDWrapper -- implementaions of class SCDWrapper

static double fluenceH = 0.0;
static double in_time = 0.0;
static double doseIon[POINTS] = {0.0};
static int annilV = 0;
static int start = 1;
static int plotTime = 1;
static int plotTime1 = 1;
static int plotTime2 = 1;
static int event = 0;
static int sinkV = 0;
static int sinkH = 0;
static int generationNumber = 0;
static int dissV = 0; //this only counts number of events
static int dissH = 0; //this only counts number of events
/* public funciton */
SCDWrapper::SCDWrapper():damage(), cpdf()
{
    for (int i = 0; i < POINTS; ++i) {
        computeMatrixRate(i);
    } /* initialized matrix rate in every element */
    computeBulkRate();  /* initialized total rate in the bulk */
    
    /* initialize reactions[][] */
    for(int i=0; i<8; i++){
        for(int j=0; j<POINTS; j++){
            reactions[i][j] = 0;
        }
    }
    /* initialize sink numbers */
    setSinks();
    /* set format of gs --- species */
    /*
    gs.set_title("Species");
    gs.set_xlabel("#(SIA/V)");
    gs.set_ylabel("#(H)");
    gs.set_xautoscale();
    gs.set_yautoscale();
    gs.cmd("set xtics 1\n");
    gs.cmd("set ytics 1\n");
    */
    /* set format of gs --- reactions */
    /*
    gr.set_title("Reactions");
    gr.set_xlabel("Element Number");
    gr.set_ylabel("Reaction Type");
    gr.set_yrange(-1,8);
    gr.set_xrange(-1,POINTS);
    gr.cmd("set xtics 1\n");
    gr.cmd("set ytics(\"DF\" 0,\"DB\" 1, \"SINK\" 2, \"DIS\" 3, \"COM\" 4, \"ION\" 5, \"H\" 6, \"SAV\" 7)\n");
    */
    /* set format of gd1 -- size distribution */
    /*
    gd1.set_title("Size Distribution");
    gd1.set_xlabel("size(nm)");
    gd1.set_ylabel("C(#/cm^3)");
    gd1.set_xrange(-6, 15);
    gd1.set_yautoscale();
    gd1.cmd("set xtics 1\n");
    */
    /* set format of gd2 ---depth distribution */
    /*
    gd2.set_title("Depth Distribution");
    gd2.set_xlabel("Depth(nm)");
    gd2.set_ylabel("C(#/cm^3)");
    gd2.set_yautoscale();
    gd2.cmd("set xtics 72\n");
    */
    /* set format of gh1 ---depth distribution */
    /*
    gh1.set_title("Depth Distribution");
    gh1.set_xlabel("Depth(nm)");
    gh1.set_ylabel("C(#/cm^3)");
    gh1.set_ylogscale();
    //gh1.set_yautoscale();
    gh1.cmd("set xtics 72\n");
    */
    /* set format of gh2 --  H to V ratio */
    /*
    gh2.set_title("H to V ratio");
    gh2.set_xlabel("time(s)");
    gh2.set_ylabel("ratio");
    gh2.cmd("set grid\n");
    gh2.cmd("set ytics 0.5\n");
    gh2.set_yautoscale();
    gh2.set_xautoscale();
    //gh2.cmd("set xtics 250\n");
    */
}

void SCDWrapper::computeMatrixRate(const int & n)
{
    /* Qianran 0925 */
    //cout << "Element " << n + 1 << endl;
    matrixRate[n] = 0.0;
    unordered_map<int64, Object*>::iterator iter;
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter) {
        int64 tempKey = iter->first;
        Object* tempObject = iter->second;
        int totalNumber = tempObject->getTotalNumber();
        while(totalNumber == 0) {
            iter++;
            removeObjectFromMap(tempKey);
            if (iter != allObjects.end()) {
                tempObject = iter->second;
                tempKey = iter->first;
                totalNumber = tempObject->getTotalNumber();
            }
            else {
                break;
            }
        }
        if (iter == allObjects.end()) {
            break;
        }
        Bundle* tempBundle = linePool[tempObject];
        OneLine* tempLine = tempBundle->lines[n];
        if (tempLine != nullptr) {
            matrixRate[n] += tempLine->computeTotalRate();
            //tempLine->display(tempObject);/* Qianran 0925 */
        }
    }
    matrixRate[n] += damage.getTotalDamage(n);
    matrixRate[n] += sinkDissRate[0][n];
    matrixRate[n] += sinkDissRate[1][n];
}

void SCDWrapper::computeBulkRate()
{
    bulkRate = 0.0;
    for (int i = 0; i < POINTS; ++i) {
        bulkRate += matrixRate[i];
    }
}

Object* SCDWrapper::selectReaction(
                                   int64& theOtherKey,
                                   Reaction& reaction,
                                   int& count)
{
    //ofstream fs;
    //fs.open("selectReaction.txt", ios::app);
    int pointIndex;
    long double randRate = ((double)rand() / RAND_MAX)*bulkRate;
    double tempRandRate = randRate;
    Object* tempObject = nullptr;
    Bundle* tempBundle;
    OneLine* tempLine;
    //fs << "BulkRate = " << bulkRate << "RandRate = " << randRate << endl;
    for (pointIndex = 0; pointIndex < POINTS; ++pointIndex) {
        if (matrixRate[pointIndex] < tempRandRate) {
            tempRandRate -= matrixRate[pointIndex];
            continue;
        }/* if the event is not positioned in this element, move on to the next one */
        else {
            reaction = NONE;
            unordered_map<int64, Object*>::iterator iter = allObjects.begin();
            while (reaction == NONE && iter != allObjects.end()) {
                tempObject = iter->second;
                tempBundle = linePool[tempObject];
                tempLine = tempBundle->lines[pointIndex];
                if (tempLine != nullptr) {
                    reaction = tempLine->selectReaction(tempObject, theOtherKey, tempRandRate);
                }
                ++iter;
            }
            if (reaction == NONE) {
                reaction = damage.selectDamage(pointIndex, tempRandRate);
            }
            if (reaction == NONE){
                if(sinkDissRate[0][pointIndex] >= tempRandRate){
                    reaction = DISSV;
                }else{
                    reaction = DISSH;
                }
                
            }
            
            count = pointIndex;
           // fs << "Element = " << pointIndex + 1 <<", "<< "Reaction = " << reaction << endl << endl;
           // fs.close();
            return tempObject;
        }
    }
    return tempObject;
}

void SCDWrapper::processEvent(
                              const Reaction& reaction,
                              Object* hostObject,
                              const int& n,
                              const int64& theOtherKey,
                              const double& time,
                              const double& dt
                              )
{
    ++event;
    fs.open("Reactions.txt", ios::app);
    switch (reaction) {
        case DIFFUSETOF:
            processDiffEvent(hostObject, n, 'f');
            //fs << hostObject->getKey() <<"  diffuses from element "<< n << " to element " << n-1 << endl;
            break;
        case DIFFUSETOB:
            processDiffEvent(hostObject, n, 'b');
            //fs << hostObject->getKey() <<"  diffuses from element "<< n << " to element " << n+1 << endl;
            break;
        case SINK:
            processSinkEvent(hostObject, n);
            //fs << hostObject->getKey() <<"  in element "<< n << " goes to sink."<< endl;
            writeSinkFile(hostObject, n, time); /* this step also updated sinkDissRate */
            break;
        case DISSOCIATION:
            processDissoEvent(hostObject, n, theOtherKey, fs);
            break;
        case COMBINATION:
            processCombEvent(hostObject, n, theOtherKey, fs);
            break;
        case PARTICLE:
            getParticleInsertion(n, dt, fs);
            break;
        case HE:
            getHeInsertion(n);
            break;
        case H:
            getHInsertion(n, dt, fs);
            break;
        case DISSV:
            processSinkDissEvent(0, n);
            dissV++;
            //cout << "dissV = " << dissV << endl;
            break;
        case DISSH:
            processSinkDissEvent(1, n);
            dissH++;
            //cout << "dissH = " << dissH << endl;
            break;
        default:
            break;
    }
    fs.close();
}

SCDWrapper::~SCDWrapper()
{
    /* clean linePool*/
    unordered_map<Object*, Bundle*>::iterator iter;
    unordered_map<int64, Object*>::iterator iter1;
    for (iter = linePool.begin(); iter != linePool.end(); ++iter) {
        delete iter->second;
    }
    /* clean contents of object* */
    for (iter1 = allObjects.begin(); iter1 != allObjects.end(); ++iter1) {
        delete iter1->second;
    }
}

unordered_map<int64, Object*>* SCDWrapper::getAllObjects()
{
    return &allObjects;
}

unordered_map<int64, Object*>* SCDWrapper::getMobileObjects()
{
    return &mobileObjects;
}

unordered_map<Object*, Bundle*>* SCDWrapper::getLinePool()
{
    return &linePool;
}

const double SCDWrapper::getAndExamineRate()
{
    fstream fs;
    //fs.open("Rate.txt",ios::app);
    int i;
    for (i = 0; i < POINTS; ++i) {
        computeMatrixRate(i);
        //fs << matrixRate[i] <<"        ";
    }
    computeBulkRate();
    //fs << "BulkRate" << bulkRate << endl<<endl<<endl;
    //fs.close();

    return bulkRate;
}

void SCDWrapper::writeSinkFile(const Object * const hostObject, const int& n, const double& time)
{
    int i;
    for (i = 0; i < LEVELS; i++) {
        int number = hostObject->getAttri(i);
        if (i == 0 && number<0) {
            sinks[i][n] += abs(number);
            computeSinkDissRate(i, n);
        }
        else {
            sinks[i + 1][n] += number;
            if(i == 2){
                computeSinkDissRate(1, n);
            }
        }
    }
}

void SCDWrapper::writeSpeciesFile(const double& time, const int& n)
{
    ofstream fo;
    unordered_map<int64, Object*>::iterator iter;
    fo.open("species.txt", ios::out);
    fo << "step = " << n << endl;
    fo << "time = " << time << endl;
    fo << "fluenceH = " << fluenceH << endl;
    
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter) {
        fo << "object " << iter->second->getKey() << "    ";
        for(int i = 0; i < POINTS; i++){
            fo << (iter->second->getNumber(i)) << "    ";
            //totalDose += doseIon[i];
        }
        fo<<endl;
    }
    fo.close();
    /* write species.out file */
    
}

void SCDWrapper::writeClusterFile(const double& time, const int& n)
{
    int i;
    ofstream fc;
    fc.open("clusters.out", ios::app);
    unordered_map<int64, Object*>::iterator iter;
    int sia[POINTS] = { 0 }, siac[POINTS] = { 0 }, siah[POINTS] = { 0 }, v[POINTS] = { 0 }, vc[POINTS] = { 0 }, vh[POINTS] = { 0 };
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter) {
        int attr0 = iter->second->getAttri(0);
        int attr2 = iter->second->getAttri(2);
        for (i = 0; i < POINTS; ++i) {
            int number = iter->second->getNumber(i);
            if (attr0 > 0) {
                if (attr0 == 1) {
                    sia[i] += number;
                }
                else {
                    siac[i] += number;
                    if (attr2 != 0) {
                        siah[i] += number;
                    }
                }
            }
            if (attr0 < 0) {
                if (attr0 == -1) {
                    v[i] += number;
                }
                else {
                    vc[i] += number;
                    if (attr2 != 0) {
                        vh[i] += number;
                    }
                }
            }
        }
    }
    fc << "Aggregate time = " << time << "  step = " << n << endl;
    for (i = 0; i < POINTS; ++i) {
        fc << "Element " << i + 1 << "    ";
        fc << fluenceH <<"  "<< doseIon[i]<<"   " << sia[i] / VOLUME << "    " << siac[i] / VOLUME << "    " << siah[i] / VOLUME << "    " << v[i] / VOLUME
        << "    " << vc[i] / VOLUME << "       " << vh[i] / VOLUME;
        fc << endl;
    }
    fc.close();
}

void SCDWrapper::writeSinkFile(const double& time, const int& n)
{
    int i, j;
    ofstream fs;
    fs.open("sink.txt", ios::out);
    //fs << "Aggregate time = " << time << "  step = " << n << endl;
    for (i = 0; i < POINTS; ++i) {
        for (j = 0; j < LEVELS + 1; ++j) {
            fs << sinks[j][i]<< "    ";
        }
        fs << endl;
    }
    fs.close();
}

void SCDWrapper::displayDamage(){
    for(int i = 0; i < POINTS; i++){
        damage.display(i);
    }
}

void SCDWrapper::displayAllObject(){
    unordered_map<int64, Object*>::iterator iter;
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter) {
        std::cout << iter->second->getKey() << "    ";
        for(int i = 0; i < POINTS; i++){
            std::cout << (iter->second->getNumber(i)) << "    " ;
        }
        std::cout<<endl;
    }
}

void SCDWrapper::writeFile(const double& time, const int & n)
{
    writeSpeciesFile(time, n);
    writeSinkFile(time, n);
    //writeClusterFile(time, n);
}

void SCDWrapper::setSinks()
{
    int i, j;
    for (i = 0; i < LEVELS + 1; ++i){
        for (j = 0; j < POINTS; ++j) {
            sinks[i][j] = 0;
        }
    }
    for (i = 0; i < 2; ++i){
        for (j = 0; j < POINTS; ++j) {
            sinkDissRate[i][j] = 0;
        }
    }
}

void SCDWrapper::computeSinkDissRate(const int type, const int point)
{
    double b = 2.8e-8; //burger's vector 2.8e-8 cm
    double nu = 1e+13; // attempt frequency s^-1 averaged with dt
    double ebH = 0.6, emH = 0.25; //binding and migration energy of hydrogen
    double ebV = 1.0, emV = 1.29; //binding and migration energy of vacancy
    double prefactor = 1.58e-3;
    double prefactorv = 177e-4;
    // vacancy dissociation
    if(type == 0){
        sinkDissRate[type][point] = (2*PI*DISLOCATION*VOLUME/ALATT/ALATT/b)*sinks[0][point]*prefactorv*exp(-(ebV+emV)/KB/TEMPERATURE);
        //sinkDissRate[type][point] = 0.0;
    }
    // hydrogen dissocaiton
    if(type == 1){
        sinkDissRate[type][point] = (2*PI*DISLOCATION*VOLUME/ALATT/ALATT/b)*sinks[3][point]*prefactor*exp(-(ebH+emH)/KB/TEMPERATURE);
    }
}

int64 SCDWrapper::attrToKey(const int * const attr)
{
    int64 key = 0;
    int sign = (attr[0] < 0) ? -1 : 1;
    for (int i = 0; i < LEVELS; ++i) {
        key += labs(attr[i])*((int64)pow(10.0, (double)EXP10*(LEVELS - 1 - i)));
    }
    key *= sign;
    return key;
}

int64 SCDWrapper::atomProperty(SCDWrapper::InsertStyle mode, const int& n)
{
    if (mode == SUBSTITUTIONAL) {
        return (-1) * ((int64)pow(10.0, (double)EXP10 * (LEVELS - 1)) + (int64)pow(10.0, (double)EXP10 * (LEVELS - n)));
    }
    else if (mode == INTERSTITIAL) {
        return (int64)pow(10.0, (double)EXP10 * (LEVELS - n));
    }
    return 0; /* If return 0, means the code is wrong */
}

void SCDWrapper::addNewObjectToMap(const int64 & key, const int* number)
{
    if (key != 0) {
        Object* newObject = new Object(key, number);
        pair<int64, Object*> newNode(key, newObject);
        allObjects.insert(newNode); /* 1. add to all object */
        if (newObject->getDiff() > 0) {
            mobileObjects.insert(newNode);
            addReactionToOther(newObject);
        }  /* 2. add to mobile object if necessary */
        Bundle* newBundle = new Bundle(newObject, mobileObjects);
        pair<Object*, Bundle*> bundle(newObject, newBundle);
        linePool.insert(bundle); /* 3. add to line pool */
    }/* do this only when the object is a valid one */
}

void SCDWrapper::addNewObjectToMap(const int64 & key, const int& count)
{
    if (key != 0) {
        Object* newObject = new Object(key, count);
        pair<int64, Object*> newNode(key, newObject);
        allObjects.insert(newNode); /* add to all object */
        if (newObject->getDiff() > 0) {
            mobileObjects.insert(newNode);
            addReactionToOther(newObject);
        }  /* add to mobile object if necessary */
        Bundle* newBundle = new Bundle(newObject, mobileObjects);
        pair<Object*, Bundle*> bundle(newObject, newBundle);
        linePool.insert(bundle); /* add to line pool */
        
    }/* do this only when the object is a valid one */
}

void SCDWrapper::addNewObjectToMap(Object* newObject)
{
    if (newObject->getKey() == 0) {
        delete newObject;
    }/* if this object is invalid, just delete this object */
    else {
        pair<int64, Object*> newNode(newObject->getKey(), newObject);
        allObjects.insert(newNode); /* add to all object */
        if (newObject->getDiff() > 0) {
            mobileObjects.insert(newNode);
            addReactionToOther(newObject); /* add to other objects' lines */
        } /* add to mobile object if necessary */
        Bundle* newBundle = new Bundle(newObject, mobileObjects);
        pair<Object*, Bundle*> bundle(newObject, newBundle);
        linePool.insert(bundle); /* add to line pool */
    }/* if this object is valid, add it to map */
}

void SCDWrapper::updateObjectInMap(Object * hostObject, const int & count)
{
    OneLine* tempLine = linePool[hostObject]->lines[count];
    double diffusivity = hostObject->getDiff();
    int number = hostObject->getNumber(count);
    if (tempLine != nullptr) {
        if (number > 0) {
            tempLine->updateLine(hostObject, count, mobileObjects);
        }
        else {
            delete tempLine;
            linePool[hostObject]->lines[count] = nullptr;
        }
    }
    else {
        if (number > 0) {
            tempLine = new OneLine(hostObject, count, mobileObjects);
            linePool[hostObject]->lines[count] = tempLine;
        }
    }
    if (diffusivity > 0) {
        updateRateToOther(hostObject, count);
    }
    if((count-1) >= 0){
        OneLine* tempLine = linePool[hostObject]->lines[count - 1];
        if(tempLine != nullptr){
            tempLine->updateLine(hostObject, count - 1, mobileObjects);
        }
    }
    if((count + 1) < POINTS){
        OneLine* tempLine = linePool[hostObject]->lines[count + 1];
        if(tempLine != nullptr){
            tempLine->updateLine(hostObject, count + 1, mobileObjects);
        }
    }
}

void SCDWrapper::addReactionToOther(Object const * const mobileObject)
{
    unordered_map<Object*, Bundle*>::iterator iter;
    for (iter = linePool.begin(); iter != linePool.end(); ++iter) {
        Object* hostObject = iter->first;
        Bundle* tempBundle = iter->second;
        for (int i = 0; i < POINTS; ++i) {
            OneLine* tempLine = tempBundle->lines[i];
            if (tempLine != nullptr) {
                tempLine->addReaction(hostObject, mobileObject, i);
            }
        }
    }
}

void SCDWrapper::updateRateToOther(Object const * const mobileObject, const int & count)
{
    unordered_map<Object*, Bundle*>::iterator iter;
    for (iter = linePool.begin(); iter != linePool.end(); ++iter) {
        Object* hostObject = iter->first;
        Bundle* tempBundle = iter->second;
        OneLine* tempLine = tempBundle->lines[count];
        if (tempLine != nullptr) {
            tempLine->updateReaction(hostObject, mobileObject, count);
        }
    }
}

void SCDWrapper::removeObjectFromMap(const int64 & deleteKey)
{
    Object* deleteObject = allObjects[deleteKey];
    Bundle* tempBundle = linePool[deleteObject];
    double diffusivity = deleteObject->getDiff();
    delete tempBundle; /* delete bundle */
    linePool.erase(deleteObject); /* remove this object from map linePool */
    delete deleteObject;  /* delete the content of this object */
    allObjects.erase(deleteKey); /* delete this object from map allObjects */
    if (diffusivity > 0) {
        mobileObjects.erase(deleteKey); /* delete this object from map mobileObjects */
        removeRateToOther(deleteKey);
    }
}

void SCDWrapper::removeRateToOther(const int64& deleteKey)
{
    unordered_map<int64, Object*>::iterator iter;
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter) {
        Object* tempObject = iter->second;
        Bundle* tempBundle = linePool[tempObject];
        for (int i = 0; i < POINTS; ++i) {
            OneLine* tempLine = tempBundle->lines[i];
            if (tempLine != nullptr) {
                tempLine->removeReaction(deleteKey);
            }
        }
    }
}

void SCDWrapper::updateSinks(const int point, const int* number){
    for(int i = 0; i< LEVELS+1; i++){
        sinks[i][point] = number[i];
        if(i==0){
            computeSinkDissRate(i,point);
        }else if(i==3){
            computeSinkDissRate(1,point);
        }
    }
}

/* private function */
void SCDWrapper::processDiffEvent(Object* hostObject, const int& n, const char& signal)
{
    hostObject->reduceNumber(n);
    
    if (signal == 'f') {
        ++reactions[0][n];
        if(n != 0){ /* when not surface */
            /* diffuse to the previous element */
            hostObject->addNumber(n - 1); // because it is diffusion to front, the surface element will not diffuse
            updateObjectInMap(hostObject, n - 1); // updated n-2, n-1, n
            
        }else{
            //surface diffuse to vacuum
            int64 key = hostObject->getKey();
            if(surface.find(key) != surface.end()){
                ++surface[key];
                
            }else{
                pair<int64, int> newNode(key, 1);
                surface.insert(newNode); /* add to all object */

            }
        }
    }
    else if(signal == 'b'){
        ++reactions[1][n];
        /* diffuse to the latter element */
        if ((n + 1) != POINTS) {
            hostObject->addNumber(n + 1); // because it is diffusion to back
            updateObjectInMap(hostObject, n + 1); // updated n n+1 and n+2
        }else{
            // bottom diffuses to vacuum
            int64 key = hostObject->getKey();
            if(bottom.find(key) != bottom.end()){
                ++bottom[key];
            }else{
                pair<int64, int> newNode(key, 1);
                bottom.insert(newNode); /* add to all object */
            }
        }
    }
    updateObjectInMap(hostObject, n); // updated n-1, n, n+1
}

void SCDWrapper::processSinkEvent(Object * hostObject, const int & n)
{
    int64 key = hostObject->getKey();
    ++reactions[2][n];
    hostObject->reduceNumber(n);
    /*
    if(key == -1000000){
        //case 1V
        sinks[0][n]++;
        computeSinkDissRate(0, n);
    }else if(key == 1){
        // case 1H
        sinks[3][n]++;
        computeSinkDissRate(1, n);
    }
    */
    updateObjectInMap(hostObject, n); //updated n-1, n n+1
    
} /* if sink rate != 0, diffusivity of this object is not 0 */

void SCDWrapper::processDissoEvent(
                                   Object * hostObject,
                                   const int & n,
                                   const int64 & monomerKey,
                                   fstream& fs)
{
    ++reactions[3][n];
    int64 HKey = 1;
    int number = 1;
    int theOtherAttr[LEVELS] = { 0 };   /* this holds the attribute of the other cluster(product) */
    /* 1) deal with monomer */
    if (allObjects.find(monomerKey) != allObjects.end()) {
        /* monomer found! then number of monomer in this element increase 1*/
        Object* anObject = allObjects[monomerKey];
        anObject->addNumber(n);
        updateObjectInMap(anObject, n);
    }
    else {
        /* monomer didn't find! build new object and insert it into map */
        addNewObjectToMap(monomerKey, n);
    }
    /* 2) generate the other cluster */
    Object* monomer = allObjects[monomerKey];
    for (int i = 0; i < LEVELS; i++) {
        theOtherAttr[i] = hostObject->getAttri(i) - monomer->getAttri(i);
    }/* now I have the attribute for the other cluster */
    int64 theOtherKey = attrToKey(theOtherAttr);
    
    if(hostObject->getAttri(0)== -1 && hostObject->getAttri(2) >0 && monomerKey == -1000000){
        theOtherKey = HKey;
        number = hostObject->getAttri(2);
        /* V1-Hm -> Hm + V */
        /* change above reaction to V1-Hm -> m * H +V */
        
    }
    
    if (allObjects.find(theOtherKey) != allObjects.end()) {
        /* the other cluster found! then number of this cluster in this element increase 1 */
        Object* anObject = allObjects[theOtherKey];
        anObject->addNumber(n, number);
        updateObjectInMap(anObject, n);
    }
    else {
        /* cluster didn't find! build new object and insert it into map */
        Object* anObject = new Object(theOtherKey, n, number);
        addNewObjectToMap(anObject);
    }
    /* 3) deal with the host object */
    hostObject->reduceNumber(n);
    updateObjectInMap(hostObject, n);
    /*
    if(n==0){
        fs1 <<"Dissociation: " << hostObject->getKey() << " -> " << theOtherKey << " + "<<monomerKey<<endl;
    }
    if(n==1){
        fs3 <<"Dissociation: " << hostObject->getKey() << " -> " << theOtherKey << " + "<<monomerKey<<endl;
    }if(n==2){
        fs5 <<"Dissociation: " << hostObject->getKey() << " -> " << theOtherKey << " + "<<monomerKey<<endl;
    }
     */
    //fs <<"Dissociation: " << hostObject->getKey() << " -> " << theOtherKey << " + "<<monomerKey<<" in Element "<<n<<endl;
}

void SCDWrapper::processCombEvent(
                                  Object * hostObject,
                                  const int & n,
                                  const int64 & theOtherKey,
                                  fstream& fs)
{
    ++reactions[4][n];
    /* 1. find the other reactant */
    Object* theOtherObject = allObjects[theOtherKey];
    int productAttr[LEVELS] = { 0 };  /* attributes of the product object */
    int64 productKey = 0;
    int64 SIAKey = 1000000;
    int64 HKey = 1;
    int number = 1; /*the number of this material added */
    for (int i = 0; i < LEVELS; ++i) {/* 2. get the attributes of the product */
        productAttr[i] = hostObject->getAttri(i) + theOtherObject->getAttri(i);
    } /* now I have the attribute of the product object */
    if(recognizeSAV(hostObject, theOtherObject)){
        ++reactions[7][n];
        //cout<<"SAV"<<endl;
        productAttr[0] -= 1; /* generate 1 vacancy */
        if (allObjects.find(SIAKey) != allObjects.end()) {
            /* we have SIA */
            Object* SIA = allObjects[SIAKey];
            SIA->addNumber(n);
            updateObjectInMap(SIA, n);
        }
        else { /* we don't have this object */
            Object* SIA = new Object(SIAKey, n);
            /* add SIA to map */
            addNewObjectToMap(SIA);
        }/* produce one SIA */
    }
    productKey = attrToKey(productAttr);
    if(hostObject->getAttri(0)<0 && hostObject->getAttri(2)>0 && productAttr[0] > 0 && productAttr[2] == 0 && (productAttr[0]+hostObject->getAttri(0))>0){
        productKey = HKey;
        number = productAttr[2];
        if (allObjects.find(productKey) != allObjects.end()) {/* we have this product */
            Object* productObject = allObjects[productKey];
            productObject->addNumber(n, number);
            updateObjectInMap(productObject, n);
        }
        else { /* we don't have this object */
            Object* productObject = new Object(productAttr, n, number);
            /* add product to map */
            addNewObjectToMap(productObject);
        }
        productKey = (productAttr[0]+hostObject->getAttri(0)) * SIAKey;
        number = 1;
        if (allObjects.find(productKey) != allObjects.end()) {/* we have this product */
            Object* productObject = allObjects[productKey];
            productObject->addNumber(n, number);
            updateObjectInMap(productObject, n);
        }
        else { /* we don't have this object */
            Object* productObject = new Object(productAttr, n, number);
            /* add product to map */
            addNewObjectToMap(productObject);
        }
    }else if(hostObject->getAttri(0)<0 && hostObject->getAttri(2)>0 && theOtherObject->getAttri(0)>0 && theOtherObject->getAttri(2)==0 && abs(hostObject->getAttri(0)) == theOtherObject->getAttri(0)){
        /* Vn-Hm + SIAn -> Hm (n, m are not zero) */
        /* change above reaction to Vn-Hm + SIAn -> m * H */
        productKey = HKey;
        number = hostObject->getAttri(2);
        if (allObjects.find(productKey) != allObjects.end()) {/* we have this product */
            Object* productObject = allObjects[productKey];
            productObject->addNumber(n, number);
            updateObjectInMap(productObject, n);
        }
        else { /* we don't have this object */
            Object* productObject = new Object(productAttr, n, number);
            /* add product to map */
            addNewObjectToMap(productObject);
        }
    }else{
        if (allObjects.find(productKey) != allObjects.end()) {/* we have this product */
            Object* productObject = allObjects[productKey];
            productObject->addNumber(n, number);
            updateObjectInMap(productObject, n);
        }
        else { /* we don't have this object */
            Object* productObject = new Object(productAttr, n, number);
            /* add product to map */
            addNewObjectToMap(productObject);
        }
        
    }
    /* update reactant */
    hostObject->reduceNumber(n);
    updateObjectInMap(hostObject, n);
    theOtherObject->reduceNumber(n);
    updateObjectInMap(theOtherObject, n);
    int attrZeroA = hostObject->getAttri(0);
    int attrZeroB = theOtherObject -> getAttri(0);
    if(attrZeroA * attrZeroB < 0){
        /* vacancy, interstitial anniles */
        annilV += abs(attrZeroA) < abs(attrZeroB) ? abs(attrZeroA) : abs(attrZeroB);
    }
    /*
    if(n==0){
        if(!recognizeSAV(hostObject, theOtherObject)){
            fs2<<"Combination Reaction: "<< hostObject->getKey()<<" + "<<theOtherKey<<" -> " << productKey<<endl;
        }
    }
    if(n==1){
        if(!recognizeSAV(hostObject, theOtherObject)){
            fs4<<"Combination Reaction: "<< hostObject->getKey()<<" + "<<theOtherKey<<" -> " << productKey << endl;
        }
    }
    if(n==2){
        if(!recognizeSAV(hostObject, theOtherObject)){
            fs6<<"Combination Reaction: "<< hostObject->getKey()<<" + "<<theOtherKey<<" -> " << productKey << endl;
        }
    }
    */
    /*
    if(recognizeSAV(hostObject, theOtherObject)){
        fs<<"Combination Reaction: "<< hostObject->getKey()<<" + "<<theOtherKey<<" -> "<<productKey<< " + "<< SIAKey <<" in element "<< n <<endl;
    }else{
        fs<<"Combination Reaction: "<< hostObject->getKey()<<" + "<<theOtherKey<<" -> "<<productKey<<" in element "<< n <<endl;
    }
    */
}
void SCDWrapper::processSinkDissEvent(const int type, const int& point){
    // dissV event
    int64 productKey = 0;
    if(type == 0){
        sinks[0][point]--;
        productKey = -1000000; //1V
    }else if(type ==1){
        //dissV event
        sinks[3][point]--;
        productKey = 1;
    }
    if (allObjects.find(productKey) != allObjects.end()) {/* we have this product */
        Object* productObject = allObjects[productKey];
        productObject->addNumber(point, 1);
        updateObjectInMap(productObject, point);
    }
    else { /* we don't have this object */
        Object* productObject = new Object(productKey, point, 1);
        /* add product to map */
        addNewObjectToMap(productObject);
    }
    computeSinkDissRate(type, point);
}

void SCDWrapper::getElectronInsertion(const int& n)
{
    /* Insert Frenkel pairs: */
    int64 SIAKey = (int64)pow(10.0, (double)EXP10 * (LEVELS - 1)); /* Key for SIA. */
    int64 vacancyKey = (-1) * SIAKey; /* Key for vacancy */
    if (allObjects.find(SIAKey) != allObjects.end()) {
        Object* tempObject = allObjects[SIAKey];
        tempObject->addNumber(n); /* default: increasing by one */
        updateObjectInMap(tempObject, n);
    }/* we have SIA */
    else {
        addNewObjectToMap(SIAKey, n);
    }/* we don't have SIA */
    if (allObjects.find(vacancyKey) != allObjects.end()) {
        Object* tempObject = allObjects[vacancyKey];
        tempObject->addNumber(n); /* default: increasing by one */
        updateObjectInMap(tempObject, n);
    }/* we have vacancy */
    else {
        addNewObjectToMap(vacancyKey, n);
    }
}

void SCDWrapper::getNeutronInsertion(const int & n)
{
    double neutronEnergy = 0.0;
    double totalEnergy = (double)Poisson(AVG_NEUTRON_EN);
    CascadeDamage damage;
    int i, j, ndef = 0;
    int64 clusterKey;
    while (neutronEnergy < totalEnergy) {
        double pkaEnergy = 0.0;
        while (pkaEnergy < 0.62) {
            // Limit to produce a stable Frenkel pair in keV (from Troev et al (2011)).
            double xi = (rand() / RAND_MAX) * cpdf.getMaxPossibility(n);
            neutronEnergy += pkaEnergy;
            pkaEnergy = cpdf.samplePkaEnergy(xi, n);
        }
        damage.generateNeutronDamage(pkaEnergy, ndef);
        for (i = 0; i < damage.size(); ++i) {
            int sign = (i == 0) ? -1 : 1;
            for (j = 0; j < ndef; ++j) {
                const int number = damage.getDamage(i, j);
                if (number != 0) {
                    clusterKey = (int64)sign*(j + 1)*(pow(10.0, (double)EXP10*(LEVELS - 1)));
                    /* generate cluster key */
                    if (allObjects.find(clusterKey) == allObjects.end()) {
                        Object* newObject = new Object(clusterKey, n, number);
                        addNewObjectToMap(newObject);
                    } /* we don't have this cluster */
                    else {
                        Object* tempObject = allObjects[clusterKey];
                        tempObject->addNumber(n, number);
                        updateObjectInMap(tempObject, n);
                    }/* we have this cluster */
                }
            }
        }
        damage.cleanDamage();
        neutronEnergy += pkaEnergy;
    }
}

void SCDWrapper::getIonInsertion(const int & n, const double& dt, fstream& fs)
{
    ++reactions[5][n];
    double ionEnergy = 0.0;
    double totalEnergy = (double)Poisson(AVG_ION_EN[n]);
    int i, j, ndef = 0;
    int nov = 0; //number of vacancy
    int64 clusterKey;
    //fstream fk;
    //fk.open("V_insertion.txt", ios::app);
    doseIon[n] += damage.getDpaRate(n)*dt;
    in_time += dt;
    //fk << damage.getDpaRate(n) << " * " << dt << "  " << in_time << "   ";
    CascadeDamage damage;
    while (ionEnergy < totalEnergy) {
        double pkaEnergy = 0.0;
        while (pkaEnergy < 620.0) {
            // Limit to produce a stable Frenkel pair in keV (from Troev et al (2011)).
            double xi = ((double)rand() / RAND_MAX) * cpdf.getMaxPossibility(n);
            ionEnergy += pkaEnergy;
            pkaEnergy = cpdf.samplePkaEnergy(xi, n);
        }
        damage.generateIonDamage(pkaEnergy, ndef);
        for (i = 0;i < damage.size();++i) {
            int sign = (i == 0) ? -1 : 1;
            for (j = 0; j < ndef; ++j) {
                const int number = damage.getDamage(i, j);
                if (number != 0) {
                    clusterKey = (int64)sign*(j + 1)*(pow(10.0, (double)EXP10*(LEVELS - 1)));
                    /*
                    if(clusterKey > 1000){
                        if (allObjects.find(clusterKey) == allObjects.end()) {
                            Object* newObject = new Object(clusterKey, n, number);
                            writeSInkFile(newObject, n, 0.0);
                        } // we don't have this cluster
                        else {
                            Object* tempObject = allObjects[clusterKey];
                            writeSInkFile(newObject, n, 0.0);
                        }// we have this cluster
                        continue;
                    }//park SIAs
                    */
                    /* generate cluster key */
                    if (allObjects.find(clusterKey) == allObjects.end()) {
                        Object* newObject = new Object(clusterKey, n, number);
                        nov = newObject->getAttri(0);
                        if(nov < 0){
                            generationNumber += (-1) * nov * number;
                        }
                        addNewObjectToMap(newObject);
                    } /* we don't have this cluster */
                    else {
                        Object* tempObject = allObjects[clusterKey];
                        tempObject->addNumber(n, number);
                        nov = tempObject->getAttri(0);
                        if(nov < 0){
                            generationNumber += (-1) * nov * number;
                        }
                        updateObjectInMap(tempObject, n);
                    }/* we have this cluster */
                    //fs <<"ion insertion in element "<< n <<", gain " << number <<" " << clusterKey <<endl;
                }
            }
        }
        damage.cleanDamage();
        ionEnergy += pkaEnergy;
    }
    //fk<<dt<<"   "<<nov<<endl;
    //fk.close();
}

void SCDWrapper::getParticleInsertion(const int& n, const double& dt, fstream& fs)
{
#ifdef ELECTRON
    getElectronInsertion(n);
#elif defined (NEUTRON)
    getNeutronInsertion(n);
#elif defined (ION)
    getIonInsertion(n, dt, fs);
#endif
}

void SCDWrapper::getHeInsertion(const int& n)
{
    int channel = 1;
    int64 clusterKey = atomProperty(INTERSTITIAL, channel + 1);
    if (allObjects.find(clusterKey) == allObjects.end()) {
        addNewObjectToMap(clusterKey, n);
    } /* we don't have this cluster */
    else {
        Object* tempObject = allObjects[clusterKey];
        tempObject->addNumber(n);
        updateObjectInMap(tempObject, n);
    }/* we have this cluster */
}

void SCDWrapper::getHInsertion(const int& n, const double& dt, fstream& fs)
{
    ++reactions[6][n];
    int channel = 2;
    int64 clusterKey = atomProperty(INTERSTITIAL, channel + 1);
    if (allObjects.find(clusterKey) == allObjects.end()) {
        addNewObjectToMap(clusterKey, n);
    } /* we don't have this cluster */
    else {
        Object* tempObject = allObjects[clusterKey];
        tempObject->addNumber(n);
        updateObjectInMap(tempObject, n);
    }/* we have this cluster */
    //fs << "H insertion: get 1 " << clusterKey <<" in element "<< n <<endl;
    fluenceH += 4.00e+16*dt; /* 4.00e+16 is H flux */
}

void restart(int & iStep, double & advTime, SCDWrapper *srscd)
{
    int64 objectKey;
    int number[LEVELS+1] = { 0 };
    int step = 0;
    string skip;
    string oneLine;
    stringstream lineHold;
    /* update sink numbers */
    ifstream file("sink.txt");
    if (file.good()) {
        for(int j = 0; j < POINTS; j++){
            while (getline(file, oneLine)) {
                lineHold.str(oneLine);
                for (int i = 0; i < LEVELS+1; i++) {
                    lineHold >> number[i];
                }
                srscd->updateSinks(j,number);
                lineHold.clear();
            }
        }
    }
    file.close();
    /* update species informtion */
    ifstream ofile("restart.txt");
    if (file.good()) {
        while (getline(ofile, oneLine)) {
            step++;
            lineHold.str(oneLine);
            if (step == 1) {
                /* update iStep */
                /* line 1 */
                lineHold >> skip >> skip >> iStep;
            }
            else if (step == 2){
                /* update advTime */
                /* line 2 */
                lineHold >> skip >> skip >> advTime;
            }
            else if(step == 3){
                /* line 3 */
                lineHold >> skip >> skip >> fluenceH;
            }else{
                lineHold >> skip >> objectKey;
                for (int i = 0; i < POINTS; i++) {
                    lineHold >> number[i];
                }
                srscd->addNewObjectToMap(objectKey, number);
            }
            lineHold.clear();
        }
    }
    ofile.close();
    
}

bool SCDWrapper::recognizeSAV(const Object *const hostObject, const Object *const theOtherObject){
    int hostAttrZero = hostObject->getAttri(0);
    int hostAttrTwo = hostObject->getAttri(2);
    int otherAttrZero = theOtherObject->getAttri(0);
    int otherAttrTwo = theOtherObject->getAttri(2);
    if(hostAttrZero < 0 && hostAttrTwo != 0){/* check whether it is a mV-nH cluster */
        /*
        if(otherAttrZero == 0 && (otherAttrTwo == 1 || otherAttrTwo == 2)){
            //check whether the other cluster is H or 2H
            double ratio = (double)hostAttrTwo/(double)abs(hostAttrZero);
            double size = double(abs(hostAttrZero));
            //double penalty1 = 1.534804 + 0.055303 * ratio - 0.010013*(pow( ratio, 3.0))*acosh(size);
            double penalty1 = 2.7013 + 3.41884/(pow(size,2.0) - 2.9719) - 0.00071*size*pow(ratio, 4.0);
            double penalty2 = 2.6859 - 2.0824/(4.9933 - 2.7605*size)- 0.02148*pow(ratio,3.0);
            //symbolic regression function of reaction "mV-nH + 2H"
            if(otherAttrTwo == 1){
                // reaction 1
                if(penalty1 < 0){
                    return true;
                    
                }else{
                    return false;
                    
                }
                
            }else if(otherAttrTwo == 2){
                if(penalty2 < 0){
                    return true;
                    
                }else{
                    return false;
                    
                }
                
            }
            
        }else{
            return false;
            //if not H or 2H code do original combination reactio;
            
        }
        */
        return false;
        
    }else if(hostAttrZero == 0 && hostAttrTwo != 0){/* check if it is pure nH cluster*/
        if(otherAttrZero == 0 && (otherAttrTwo == 1 || otherAttrTwo == 2)){
            // check whether the other cluster is H or 2H
            if(hostAttrTwo > 1){
                return true;
            }else{
                return false;
            }
            
        }else{
            return false;
        }
        //return false;
    }else{
        return false; /* if not, return false, code do original combination reaction */
    }
    return false;
}

int SCDWrapper::countDefectNumber(const int& count, char* type){
    fstream vd, id, hd;
    fstream v1d, v2d, v3d;
    /**
     * vd : vacancy number vs depth file\
     * id : SIA number vs depth file\
     * hd : hydrogen number vs depth file\
     **/
    if(type == "V"){
        vd.open("vd.txt", ios::out);
        /*
        v1d.open("v1d.txt", ios::out);
        v2d.open("v2d.txt", ios::out);
        v3d.open("v3d.txt", ios::out);
        */
        fv.open("vt.txt", ios::app);
        
    }else if(type == "SIA"){
        //id.open("id.txt", ios::out);
    }else{
        hd.open("hd.txt", ios::out);
    }
    double v = 0.0;
    /* this file work after damage*/
    
    int ndef[POINTS] = {0}; /* number of this object in every element*/
    int nv1[POINTS] = {0}, nv2[POINTS] = {0}, nv3[POINTS] = {0};
    int tndef = 0; /*number of this kind of defect in total */
    unordered_map<int64, Object*>::iterator iter;
    for(int i=0; i<POINTS; i++){
        double volume;
        if(i==0){
            volume = (VOLUME/20) * SURFACE_THICKNESS; /* volume on surface */
        }else{
            volume = VOLUME;
        }
        for (iter = allObjects.begin(); iter != allObjects.end(); ++iter){
            Object* thisObject = iter -> second;
            int totalNumber  = thisObject -> getNumber(i);
            int attribute = thisObject -> getAttri(count);
            if(count == 0){
                if(attribute>0 && type=="SIA"){
                    ndef[i] += totalNumber * abs(attribute);
                }else if(attribute<0 && type =="V"){
                    ndef[i] += totalNumber * abs(attribute);
                    if(attribute == -1){
                        nv1[i] = totalNumber;
                    }else if(attribute == -2){
                        nv2[i] = totalNumber;
                    }else if(attribute == -3){
                        nv3[i] = totalNumber;
                    }
                }
                
            }else{
                ndef[i] += totalNumber * abs(attribute);
                
            }
        }
        tndef += ndef[i];
        if(type == "V"){
            if(i==0){
                vd << i << "      " << ndef[i]/volume << endl;
                //v1d << i << "   " << nv1[i]/volume << endl;
                //v2d << i << "   " << nv2[i]/volume << endl;
                //v3d << i << "   " << nv3[i]/volume << endl;
                v += ndef[i]/volume;
            }else{
                vd << 10+20*(i-1) << "      "<<ndef[i]/volume<<endl;
                //v1d << 18+36*(i-1) << "   " << nv1[i]/volume << endl;
                //v2d << 18+36*(i-1) << "   " << nv2[i]/volume << endl;
                //v3d << 18+36*(i-1) << "   " << nv3[i]/volume << endl;
                v += ndef[i]/volume;
            }
            
         }else if(type == "SIA"){
            if(i==0){
                
                //id << i << "     "<<ndef[i]/volume<<endl;
            }else{
                //id<< 18+36*(i-1) << "     "<<ndef[i]/volume<<endl;
            }
         }else{
            if(i==0){
                hd<< i << "     "<<ndef[i]/volume<<endl;
            }else{
                hd<< 10+20*(i-1) << "     "<<ndef[i]/volume<<endl;
            }
        }
    }
    
    if(type == "V"){
        fv << v << endl;
        vd.close();
        //v1d.close();
        //v2d.close();
        //v3d.close();
        fv.close();
        
    }else if(type == "SIA"){
        //id.close();
    }else{
        hd.close();
    }
    return tndef;
}

void SCDWrapper::sizeDistribution(){
    unordered_map<int, int> sizeD;
    /* preset size from -6 to 6 */
    for(int preSize = -6; preSize<= 6; preSize++){
        std::pair<int, int> oneSize(preSize, 0);
        sizeD.insert(oneSize);
    }
    unordered_map<int64, Object*>::iterator iter;
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter){
        Object* thisObject = iter -> second;
        int totalNumber  = thisObject -> getTotalNumber();
        int attribute = thisObject -> getAttri(0);
        if(sizeD.find(attribute) != sizeD.end()){
           /* find this size */
            sizeD[attribute] += totalNumber;
        }else{
            std::pair<int, int> oneSize(attribute, totalNumber);
            sizeD.insert(oneSize);
        }
    }
    /* write to a file */
    fstream sd;
    sd.open("sd.txt", ios::out);
    unordered_map<int, int>::iterator iterr;
    for (iterr = sizeD.begin(); iterr != sizeD.end(); ++iterr){
        int n = iterr -> first;
        if(n > 0){
            //SIA cluster
            sd << iterr->first << "  " <<pow(ATOMICVOLUME*n/PI/BURGER, 0.5)<<"  "<< iterr->second/VOLUME<<endl;
            
        }else{
            //V cluster
            sd << iterr->first << "  " <<pow(3*ATOMICVOLUME*abs(n)/4/PI/BURGER, 0.333)<<"   "<< iterr->second/VOLUME<<endl;
        }
    }
    sd.close();
}

void SCDWrapper::writeReaction(){
    fstream fr;
    fr.open("reaction.txt", ios::out);
    for(int i=0; i<8; i++){
        for(int j=0; j<POINTS; j++){
            fr << i <<"    "<< j <<"    "<<reactions[i][j]<<endl;
        }
    }
    fr.close();
}


/* test functions */
void SCDWrapper::countRatioDistribution(double& t){
    double sW = DENSITY * (VOLUME / 36) * SURFACE_THICKNESS;
    /*number of surface tungsten */
    int sH = 0; /* number of surface hydrogen*/
    unordered_map<int64, Object*>::iterator iter;
    fstream fo;
    fstream vha; // vacancy hydrogen number of all objects
    fstream vhc; // vacancy hydrogen number of only (V-H)clusters
    //fo.open("object.txt", ios::out);
    //vha.open("vha.txt", ios::app);
    //vhc.open("vhc.txt", ios::app);
    double ah = 0.0, av = 0.0, ch = 0.0, cv = 0.0;
    // all H, all V, cluster H, cluster V
    for (iter = allObjects.begin(); iter != allObjects.end(); ++iter){
        Object* thisObject = iter -> second;
        int attrTwo = thisObject->getAttri(2);
        int attrZero = thisObject->getAttri(0);
        int totalNumber = thisObject->getTotalNumber();
        
        if(attrZero < 0){
            av += abs(attrZero) * totalNumber;
        }
        ah += attrTwo * totalNumber;
        if(attrZero < 0 && attrTwo >0){
            /* if have vacancy */
            /* a v-h cluster */
            cv += abs(attrZero) * totalNumber;
            ch += attrTwo * totalNumber;
        }
        int surfaceNumber = thisObject->getNumber(0); /* get the number on surface */
        sH += attrTwo * surfaceNumber;  /* get total number of H on surface */
        //fo << attrZero << " " << attrTwo << "   "<< totalNumber << endl;
    }
    /*
    if(av == 0.0){
        vha << t << "   -1" << endl;
    }else{
        vha << t<< "    "<< ah/av << endl;
    }
    if(cv == 0.0){
        vhc << t << "   -1" << endl;
    }else{
        vhc << t << "   " << ch/cv <<endl;
    }
    */
    //fo.close();
    //vha.close();
    //vhc.close();
   
}

void SCDWrapper::test(const int& inputV){
    ofstream fo;
    fo.open("SAV.txt", ios::app);
    if(start){
        fo<<"DoseH"<<"  "<<"InputVacancy"<<"    "<<"NowV"<<"    "<<"AnnilV"<<"  "<<"CreateV"<<" "<<"NowH"<<"    "<<"V/H"<<endl;
        start = 0;
    }
    int nowV = countDefectNumber(0, "V");
    int hydrogen = countDefectNumber(2, "H");
    fo << fluenceH << "    " << inputV/VOLUME << " " << nowV/VOLUME << " " << sinkV/VOLUME << " " << annilV/VOLUME << "  " << (nowV+sinkV+annilV-inputV)/VOLUME << "   " << hydrogen/VOLUME << " ";
    if(hydrogen == 0){
        fo << "---" << endl;
    }else{
        fo << double(hydrogen)/double(nowV) << endl;
    }
    fo.close();
}

void SCDWrapper::drawSpeciesAndReactions( double&t ){
    /* draw species */
    countRatioDistribution(t);
    char buffer[20];
    char v[20];
    gcvt(t, 8, buffer);
    gcvt(VOLUME, 1, v);
    std::ostringstream cmdstr;
    std::ostringstream cmdstr1;
    //gs.cmd("set key bmargin left horizontal Right noreverse enhanced title \"Td = 573K \" box\n");
    //gs.cmd("set grid \n");
    cmdstr<<"plot \"object.txt\" using 1:2:3 with labels title \" t = "<<buffer<<" s,   V= " << v << " cm^3 \" font \"New-Roman, 20, Bold \" textcolor lt 7 \n";
    //gs.cmd(cmdstr.str());
    
    /* draw reactions */
    //writeReaction();
    //gr.cmd("set key bmargin left horizontal Right noreverse enhanced title \"Td = 300K \" box\n");
    //gr.cmd("set grid \n");
    // gr.cmd("set view map\n");
    //gr.cmd("set cbrange[0:500]\n");
    //gr.cmd("set cbtics add ('>1000' 1000) \n");
    //gr.cmd("set pm3d map\n");
    //gr.cmd("set palette model CMY rgbformulae 7, 5, 15\n");
    //gr.cmd("set palette defined(0 \"green\", 1 \"blue\", 2 \"red\" , 3 \"orange\", 4 \"yellow\", 5 \"grey\")\n");
    //cmdstr1<<"splot \"reaction.txt\" using 2:1:3 with points palette ps 2 pt 3 title \" t = "<<buffer<<" s,   V= " << v << " cm^3 \" \n";
    //gr.cmd("set pm3d map\n");
    //gr.cmd(cmdstr1.str());
    /*
    if(plotTime1 == 1){
        cout<<"press a char"<<endl;
        getchar();
        plotTime1++;
    }
    */
    //sleep(0.02);
    
}

void SCDWrapper::drawDamage(double& t){
    /* draw size distribution */
    char buffer[20];
    gcvt(t,8,buffer);
    double dpa = getTotalDpa();
    std::ostringstream cmdstr;
    std::ostringstream cmdstr1;
    std::ostringstream cmdstr2;
    sizeDistribution();
    cmdstr2 <<"set key bmargin left horizontal Right noreverse enhanced title \"T = "<< TEMPERATURE <<"K \" box\n";
    //gd1.cmd(cmdstr2.str());
    cmdstr<<"plot \"sd.txt\" using 1:2 with histeps title \" t = "<<buffer<<" \" lw 3\n";
    //gd1.cmd(cmdstr.str());
    /* draw concentration-depth */
    fv.open("vt.txt", ios::app);
    fv << dpa << "    ";
    fv.close();
    countDefectNumber(0, "V");
    countDefectNumber(0, "SIA");
    //gd2.cmd(cmdstr2.str());
    //cmdstr1<<"plot \"vd.txt\" using 1:2 with lp title \" t = "<<buffer<<" V \" lw 3\n";
    //cmdstr2<<"replot \"id.txt\" using 1:2 with lp title \" t = "<<buffer<<" SIA \" lw 3\n";
    //gd2.cmd(cmdstr1.str());
    //gd2.cmd(cmdstr2.str());
    //cmdstr1<<"plot \"vd.txt\" using 1:2 with lp title \" t = "<<buffer<<"       V \" lw 3, \"id.txt\" using 1:2 with lp title \" SIA \" lw 3\n";
    cmdstr1<<"plot \"vd.txt\" using 1:2 with lp title \" dpa = " << dpa << "       V \" lw 3, \"id.txt\" using 1:2 with lp title \" SIA \" lw 3\n";
    //gd2.cmd(cmdstr1.str());
    //sleep(0.02);
    //gv.cmd("plot \"vt.txt\" using 1:2 w lp lw 3 title \"V\" \n");
    /* wait to adjust the screen locations */
    /*
    if(plotTime == 1){
        cout<<"press a char"<<endl;
        getchar();
        plotTime++;
    }
    */
}

void SCDWrapper::drawHD(double& t){
    /* draw concentration-depth */
    char buffer[20];
    gcvt(t,8,buffer);
    std::ostringstream cmdstr1;
    countDefectNumber(0, "V");
    countDefectNumber(0, "SIA");
    countDefectNumber(2, "H");
    //gh1.cmd("set key bmargin left horizontal Right noreverse enhanced title \"Td= 573K \" box\n");
    cmdstr1<<"plot \"vd.txt\" using 1:2 with lp title \" t = "<<buffer<<" V \" lw 3, \"id.txt\" using 1:2 with lp title \"SIA\" lw 3, \"hd.txt\" using 1:2 with lp title \"H\" lw 3\n";
    //gh1.cmd(cmdstr1.str());
    
   
    /* draw H to V ratio */
    //gh2.cmd("plot \"vha.txt\" using 1:2 w l title \" whole H/V \" lw 3, \"vhc.txt\" using 1:2 w l title \" cluster H/V \" lw 3 \n");
    
    if(plotTime2==1){
        getchar();
        ++plotTime2;
    }
    //sleep(0.02);
}

void SCDWrapper::writeVacancy(){
    double dpa = getTotalDpa();
    fstream fv;
    fv.open("V.txt", ios::app);
    fv<<"At dpa = " <<dpa<<", "<<generationNumber<<" vacancies are generated, "<<annilV<<" consumed from combination, "<<sinkV<<" goes to sink, "<< generationNumber - annilV - sinkV<<" left in bulk"<<endl;
    fv.close();
}

double SCDWrapper::getTotalDpa(){
    double dpa = 0;;
    for(int i = 0; i<POINTS; i++){
        dpa += doseIon[i];
    }
    return dpa;
}


