/**
 * \author Sylvain Marsat, University of Maryland - NASA GSFC
 *
 * \brief C code headers for the geometric coefficients entering the response for LISA-like detectors.
 *
 */

#ifndef _LISAGEOMETRY_H
#define _LISAGEOMETRY_H

#define _XOPEN_SOURCE 500

#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <complex.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_bspline.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_complex.h>

#include "constants.h"
#include "struct.h"

#if defined(__cplusplus)
#define complex _Complex
extern "C" {
#elif 0
} /* so that editors will match preceding brace */
#endif

/*****************************************************/
/**************** TDI variables **********************/

typedef enum TDItag {
  delayO,                  /* Orbital delay */
  y12L,                    /* Constellation-only response y12L */
  y12,                     /* Complete response y12 (includes orbital delay) */
  TDIXYZ,
  TDIalphabetagamma,
  TDIAETXYZ,
  TDIAETalphabetagamma,
  TDIX,
  TDIalpha,
  TDIAXYZ,
  TDIEXYZ,
  TDITXYZ,
  TDIAalphabetagamma,
  TDIEalphabetagamma,
  TDITalphabetagamma
} TDItag;

/* Enumerator to choose what level of low-f approximation to do in the response */
typedef enum ResponseApproxtag {
  full,
  lowfL,
  lowf
} ResponseApproxtag;

/**************************************************/
/************** LISAconstellation *****************/

typedef enum LISANoiseType{
  LISA2010noise,
  LISA2017noise,
  LISAProposalnoise
} LISANoiseType;

typedef struct tagLISAconstellation {
  double OrbitOmega;
  double OrbitPhi0;
  double OrbitR;
  double ConstOmega;
  double ConstPhi0;
  double ConstL;
  LISANoiseType noise;
} LISAconstellation;
extern LISAconstellation LISAProposal;
extern LISAconstellation LISA2017;
extern LISAconstellation LISA2010;
extern LISAconstellation slowOrbitLISA;
extern LISAconstellation tinyOrbitLISA;
extern LISAconstellation fastOrbitLISA;
extern LISAconstellation bigOrbitLISA;

/**************************************************/
/**************** Prototypes **********************/

/* Function to convert string input TDI string to TDItag */
TDItag ParseTDItag(char* string);

/* Function to convert string input ResponseApprox to tag */
ResponseApproxtag ParseResponseApproxtag(char* string);

/* Function cardinal sine */
double sinc(const double x);

/* Compute Solar System Barycenter time tSSB from retarded time at the center of the LISA constellation tL */
/* NOTE: depends on the sky position given in SSB parameters */
double tSSBfromLframe(const LISAconstellation *variant, const double tL, const double lambdaSSB, const double betaSSB);
/* Compute retarded time at the center of the LISA constellation tL from Solar System Barycenter time tSSB */
double tLfromSSBframe(const LISAconstellation *variant, const double tSSB, const double lambdaSSB, const double betaSSB);
/* Convert L-frame params to SSB-frame params */
/* NOTE: no transformation of the phase -- approximant-dependence with e.g. EOBNRv2HMROM setting phiRef at fRef, and freedom in definition */
int ConvertLframeParamsToSSBframe(
  double* tSSB,
  double* lambdaSSB,
  double* betaSSB,
  double* polSSB,
  const double tL,
  const double lambdaL,
  const double betaL,
  const double psiL,
  const LISAconstellation *variant);
/* Convert SSB-frame params to L-frame params */
/* NOTE: no transformation of the phase -- approximant-dependence with e.g. EOBNRv2HMROM setting phiRef at fRef, and freedom in definition */
int ConvertSSBframeParamsToLframe(
  double* tL,
  double* lambdaL,
  double* betaL,
  double* polL,
  const double tSSB,
  const double lambdaSSB,
  const double betaSSB,
  const double psiSSB,
  const LISAconstellation *variant);

/* Function to compute, given a value of a sky position and polarization, all the complicated time-independent trigonometric coefficients entering the response */
void SetCoeffsG(const double lambda, const double beta, const double psi);

/* Functions evaluating the G_AB functions, combining the two polarization with the spherical harmonics factors */
double complex G21mode(const LISAconstellation *LISAvariant, const double f, const double t, const double complex Yfactorplus, const double complex Yfactorcross);
double complex G12mode(const LISAconstellation *LISAvariant, const double f, const double t, const double complex Yfactorplus, const double complex Yfactorcross);
double complex G32mode(const LISAconstellation *LISAvariant, const double f, const double t, const double complex Yfactorplus, const double complex Yfactorcross);
double complex G23mode(const LISAconstellation *LISAvariant, const double f, const double t, const double complex Yfactorplus, const double complex Yfactorcross);
double complex G13mode(const LISAconstellation *LISAvariant, const double f, const double t, const double complex Yfactorplus, const double complex Yfactorcross);
double complex G31mode(const LISAconstellation *LISAvariant, const double f, const double t, const double complex Yfactorplus, const double complex Yfactorcross);
int EvaluateGABmode(
  const LISAconstellation *LISAvariant,
  double complex* G12,                     /* Output for G12 */
  double complex* G21,                     /* Output for G21 */
  double complex* G23,                     /* Output for G23 */
  double complex* G32,                     /* Output for G32 */
  double complex* G31,                     /* Output for G31 */
  double complex* G13,                     /* Output for G13 */
  const double f,                          /* Frequency */
  const double t,                          /* Time */
  const double complex Yfactorplus,        /* Spin-weighted spherical harmonic factor for plus */
  const double complex Yfactorcross,       /* Spin-weighted spherical harmonic factor for cross */
  const int tagdelayR,                     /* Tag: when 1, include the phase term of the R-delay */
  const ResponseApproxtag responseapprox); /* Tag to select possible low-f approximation level in FD response */

/* Functions evaluating the Fourier-domain factors (combinations of the GAB's) for TDI observables */
/* NOTE: factors have been scaled out, in parallel of what is done for the noise function */
/* Note: in case only one channel is considered, amplitudes for channels 2 and 3 are simply set to 0 */
/* (allows minimal changes from the old structure that assumed KTV A,E,T - but probably not optimal) */
int EvaluateTDIfactor3Chan(
  const LISAconstellation *variant,    /* Description of LISA variant */
  double complex* factor1,                       /* Output for factor for TDI channel 1 */
  double complex* factor2,                       /* Output for factor for TDI channel 2 */
  double complex* factor3,                       /* Output for factor for TDI channel 3 */
  const double complex G12,                      /* Input for G12 */
  const double complex G21,                      /* Input for G21 */
  const double complex G23,                      /* Input for G23 */
  const double complex G32,                      /* Input for G32 */
  const double complex G31,                      /* Input for G31 */
  const double complex G13,                      /* Input for G13 */
  const double f,                                /* Frequency */
  const TDItag tditag,                           /* Selector for the TDI observables */
  const ResponseApproxtag responseapprox);       /* Tag to select possible low-f approximation level in FD response */
/* int EvaluateTDIfactor1Chan( */
/*   double complex* factor,                       /\* Output for factor for TDI channel *\/ */
/*   const double complex G12,                      /\* Input for G12 *\/ */
/*   const double complex G21,                      /\* Input for G21 *\/ */
/*   const double complex G23,                      /\* Input for G23 *\/ */
/*   const double complex G32,                      /\* Input for G32 *\/ */
/*   const double complex G31,                      /\* Input for G31 *\/ */
/*   const double complex G13,                      /\* Input for G13 *\/ */
/*   const double f,                                /\* Frequency *\/ */
/*   const TDItag tditag);                          /\* Selector for the TDI observable *\/ */
/* Function evaluating the Fourier-domain factors that have been scaled out of TDI observables */
/* The factors scaled out, parallel what is done for the noise functions */
/* Note: in case only one channel is considered, factors for channels 2 and 3 are simply set to 0 */
int ScaledTDIfactor3Chan(
  const LISAconstellation *variant,    /* Description of LISA variant */
  double complex* factor1,                       /* Output for factor for TDI factor 1 */
  double complex* factor2,                       /* Output for factor for TDI factor 2 */
  double complex* factor3,                       /* Output for factor for TDI factor 3 */
  const double f,                                /* Frequency */
  const TDItag tditag);                          /* Selector for the TDI observables */
/* Function restoring the factor that have been scaled out of the TDI observables */
/* NOTE: the operation is made in-place, and the input is overwritten */
int RestoreInPlaceScaledFactorTDI(
  const LISAconstellation *variant,    /* Description of LISA variant */
  ListmodesCAmpPhaseFrequencySeries* listtdi,     /* Output/Input: list of mode contributions to TDI observable */
  TDItag tditag,                                  /* Tag selecting the TDI observable */
  int nchannel);                                  /* TDI channel number */

/* Functions for the response in time domain */

/* Basic yslr observables (including orbital delay) from hplus, hcross */
double y12TD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
double y21TD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
double y23TD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
double y32TD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
double y31TD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
double y13TD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
/* TDI observables (including orbital delay) from hplus, hcross */
int EvaluateTDIXYZTDhphc(
  const LISAconstellation *variant,    /* Description of LISA variant */
  double* TDIX,                            /* Output: value of TDI observable X */
  double* TDIY,                            /* Output: value of TDI observable Y */
  double* TDIZ,                            /* Output: value of TDI observable Z */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */
int EvaluateTDIAETXYZTDhphc(
  const LISAconstellation *variant,    /* Description of LISA variant */
  double* TDIA,                            /* Output: value of TDI observable X */
  double* TDIE,                            /* Output: value of TDI observable Y */
  double* TDIT,                            /* Output: value of TDI observable Z */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  const double t);                         /* Time */

/* Generate hO orbital-delayed for one mode contribution from amp, phase */
int Generateh22TDO(
  const LISAconstellation *variant,    /* Description of LISA variant */
  AmpPhaseTimeSeries** h22tdO,             /* Output: amp/phase time series for h22TDO */
  gsl_spline* splineamp,                   /* Input spline for TD mode amplitude */
  gsl_spline* splinephase,                 /* Input spline for TD mode phase */
  gsl_interp_accel* accelamp,              /* Accelerator for amp spline */
  gsl_interp_accel* accelphase,            /* Accelerator for phase spline */
  gsl_vector* times,                       /* Vector of times to evaluate */
  int nbptmargin);                         /* Margin set to 0 on both side to avoid problems with delays out of the domain */
/* Generate y12L from orbital-delayed h22 in amp/phase form */
int Generatey12LTD(
  const LISAconstellation *variant,    /* Description of LISA variant */
  RealTimeSeries** y12Ltd,                 /* Output: real time series for y12L */
  gsl_spline* splineamp,                   /* Input spline for TD mode amplitude */
  gsl_spline* splinephase,                 /* Input spline for TD mode phase */
  gsl_interp_accel* accelamp,              /* Accelerator for amp spline */
  gsl_interp_accel* accelphase,            /* Accelerator for phase spline */
  gsl_vector* times,                       /* Vector of times to evaluate */
  double Theta,                            /* Inclination */
  double Phi,                              /* Phase */
  int nbptmargin);                         /* Margin set to 0 on both side to avoid problems with delays out of the domain */

/* Generate TDI observables (including orbital delay) for one mode contritbution from amp, phase */
/* NOTE: developed for testing purposes, so far supports only dO and y12L */
int GenerateTDITD3Chanhlm(
  AmpPhaseTimeSeries** hlm,                /* Output: real time series for TDI channel 1 */
  RealTimeSeries** TDI2,                   /* Output: real time series for TDI channel 2 */
  RealTimeSeries** TDI3,                   /* Output: real time series for TDI channel 3 */
  gsl_spline* splineamp,                   /* Input spline for TD mode amplitude */
  gsl_spline* splinephase,                 /* Input spline for TD mode phase */
  gsl_interp_accel* accelamp,              /* Accelerator for amp spline */
  gsl_interp_accel* accelphase,            /* Accelerator for phase spline */
  gsl_vector* times,                       /* Vector of times to evaluate */
  int nbptsmargin,                         /* Margin set to 0 on both side to avoid problems with delays out of the domain */
  double theta,                            /* Inclination angle - used to convert hlm to hplus, hcross for y12L - ignored for dO */
  double phi,                              /* Observer phase - used to convert hlm to hplus, hcross for y12L - ignored for dO */
  TDItag tditag);                          /* Tag selecting the TDI observables */

/* Generate TDI observables (including orbital delay) for one mode contritbution from hplus, hcross */
int GenerateTDITD3Chanhphc(
  const LISAconstellation *variant,    /* Description of LISA variant */
  RealTimeSeries** TDI1,                   /* Output: real time series for TDI channel 1 */
  RealTimeSeries** TDI2,                   /* Output: real time series for TDI channel 2 */
  RealTimeSeries** TDI3,                   /* Output: real time series for TDI channel 3 */
  gsl_spline* splinehp,                    /* Input spline for TD hplus */
  gsl_spline* splinehc,                    /* Input spline for TD hcross */
  gsl_interp_accel* accelhp,               /* Accelerator for hp spline */
  gsl_interp_accel* accelhc,               /* Accelerator for hc spline */
  gsl_vector* times,                       /* Vector of times to evaluate */
  int nbptsmargin,                         /* Margin set to 0 on both side to avoid problems with delays out of the domain */
  TDItag tditag);                          /* Tag selecting the TDI observables */

#if 0
{ /* so that editors will match succeeding brace */
#elif defined(__cplusplus)
}
#endif

#endif /* _LISAGEOMETRY_H */


#ifndef INITIAL_POINT_H
#define INITIAL_POINT_H

#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_math.h>

int pdip_initial_point_feasible_x( const gsl_matrix * A, const gsl_vector *b, gsl_vector *x );
int pdip_initial_point_feasible_s( const gsl_matrix * C, const gsl_vector *d, const gsl_vector *x, gsl_vector *s );
int pdip_initial_point_y( const gsl_matrix *Q, const gsl_vector *q, const gsl_matrix *A, const gsl_vector *x, gsl_vector *y );
int pdip_initial_point_z( gsl_vector *z );
int pdip_initial_point_strict_feasible( gsl_vector *x, gsl_vector *s );

#endif


/**
 *
 * @file qwrapper_zgemv.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mark Gates
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @precisions normal z -> c d s
 *
 **/
#include <cblas.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zgemv(Quark *quark, Quark_Task_Flags *task_flags,
                      PLASMA_enum trans, int m, int n,
                      PLASMA_Complex64_t alpha, const PLASMA_Complex64_t *A, int lda,
                                                const PLASMA_Complex64_t *x, int incx,
                      PLASMA_Complex64_t beta,        PLASMA_Complex64_t *y, int incy)
{
    DAG_CORE_GEMV;
    QUARK_Insert_Task(quark, CORE_zgemv_quark, task_flags,
        sizeof(PLASMA_enum),             &trans,  VALUE,
        sizeof(int),                     &m,      VALUE,
        sizeof(int),                     &n,      VALUE,
        sizeof(PLASMA_Complex64_t),      &alpha,  VALUE,
        sizeof(PLASMA_Complex64_t)*m*n,  A,               INPUT,
        sizeof(int),                     &lda,    VALUE,
        sizeof(PLASMA_Complex64_t)*n,    x,               INPUT,
        sizeof(int),                     &incx,   VALUE,
        sizeof(PLASMA_Complex64_t),      &beta,   VALUE,
        sizeof(PLASMA_Complex64_t)*m,    y,               INOUT,
        sizeof(int),                     &incy,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zgemv_quark = PCORE_zgemv_quark
#define CORE_zgemv_quark PCORE_zgemv_quark
#endif
void CORE_zgemv_quark(Quark *quark)
{
    PLASMA_enum trans;
    int m, n, lda, incx, incy;
    PLASMA_Complex64_t alpha, beta;
    const PLASMA_Complex64_t *A, *x;
    PLASMA_Complex64_t *y;

    quark_unpack_args_11( quark, trans, m, n, alpha, A, lda, x, incx, beta, y, incy );
    cblas_zgemv(
        CblasColMajor,
        (CBLAS_TRANSPOSE)trans,
        m, n,
        CBLAS_SADDR(alpha), A, lda,
                            x, incx,
        CBLAS_SADDR(beta),  y, incy);
}


#include <sbmf/sbmf.h>

#define SBMF_UNUSED(x) \
	(void)x

#include <omp.h>
#include <cblas.h>
#include <lapacke.h>
#include <arpack/arpack.h>
/* #include <arpack/debug_c.h> */

/* c stdlib */
#include <stdio.h>  /* sprintf, fopen, fprintf, fread */
#include <stdlib.h> /* malloc */
#include <string.h> /* memcpy,memset */
#include <stdarg.h>
#include <assert.h>
#include <float.h>  /* isinf, isnan */

/* unix headers */
#include <signal.h>
#include <time.h>

/*
 * Getting into actual code
 */

#include "profile.c"
#include "memory/stack_allocator.c"
#include "global_state.c"
#include "indices.c"
#include "memory/bucketarray.c"
#include "memory/prioqueue.c"
#include "math/functions.c"
#include "math/matrix.c"
#include "math/find_eigenpairs.c"
#include "math/basis.c"
#include "math/hermite_integrals.c"
#include "methods/quadgk.c"
#include "methods/nlse_solver.c"
#include "methods/grosspitaevskii.c"
#include "methods/best_meanfield.c"
#include "methods/perturbation_theory.c"

/*
 * Logging definitions
 */

#define MAX_LOG_MSG_LEN 128

void sbmf_set_log_callback(sbmf_log_callback_func* func) {
	_state.log_callback = func;
}

static void sbmf_log(enum sbmf_log_level log_level, const char* fmt, va_list args) {
	if (!_state.log_callback)
		return;

	static char msg_buffer[MAX_LOG_MSG_LEN];

	vsnprintf(msg_buffer, MAX_LOG_MSG_LEN, fmt, args);
	_state.log_callback(log_level, msg_buffer);
}

void sbmf_log_info(const char* fmt, ...) {
	va_list args;
	va_start(args, fmt);
	sbmf_log(SBMF_LOG_LEVEL_INFO, fmt, args);
	va_end(args);
}

void sbmf_log_warning(const char* fmt, ...) {
	va_list args;
	va_start(args, fmt);
	sbmf_log(SBMF_LOG_LEVEL_WARNING, fmt, args);
	va_end(args);
}

void sbmf_log_error(const char* fmt, ...) {
	va_list args;
	va_start(args, fmt);
	sbmf_log(SBMF_LOG_LEVEL_ERROR, fmt, args);
	va_end(args);
}

void sbmf_log_panic(const char* fmt, ...) {
	va_list args;
	va_start(args, fmt);
	sbmf_log(SBMF_LOG_LEVEL_PANIC, fmt, args);
	va_end(args);
}


/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

/*!
 *  \file reconstruct_line2d.c
 *  \brief reconstruct 2d line and BLR model.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <gsl/gsl_interp.h>

#include "brains.h"

void *best_model_line2d;      /*!< best model */
void *best_model_std_line2d;  /*!< standard deviation of the best model */

/*!
 * postprocessing.
 */
void postprocess2d()
{
  char posterior_sample_file[BRAINS_MAX_STR_LENGTH];
  int num_ps, i, j, k, nc;
  double *pm, *pmstd;
  double *lag;
  void *posterior_sample, *post_model;
  double mean_lag, mean_lag_std, sum1, sum2;
  int size_of_modeltype = num_params * sizeof(double);

  best_model_line2d = malloc(size_of_modeltype);
  best_model_std_line2d = malloc(size_of_modeltype);

  if(thistask == roottask)
  {
    // initialize smoothing workspace
    smooth_init(n_vel_data_ext, Vline_data_ext);
    char fname[200];
    FILE *fp, *fcon, *fline, *ftran, *fline1d;
    double *Fline1d, dV;

    // velocity grid width, in term of wavelength of Hbeta.
    dV = (Vline_data[n_vel_data-1]-Vline_data[0])/(n_vel_data-1) * parset.linecenter/C_Unit;
    Fline1d = malloc(n_line_data * sizeof(double));

    // get number of lines in posterior sample file
    get_posterior_sample_file(dnest_options_file, posterior_sample_file);

    //file for posterior sample
    fp = fopen(posterior_sample_file, "r");
    if(fp == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", posterior_sample_file);
      exit(0);
    }
    //file for continuum reconstruction
    sprintf(fname, "%s/%s", parset.file_dir, "data/con_rec.txt");
    fcon = fopen(fname, "w");
    if(fcon == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
      exit(0);
    }
    //file for line reconstruction
    sprintf(fname, "%s/%s", parset.file_dir, "data/line2d_rec.txt");
    fline = fopen(fname, "w");
    if(fline == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
      exit(0);
    }

    //file for line reconstruction
    sprintf(fname, "%s/%s", parset.file_dir, "data/line_rec.txt");
    fline1d = fopen(fname, "w");
    if(fline1d == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
      exit(0);
    }

    //file for transfer function
    sprintf(fname, "%s/%s", parset.file_dir, "data/tran2d_rec.txt");
    ftran = fopen(fname, "w");
    if(ftran == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
      exit(0);
    }

    // read number of lines
    if(fscanf(fp, "# %d", &num_ps) < 1)
    {
      fprintf(stderr, "# Error: Cannot read file %s.\n", posterior_sample_file);
      exit(0);
    }
    printf("# Number of points in posterior sample: %d\n", num_ps);

    lag = malloc(num_ps * sizeof(double));
    post_model = malloc(size_of_modeltype);
    posterior_sample = malloc(num_ps * size_of_modeltype);

    force_update = 1;
    which_parameter_update = -1; // force to update the transfer function
    which_particle_update = 0;
    mean_lag = 0.0;
    nc = 0;

    Fcon_rm = Fcon_rm_particles[which_particle_update];
    TransTau = TransTau_particles[which_particle_update];
    Trans2D_at_veldata = Trans2D_at_veldata_particles[which_particle_update];
    Fline2d_at_data = Fline_at_data_particles[which_particle_update];

    for(i=0; i<num_ps; i++)
    {
      // read lines
      for(j=0; j<num_params; j++)
      {
        if(fscanf(fp, "%lf", (double *)post_model + j) < 1)
        {
          fprintf(stderr, "# Error: Cannot read file %s.\n", posterior_sample_file);
          exit(0);
        }
      }
      fscanf(fp, "\n");

      //store model
      memcpy(posterior_sample+i*size_of_modeltype, post_model, size_of_modeltype);

      //calculate_con_from_model(post_model + num_params_blr *sizeof(double));
      calculate_con_from_model_semiseparable(post_model + num_params_blr *sizeof(double));
      calculate_con_rm(post_model);
      gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);

      transfun_2d_cal(post_model, Vline_data_ext, Trans2D_at_veldata,
                                      n_vel_data_ext, 0);
      calculate_line2d_from_blrmodel(post_model, Tline_data, Vline_data_ext, Trans2D_at_veldata,
                                      Fline2d_at_data, n_line_data, n_vel_data_ext);

      // calculate integrated line fluxes
      for(j = 0; j < n_line_data; j++)
      {
        Fline1d[j] = 0.0;
        for(k=0; k<n_vel_data; k++)
        {
          Fline1d[j] += Fline2d_at_data[j * n_vel_data_ext + (k+n_vel_data_incr)] * dV /line_scale;
        }
      }

      // calculate mean time lag
      sum1 = 0.0;
      sum2 = 0.0;
      for(j=0; j<parset.n_tau; j++)
      {
        for(k=0; k<n_vel_data_ext; k++)
        {
          sum1 += Trans2D_at_veldata[j * n_vel_data_ext + k] * TransTau[j];
          sum2 += Trans2D_at_veldata[j * n_vel_data_ext + k];
        }
      }
      // take care of zero transfer function
      if(sum2 > 0.0)
      {
        lag[i] = sum1/sum2;
        mean_lag += lag[i];
        nc++;
      }
      else
      {
        lag[i] = -DBL_MAX;
      }

      //if( i % (num_ps/10+1) == 0)
      {
        // output continuum
        for(j=0; j<parset.n_con_recon; j++)
        {
          fprintf(fcon, "%e %e\n", Tcon[j]*(1.0+parset.redshift), Fcon[j]/con_scale);
        }
        fprintf(fcon, "\n");

        // output 2d line
        for(j=0; j<n_line_data; j++)
        {
          for(k=0; k<n_vel_data; k++)
          {
            fprintf(fline, "%e ", Fline2d_at_data[j * n_vel_data_ext + (k+n_vel_data_incr)]/line_scale);
          }
          fprintf(fline, "\n");
        }
        fprintf(fline, "\n");

        // output transfer function
        for(j=0; j<parset.n_tau; j++)
        {
          fprintf(ftran, "%e ", TransTau[j]);

          for(k=0; k<n_vel_data; k++)
          {
            fprintf(ftran, "%e ", Trans2D_at_veldata[j * n_vel_data_ext + (k+n_vel_data_incr)]);
          }
          fprintf(ftran, "\n");
        }
        fprintf(ftran, "\n");

        // output 1d line
        for(j = 0; j<n_line_data; j++)
        {
          fprintf(fline1d, "%e %e\n", Tline_data[j]*(1.0+parset.redshift), Fline1d[j]);
        }
        fprintf(fline1d, "\n");
      }
    }

    smooth_end();

    fclose(fp);
    fclose(fcon);
    fclose(fline);
    fclose(ftran);
    fclose(fline1d);

    /* calculate mean lags */
    mean_lag /= (nc);
    mean_lag_std = 0.0;
    for(i=0; i<num_ps; i++)
    {
      if(lag[i] > -DBL_MAX)
        mean_lag_std += (lag[i] - mean_lag) * (lag[i] - mean_lag);
    }
    if(nc > 1)
      mean_lag_std = sqrt(mean_lag_std/(nc -1.0));
    else
      mean_lag_std = 0.0;
    printf("Mean time lag: %f+-%f\n", mean_lag, mean_lag_std);

    pm = (double *)best_model_line2d;
    pmstd = (double *)best_model_std_line2d;
    for(j=0; j<num_params; j++)
    {
      pm[j] = pmstd[j] = 0.0;
    }
    for(i=0; i<num_ps; i++)
    {
      for(j =0; j<num_params; j++)
        pm[j] += *((double *)posterior_sample + i*num_params + j );
    }

    for(j=0; j<num_params; j++)
      pm[j] /= num_ps;

    for(i=0; i<num_ps; i++)
    {
      for(j=0; j<num_params; j++)
        pmstd[j] += pow( *((double *)posterior_sample + i*num_params + j ) - pm[j], 2.0 );
    }

    for(j=0; j<num_params; j++)
    {
      if(num_ps > 1)
        pmstd[j] = sqrt(pmstd[j]/(num_ps-1.0));
      else
        pmstd[j] = 0.0;
    }

    for(j = 0; j<num_params_blr + num_params_var; j++)
      printf("Best params %d %f +- %f\n", j, *((double *)best_model_line2d + j),
                                             *((double *)best_model_std_line2d+j) );

    free(lag);
    free(post_model);
    free(posterior_sample);
    free(Fline1d);
  }
  return;
}

/*!
 * this function run dnest sampleing, reconstruct light curves using the best estimates for parameters.
 */
void reconstruct_line2d()
{
  int i, argc=0;
  char **argv;

  //configure restart of dnest
  argv = malloc(9*sizeof(char *));
  for(i=0; i<9; i++)
  {
    argv[i] = malloc(BRAINS_MAX_STR_LENGTH*sizeof(char));
  }
  //setup argc and argv
  strcpy(argv[argc++], "dnest");
  strcpy(argv[argc++], "-s");
  strcpy(argv[argc], parset.file_dir);
  strcat(argv[argc++], "/data/restart2d_dnest.txt");

  if(parset.flag_restart == 1)
  {
    strcpy(argv[argc++], "-r");
    strcpy(argv[argc], parset.file_dir);
    strcat(argv[argc], "/");
    strcat(argv[argc++], "data/restart2d_dnest.txt");
  }
  if(parset.flag_postprc == 1)
  {
    strcpy(argv[argc++], "-p");
  }
  if(parset.flag_temp == 1)
  {
    sprintf(argv[argc++], "-t%f", parset.temperature);
  }
  if(parset.flag_sample_info == 1)
  {
    strcpy(argv[argc++], "-c");
  }

  //level-dependent sampling
  {
    strcpy(argv[argc++], "-l");
  }

  reconstruct_line2d_init();

  smooth_init(n_vel_data_ext, Vline_data_ext);
  dnest_line2d(argc, argv);
  smooth_end();

  if(parset.flag_exam_prior != 1 && parset.flag_para_name != 1)
  {
    postprocess2d();

    // calculate light curves using the best model
    if(thistask == roottask)
    {
      force_update = 1;
      which_parameter_update = -1;
      which_particle_update = 0;
      Fcon_rm = Fcon_rm_particles[which_particle_update];
      TransTau = TransTau_particles[which_particle_update];
      Trans2D_at_veldata = Trans2D_at_veldata_particles[which_particle_update];
      Fline2d_at_data = Fline_at_data_particles[which_particle_update];

      //calculate_con_from_model(best_model_line2d + num_params_blr *sizeof(double));
      calculate_con_from_model_semiseparable(best_model_line2d + num_params_blr *sizeof(double));
      calculate_con_rm(best_model_line2d);
      gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);

      FILE *fp;
      char fname[200];
      int i, j;

      sprintf(fname, "%s/%s", parset.file_dir, parset.pcon_out_file);
      fp = fopen(fname, "w");
      if(fp == NULL)
      {
        fprintf(stderr, "# Error: Cannot open file %s\n", fname);
        exit(-1);
      }

      for(i=0; i<parset.n_con_recon; i++)
      {
        fprintf(fp, "%e %e %e\n", Tcon[i]*(1.0+parset.redshift), Fcon[i] / con_scale, Fcerrs[i] / con_scale);
      }
      fclose(fp);

      smooth_init(n_vel_data_ext, Vline_data_ext);
      // recovered line2d at data points
      transfun_2d_cal(best_model_line2d, Vline_data_ext, Trans2D_at_veldata,
                                              n_vel_data_ext, parset.flag_save_clouds);
      calculate_line2d_from_blrmodel(best_model_line2d, Tline_data, Vline_data_ext, Trans2D_at_veldata,
                                                       Fline2d_at_data, n_line_data, n_vel_data_ext);

      sprintf(fname, "%s/%s", parset.file_dir, parset.pline2d_data_out_file);
      fp = fopen(fname, "w");
      if(fp == NULL)
      {
        fprintf(stderr, "# Error: Cannot open file %s\n", fname);
        exit(-1);
      }

      fprintf(fp, "# %d %d\n", n_line_data, n_vel_data);
      for(i=0; i<n_line_data; i++)
      {
        fprintf(fp, "# %f\n", Tline_data[i]*(1.0+parset.redshift));
        for(j=0; j<n_vel_data; j++)
        {
          fprintf(fp, "%e %e\n", Wline_data[j],
                            Fline2d_at_data[i*n_vel_data_ext + (j+n_vel_data_incr)] / line_scale);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);

      sprintf(fname, "%s/%s", parset.file_dir, parset.tran2d_data_out_file);
      fp = fopen(fname, "w");
      if(fp == NULL)
      {
        fprintf(stderr, "# Error: Cannot open file %s\n", fname);
        exit(-1);
      }

      fprintf(fp, "# %d %d\n", parset.n_tau, n_vel_data);
      for(i=0; i<parset.n_tau; i++)
      {
        fprintf(fp, "# %f\n", TransTau[i]);
        for(j=0; j<n_vel_data; j++)
        {
          fprintf(fp, "%e %e\n", Vline_data[j], Trans2D_at_veldata[i*n_vel_data_ext + (j+n_vel_data_incr)]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);

      smooth_end();

      // recovered line2d at specified points
      smooth_init(parset.n_vel_recon, TransV);

      which_parameter_update = -1;
      which_particle_update = 0;
      transfun_2d_cal(best_model_line2d, TransV, Trans2D, parset.n_vel_recon, 0);

      /* there is no data for spectral broadening at given specified epoch, using the mean value
       * and set InstRes_err=0.0.
       */
      double *pm = (double *)best_model_line2d;
      if(parset.flag_InstRes > 0)
      {
        parset.flag_InstRes = 0; /* force to be uniform prior */
        double instres_mean = 0.0;
        for(i=0; i<n_line_data; i++)
        {
          instres_mean += instres_epoch[i];
        }
        parset.InstRes = instres_mean/n_line_data;
        parset.InstRes_err = 0.0;

        instres_mean = 0.0;
        for(i=0; i<n_line_data; i++)
        {
          instres_mean += pm[num_params_blr_model+num_params_nlr+i];
        }
        pm[num_params_blr_model + num_params_nlr ] = instres_mean/n_line_data;
      }

      /* similarly, there is no data for line center information at given specified epoch,
       * using the mean value
       */
      if(parset.flag_linecenter < 0)
      {
        parset.flag_linecenter = 1; /* force to be uniform prior, note num_params_linecenter is still unchanged */
        double linecenter_mean = 0.0;
        for(i=0; i<n_line_data; i++)
        {
          linecenter_mean += pm[idx_linecenter + i];
        }
        pm[idx_linecenter] = linecenter_mean/n_line_data;
      }

      calculate_line2d_from_blrmodel(best_model_line2d, Tline, TransV,
          Trans2D, Fline2d, parset.n_line_recon, parset.n_vel_recon);

      sprintf(fname, "%s/%s", parset.file_dir, parset.pline2d_out_file);
      fp = fopen(fname, "w");
      if(fp == NULL)
      {
        fprintf(stderr, "# Error: Cannot open file %s\n", fname);
        exit(-1);
      }

      fprintf(fp, "# %d %d\n", parset.n_line_recon, parset.n_vel_recon);
      for(i=0; i<parset.n_line_recon; i++)
      {
        fprintf(fp, "# %f\n", Tline[i]*(1.0+parset.redshift));
        for(j=0; j<parset.n_vel_recon; j++)
        {
          fprintf(fp, "%e %e\n", (1.0 + TransV[j]/C_Unit) * parset.linecenter * (1.0+parset.redshift),
                Fline2d[i*parset.n_vel_recon + j] / line_scale);
        }

        fprintf(fp, "\n");
      }
      fclose(fp);

      // output 2d transfer function
      sprintf(fname, "%s/%s", parset.file_dir, parset.tran2d_out_file);
      fp = fopen(fname, "w");
      if(fp == NULL)
      {
        fprintf(stderr, "# Error: Cannot open file %s\n", fname);
        exit(-1);
      }
      fprintf(fp, "# %d %d\n", parset.n_tau, parset.n_vel_recon);
      for(i=0; i<parset.n_tau; i++)
      {
        fprintf(fp, "# %f\n", TransTau[i]);
        for(j=0; j<parset.n_vel_recon; j++)
        {
          fprintf(fp, "%e %e\n", TransV[j]*VelUnit, Trans2D[i*parset.n_vel_recon + j]);
        }

        fprintf(fp, "\n");
      }
      fclose(fp);
      smooth_end();
    }
  }

  reconstruct_line2d_end();

  //clear up argv
  for(i=0; i<9; i++)
  {
    free(argv[i]);
  }
  free(argv);

  return;
}

/*!
 * this function initializes 2d reconstruction.
 */
void reconstruct_line2d_init()
{
  int i, j;
  double dT, Tspan;

  Tspan = Tcon_data[n_con_data -1] - Tcon_data[0];

  /* set time grid for continuum */
  /*if(parset.time_back > 0.0)
    Tcon_min = Tcon_data[0] - parset.time_back;
  else
    Tcon_min = Tcon_data[0] - fmax(0.05*Tspan, Tspan/2.0 + (Tcon_data[0] - Tline_data[0]));*/

  Tcon_min = Tcon_data[0] - time_back_set - 10.0;

  Tcon_max = Tcon_data[n_con_data-1] + fmax(0.05*Tspan, 20.0);
  Tcon_max = fmax(Tcon_max, Tline_data[n_line_data -1] + 10.0);  /* The time span should cover that of the emission line data */

  if(thistask == roottask)
    printf("Tcon_min_max: %f %f\n", Tcon_min - Tcon_data[0], Tcon_max - Tcon_data[n_con_data-1]);

  dT = (Tcon_max - Tcon_min)/(parset.n_con_recon -1);

  for(i=0; i<parset.n_con_recon; i++)
  {
    Tcon[i] = Tcon_min + i*dT;
  }

  /* set Larr_rec */
  for(i=0;i<parset.n_con_recon;i++)
  {
    Larr_rec[i*nq + 0]=1.0;
    for(j=1; j<nq; j++)
      Larr_rec[i*nq + j] = pow(Tcon[i], j);
  }

  //TransTau = malloc(parset.n_tau * sizeof(double));
  //Trans2D_at_veldata = malloc(parset.n_tau * n_vel_data * sizeof(double));

  TransV = malloc(parset.n_vel_recon * sizeof(double));
  Trans2D = malloc(parset.n_tau * parset.n_vel_recon * sizeof(double));

  //Fline2d_at_data = malloc(n_line_data * n_vel_data * sizeof(double));

  Tline = malloc(parset.n_line_recon * sizeof(double));
  Fline2d = malloc(parset.n_line_recon * parset.n_vel_recon * sizeof(double));

  Tline_min = Tline_data[0] - fmin(0.1*(Tline_data[n_line_data - 1] - Tline_data[0]), 10.0);
  if(parset.time_back <= 0.0)
    Tline_min = fmax(Tline_min, Tcon_min + time_back_set);

  Tline_max = Tline_data[n_line_data -1] + fmin(0.1*(Tline_data[n_line_data - 1] - Tline_data[0]), 10.0);
  Tline_max = fmin(Tline_max, Tcon_max - 1.0);  /* The time span should be smaller than that of the continuum */

  if(thistask == roottask)
    printf("Tline_min_max: %f %f\n", Tline_min - Tline_data[0], Tline_max - Tline_data[n_line_data -1]);

  dT = (Tline_max - Tline_min)/(parset.n_line_recon - 1);

  for(i=0; i<parset.n_line_recon; i++)
  {
    Tline[i] = Tline_min + i*dT;
  }

  double vel_max_set = Vline_data_ext[n_vel_data_ext -1], vel_min_set = Vline_data_ext[0];
  double dVel = (vel_max_set- vel_min_set)/(parset.n_vel_recon -1.0);

  for(i=0; i<parset.n_vel_recon; i++)
  {
  	TransV[i] = vel_min_set + dVel*i;
  }

  sprintf(dnest_options_file, "%s/%s", parset.file_dir, "src/OPTIONS2D");
  if(thistask == roottask)
  {
    get_num_particles(dnest_options_file);
  }
  MPI_Bcast(&parset.num_particles, 1, MPI_INT, roottask, MPI_COMM_WORLD);

  Fcon = malloc(parset.n_con_recon * sizeof(double *));
  Fcon_rm_particles = malloc(parset.num_particles * sizeof(double *));
  Fcon_rm_particles_perturb = malloc(parset.num_particles * sizeof(double *));
  for(i=0; i<parset.num_particles; i++)
  {
    Fcon_rm_particles[i] = malloc(parset.n_con_recon * sizeof(double));
    Fcon_rm_particles_perturb[i] = malloc(parset.n_con_recon * sizeof(double));
  }

  TransTau_particles = malloc(parset.num_particles * sizeof(double *));
  TransTau_particles_perturb = malloc(parset.num_particles * sizeof(double *));
  for(i=0; i<parset.num_particles; i++)
  {
    TransTau_particles[i] = malloc(parset.n_tau * sizeof(double));
    TransTau_particles_perturb[i] = malloc(parset.n_tau * sizeof(double));
  }

  Trans2D_at_veldata_particles = malloc(parset.num_particles * sizeof(double *));
  Trans2D_at_veldata_particles_perturb = malloc(parset.num_particles * sizeof(double *));
  for(i=0; i<parset.num_particles; i++)
  {
    Trans2D_at_veldata_particles[i] = malloc(parset.n_tau * n_vel_data_ext * sizeof(double));
    Trans2D_at_veldata_particles_perturb[i] = malloc(parset.n_tau * n_vel_data_ext * sizeof(double));
  }

  Fline_at_data_particles = malloc(parset.num_particles * sizeof(double *));
  Fline_at_data_particles_perturb = malloc(parset.num_particles * sizeof(double *));
  for(i=0; i<parset.num_particles; i++)
  {
    Fline_at_data_particles[i] = malloc(n_line_data * n_vel_data_ext * sizeof(double));
    Fline_at_data_particles_perturb[i] = malloc(n_line_data * n_vel_data_ext * sizeof(double));
  }

  clouds_tau = malloc(parset.n_cloud_per_task * sizeof(double));
  clouds_weight = malloc(parset.n_cloud_per_task * sizeof(double));
  clouds_vel = malloc(parset.n_cloud_per_task * parset.n_vel_per_cloud * sizeof(double));

  if(parset.flag_save_clouds && thistask == roottask)
  {
    if(parset.n_cloud_per_task <= 10000)
      icr_cloud_save = 1;
    else
      icr_cloud_save = parset.n_cloud_per_task/10000;

    char fname[200];
    sprintf(fname, "%s/%s", parset.file_dir, parset.cloud_out_file);
    fcloud_out = fopen(fname, "w");
    if(fcloud_out == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s\n", fname);
      exit(-1);
    }
  }
  return;
}

/*!
 * this function finalizes 2d reconstruction.
 */
void reconstruct_line2d_end()
{
  free(Tline);
  //free(Fline2d_at_data);
  free(Fline2d);

  //free(TransTau);
  free(TransV);
  free(Trans2D);
  //free(Trans2D_at_veldata);
  int i;
  for(i=0; i<parset.num_particles; i++)
  {
    free(Fcon_rm_particles[i]);
    free(Fcon_rm_particles_perturb[i]);
  }
  free(Fcon);
  free(Fcon_rm_particles);
  free(Fcon_rm_particles_perturb);

  free(Fline_at_data);

  free(par_fix);
  free(par_fix_val);

  free(best_model_line2d);
  free(best_model_std_line2d);

  for(i=0; i<parset.num_particles; i++)
  {
    free(Trans2D_at_veldata_particles[i]);
    free(Trans2D_at_veldata_particles_perturb[i]);
    free(TransTau_particles[i]);
    free(TransTau_particles_perturb[i]);
    free(Fline_at_data_particles[i]);
    free(Fline_at_data_particles_perturb[i]);
  }
  free(Trans2D_at_veldata_particles);
  free(Trans2D_at_veldata_particles_perturb);
  free(TransTau_particles);
  free(TransTau_particles_perturb);
  free(Fline_at_data_particles);
  free(Fline_at_data_particles_perturb);

  for(i=0; i<num_params; i++)
  {
    free(par_range_model[i]);
    free(par_prior_gaussian[i]);
  }
  free(par_range_model);
  free(par_prior_gaussian);
  free(par_prior_model);

  free(clouds_tau);
  free(clouds_weight);
  free(clouds_vel);

  if(parset.flag_save_clouds && thistask==roottask)
  {
    fclose(fcloud_out);
  }

  if(thistask == roottask)
  {
    printf("Ends reconstruct_line2d.\n");
  }
  return;
}

/*!
 * this function calculate probability at initial step.
 */
double prob_initial_line2d(const void *model)
{
  double prob_line = 0.0, var2, dy, var2_se;
  int i, j;
  double *pm = (double *)model;

  which_particle_update = dnest_get_which_particle_update();

  Fcon_rm = Fcon_rm_particles[which_particle_update];
  //calculate_con_from_model(model + num_params_blr*sizeof(double));
  calculate_con_from_model_semiseparable(model + num_params_blr*sizeof(double));
  calculate_con_rm(model);
  gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);

  TransTau = TransTau_particles[which_particle_update];
  Trans2D_at_veldata = Trans2D_at_veldata_particles[which_particle_update];
  Fline2d_at_data = Fline_at_data_particles[which_particle_update];
  which_parameter_update = -1;
  transfun_2d_cal(model, Vline_data_ext, Trans2D_at_veldata, n_vel_data_ext, 0);
  calculate_line2d_from_blrmodel(model, Tline_data, Vline_data_ext, Trans2D_at_veldata, Fline2d_at_data, n_line_data, n_vel_data_ext);

  var2_se = (exp(pm[num_params_blr-1])-1.0) * (exp(pm[num_params_blr-1])-1.0) * line_error_mean_sq;
  for(i=0; i<n_line_data; i++)
  {
    for(j=0; j<n_vel_data; j++)
    {
      //note mask with error < 0.0
      if(Flerrs2d_data[i] > 0.0)
      {
        dy = Fline2d_data[i*n_vel_data + j] - Fline2d_at_data[i * n_vel_data_ext + (j+n_vel_data_incr)];
        var2 = Flerrs2d_data[i*n_vel_data+j]*Flerrs2d_data[i*n_vel_data+j] + var2_se;
        prob_line += (-0.5 * (dy*dy)/var2) - 0.5*log(var2 * 2.0*PI);
      }
    }
  }

  return prob_line;
}

/*!
 * this function calculate probability at restart step.
 */
double prob_restart_line2d(const void *model)
{
  double prob_line = 0.0, var2, dy, var2_se;
  int i, j;
  double *pm = (double *)model;

  which_particle_update = dnest_get_which_particle_update();
  Fcon_rm = Fcon_rm_particles[which_particle_update];
  //calculate_con_from_model(model + num_params_blr*sizeof(double));
  calculate_con_from_model_semiseparable(model + num_params_blr*sizeof(double));
  calculate_con_rm(model);
  gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);

  TransTau = TransTau_particles[which_particle_update];
  Trans2D_at_veldata = Trans2D_at_veldata_particles[which_particle_update];
  Fline2d_at_data = Fline_at_data_particles[which_particle_update];
  which_parameter_update = num_params + 1; // so as not to update clouds.
  transfun_2d_cal(model, Vline_data_ext, Trans2D_at_veldata, n_vel_data_ext, 0);
  calculate_line2d_from_blrmodel(model, Tline_data, Vline_data_ext, Trans2D_at_veldata, Fline2d_at_data, n_line_data, n_vel_data_ext);

  var2_se = (exp(pm[num_params_blr-1])-1.0) * (exp(pm[num_params_blr-1])-1.0) * line_error_mean_sq;
  for(i=0; i<n_line_data; i++)
  {
    for(j=0; j<n_vel_data; j++)
    {
      //note mask with error < 0.0
      if(Flerrs2d_data[i] > 0.0)
      {
        dy = Fline2d_data[i*n_vel_data + j] - Fline2d_at_data[i * n_vel_data_ext + (j+n_vel_data_incr)];
        var2 = Flerrs2d_data[i*n_vel_data+j]*Flerrs2d_data[i*n_vel_data+j] + var2_se;
        prob_line += (-0.5 * (dy*dy)/var2) - 0.5*log(var2 * 2.0*PI);
      }
    }
  }

  return prob_line;
}
/*!
 * this function calculate probability.
 *
 * At each MCMC step, only one parameter is updated, which only changes some values; thus,
 * optimization that reuses the unchanged values can improve computation efficiency.
 */
double prob_line2d(const void *model)
{
  double prob_line = 0.0, var2, dy, var2_se;
  int i, j;
  double *pm = (double *)model;

  which_particle_update = dnest_get_which_particle_update();

  // only update continuum reconstruction when the corresponding parameters are updated
  if(which_parameter_update >= num_params_blr)
  {
    Fcon_rm = Fcon_rm_particles_perturb[which_particle_update];
    //calculate_con_from_model(model + num_params_blr*sizeof(double));
    calculate_con_from_model_semiseparable(model + num_params_blr*sizeof(double));
    calculate_con_rm(model);
    gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);
  }
  else /* continuum has no change, use the previous values */
  {
    Fcon_rm = Fcon_rm_particles[which_particle_update];
    gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);
  }

   // only update transfer function when BLR model is changed.
   // or when forced to updated
  if( (which_parameter_update < num_params_blr-1) || force_update == 1)
  {
    TransTau = TransTau_particles_perturb[which_particle_update];
    Trans2D_at_veldata = Trans2D_at_veldata_particles_perturb[which_particle_update];
    transfun_2d_cal(model, Vline_data_ext, Trans2D_at_veldata, n_vel_data_ext, 0);
  }
  else
  {
    TransTau = TransTau_particles[which_particle_update];
    Trans2D_at_veldata = Trans2D_at_veldata_particles[which_particle_update];
  }

  /* no need to calculate line when only systematic error parameter of line are updated.
   * otherwise, always need to calculate line.
   */
  if( which_parameter_update != num_params_blr-1 || force_update == 1 )
  {
    Fline2d_at_data = Fline_at_data_particles_perturb[which_particle_update];
    calculate_line2d_from_blrmodel(model, Tline_data, Vline_data_ext, Trans2D_at_veldata, Fline2d_at_data, n_line_data, n_vel_data_ext);

    var2_se = (exp(pm[num_params_blr-1])-1.0) * (exp(pm[num_params_blr-1])-1.0) * line_error_mean_sq;
    for(i=0; i<n_line_data; i++)
    {
      for(j=0; j<n_vel_data; j++)
      {
        //note mask with error < 0.0
        if(Flerrs2d_data[i] > 0.0)
        {
          dy = Fline2d_data[i*n_vel_data + j] - Fline2d_at_data[i * n_vel_data_ext + (j+n_vel_data_incr)];
          var2 = Flerrs2d_data[i*n_vel_data+j]*Flerrs2d_data[i*n_vel_data+j] + var2_se;
          prob_line += (-0.5 * (dy*dy)/var2) - 0.5*log(var2 * 2.0*PI);
        }
      }
    }
  }
  else
  {
    /* re-point */
    Fline2d_at_data = Fline_at_data_particles[which_particle_update];
    var2_se = (exp(pm[num_params_blr-1])-1.0) * (exp(pm[num_params_blr-1])-1.0) * line_error_mean_sq;
    for(i=0; i<n_line_data; i++)
    {
      for(j=0; j<n_vel_data; j++)
      {
        //note mask with error < 0.0
        if(Flerrs2d_data[i] > 0.0)
        {
          dy = Fline2d_data[i*n_vel_data + j] - Fline2d_at_data[i * n_vel_data_ext + (j+n_vel_data_incr)];
          var2 = Flerrs2d_data[i*n_vel_data+j]*Flerrs2d_data[i*n_vel_data+j] + var2_se;
          prob_line += (-0.5 * (dy*dy)/var2) - 0.5*log(var2 * 2.0*PI);
        }
      }
    }
  }

  return prob_line;
}