# -*- coding: utf-8 -*-
import os
import csv
import math
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate, optimize, stats
from shapely.geometry import LineString, MultiPoint, Point
from scipy.interpolate import interp1d
from scipy.stats import norm

def read_capacity_curves(input_file):
    # This function reads one, or a set of capacity curves

    with open(input_file, 'rU') as f:
        data = csv.reader(f)
        capacity_curves = read_SdSa_capacity_curves(data)

    return capacity_curves

def read_SdSa_capacity_curves(data):

    # This function reads Sd-Sa type of capacity curves
    periods = []
    heights = []
    gammas = []
    Sdy = []
    Say = []
    Sd = []
    Sa = []

    for line in data:
        if line[0] == 'Periods [s]':
            for value in line[1:]:
                if isNumber(value):
                    periods.append(float(value))
        if line[0] == 'Heights [m]':
            for value in line[1:]:
                if isNumber(value):
                    heights.append(float(value))
        if line[0] == 'Gamma participation factors':
            for value in line[1:]:
                if isNumber(value):
                    gammas.append(float(value))
        if line[0] == 'Sdy [m]':
            for value in line[1:]:
                if isNumber(value):
                    Sdy.append(float(value))
        if line[0] == 'Say [g]':
            for value in line[1:]:
                if isNumber(value):
                    Say.append(float(value))
        if line[0][0:2] == 'Sd' and isNumber(line[0][2]):
            subSd = []
            for value in line[1:]:
                if isNumber(value):
                    subSd.append(float(value))
            Sd.append(subSd)
        if line[0][0:2] == 'Sa' and isNumber(line[0][2]):
            subSa = []
            for value in line[1:]:
                if isNumber(value):
                    subSa.append(float(value))
            Sa.append(subSa)

    # Store all the data in the dictionary
    capacity_curves = {'type': None, 'periods': None, 'heights': None,
        'gamma': None, 'Sdy': None, 'Say': None, 'Sd': None, 'Sa': None}

    capacity_curves['type'] = 'Sd-Sa'
    capacity_curves['periods'] = periods
    capacity_curves['heights'] = heights
    capacity_curves['gamma'] = gammas
    capacity_curves['Sdy'] = Sdy
    capacity_curves['Say'] = Say
    capacity_curves['Sd'] = Sd
    capacity_curves['Sa'] = Sa

    return capacity_curves

def save_capacity_curves(capacity_curves, filename):

    if capacity_curves['type'] == 'Sd-Sa':
        save_SdSa_capacity_curves(capacity_curves, filename)

def save_SdSa_capacity_curves(capacity_curves, filename):

    no_capacity_curves = len(capacity_curves['Sd'])
    output = open(filename, 'w')
    output.write('Vb-droof,FALSE\n')
    output.write('Vb-dfloor,FALSE\n')
    output.write('Sd-Sa,TRUE\n')
    periods = 'Periods [s]'
    heights = 'Heights [m]'
    gammas = 'Gamma participation factors'
    Sdy = 'Sdy [m]'
    Say = 'Say [g]'
    for icc in range(no_capacity_curves):
        if capacity_curves['periods'] !=  None:
            periods = periods + ',' + str(capacity_curves['periods'][icc])
        if capacity_curves['heights'] !=  None:
            heights = heights + ',' + str(capacity_curves['heights'][icc])
        if capacity_curves['gamma'] !=  None:
            gammas = gammas + ',' + str(capacity_curves['gamma'][icc])
        if capacity_curves['Sdy'] !=  None:
            Sdy = Sdy + ',' + str(capacity_curves['Sdy'][icc])
        if capacity_curves['Say'] !=  None:
            Say = Say + ',' + str(capacity_curves['Say'][icc])

    output.write(periods + '\n')
    output.write(heights + '\n')
    output.write(gammas + '\n')
    output.write(Sdy + '\n')
    output.write(Say + '\n')

    for icc in range(no_capacity_curves):
        Sd = 'Sd'+str(icc+1)+' [m]'
        Sa = 'Sa'+str(icc+1)+' [g]'
        for ivalue in range(len(capacity_curves['Sd'][icc])):
            Sd = Sd + ',' + str(capacity_curves['Sd'][icc][ivalue])
            Sa = Sa + ',' + str(capacity_curves['Sa'][icc][ivalue])
        output.write(Sd + '\n')
        output.write(Sa + '\n')

    output.close()

def plot_capacity_curves(capacity_curves):

    #This function plots the capacity curves
    if capacity_curves['type'] == 'Sd-Sa':
        no_capacity_curves = len(capacity_curves['Sa'])
        for icc in range(no_capacity_curves):
            Sa = capacity_curves['Sa'][icc]
            Sd = capacity_curves['Sd'][icc]
            plt.plot(Sd, Sa, color = 'g', linewidth = 2)
        plt.plot(Sd, Sa, color = 'g', linewidth = 2,
                 label = 'individual capacity curve')
        plt.xlabel('Spectral displacement [m]', fontsize = 10)
        plt.ylabel('Spectral acceleration [g]', fontsize = 10)

    elif capacity_curves['type'] == 'Vb-dfloor' or capacity_curves['type'] == 'Vb-droof':
        no_capacity_curves = len(capacity_curves['Vb'])
        for icc in range(no_capacity_curves):
            Vb = capacity_curves['Vb'][icc]
            droof = capacity_curves['droof'][icc]
            plt.plot(droof, Vb, color = 'b', linewidth = 2)
        plt.plot(droof, Vb, color = 'b', linewidth = 2,
                 label = 'individual capacity curve')
        plt.xlabel('Roof displacement [m]', fontsize = 10)
        plt.ylabel('Base shear [kN]', fontsize = 10)

    plt.suptitle('Capacity curves')
    plt.legend(loc = 'lower right', frameon = False)
    plt.show()


def read_gmrs(folder):

    #This function reads a set of ground motion records
    #and stores them in a dictionary
    time = []
    acc = []
    dt = []
    no_points = []
    name = []
    for f in os.listdir(folder):
        if f.endswith(".csv"):
            itime, iacc = read_gmr(folder, f)
            time.append(itime)
            acc.append(iacc)
            dt.append(itime[1] - itime[0])
            no_points.append(len(iacc))
            name.append(f)

    gmrs = {'time': None, 'acc': None, 'dt': None,
        'no_points': None, 'name': None}
    gmrs['time'] = time
    gmrs['acc'] = acc
    gmrs['dt'] = dt
    gmrs['no_points'] = no_points
    gmrs['name'] = name

    return gmrs

def read_gmr(folder, gmr):

    time, acc = [], []
    with open(folder + '/' + gmr) as f:
        for line in f.readlines():
            line = line.split(',')
            time.append(float(line[0]))
            acc.append(float(line[1])*9.81)

    return time, acc
    
def evaluate_optimal_IM(gmrs,PDM,minT,maxT,stepT,damage_model,damping_ratio,method):
    
    T = np.arange(minT, maxT,stepT)
    setRsquare = []
    for it in range(len(T)):
        print(str((it+1)*100/len(T))+'%')
        fragility_model = calculate_mean_fragility(gmrs, PDM, T[it], damping_ratio, 'Sa', damage_model, method)
        setRsquare.append(fragility_model['Rsquare'])

    meanRsquare = np.mean(np.array(setRsquare),axis=1)        
    bestT = T[np.argmax(meanRsquare)]
    print('The best damage-intensity correlation was achieved for Sa at T='+str(bestT)+'s')
    plot_correlation(T,setRsquare,meanRsquare,damage_model,bestT)
    
def plot_correlation(T,setRsquare,meanRsquare,damage_model,bestT):
    
    setRsquare = np.array(setRsquare)
    color_scheme = ['g', 'b', 'y', 'orangered', 'r', 'k', 'm', 'c', '0.5', '0.75']
    for iDS in range(len(damage_model['damage_states'])):
        plt.plot(T,setRsquare[:,iDS],color=color_scheme[iDS],linewidth=2,label=damage_model['damage_states'][iDS])
    
    plt.plot(T,meanRsquare,color='k',linewidth=2,linestyle='dashed',label='Mean correlation')
    plt.xlabel('T (s)', fontsize = 12)
    plt.ylabel('Coefficient of correlation - R2', fontsize = 12)
    plt.legend(loc=4)
    plt.legend(frameon=False)
    plt.show()
    
def calculate_correlation(logmeans,logstddev,cumPDM,imls):
  
    Rsquare = []
    for iDS in range(len(logmeans)):
        sigma = logstddev[iDS]
        mu = logmeans[iDS]
        expected = stats.lognorm.cdf(imls, sigma,scale=math.exp(mu)),
        observed = cumPDM[iDS+1,:]
        R = np.corrcoef(observed,expected)
        Rsquare.append(R[0][1]**2)                        
    return Rsquare

def plot_response_spectra(gmrs, minT, maxT):

    no_gmrs = len(gmrs['time'])
    damping = 0.05
    T = np.linspace(minT, maxT, 50)
    plt.figure(figsize = (15, 5))
    for igmr in range(no_gmrs):
        acc = gmrs['acc'][igmr]
        time = gmrs['time'][igmr]
        spectrum = NigamJennings(time, acc, T, damping)
    #    spectrum = NewmarkBeta(time, acc, T, damping)

        plt.subplot(1, 3, 1)
        plt.plot(T, spectrum['Sa'], color = 'b', linewidth = 2)
        plt.subplot(1, 3, 2)
        plt.plot(T, spectrum['Sd'], color = 'g', linewidth = 2)
        plt.subplot(1, 3, 3)
        plt.plot(spectrum['Sd'], spectrum['Sa'], color = 'r', linewidth = 2)

    plt.subplot(1, 3, 1)
    plt.xlabel('Periods of vibration (sec)', fontsize = 10)
    plt.ylabel('Spectral acceleration (g)', fontsize = 10)
    plt.subplot(1, 3, 2)
    plt.xlabel('Periods of vibration (sec)', fontsize = 10)
    plt.ylabel('Spectral displacement (m)', fontsize = 10)
    plt.subplot(1, 3, 3)
    plt.xlabel('Spectral displacement (m)', fontsize = 10)
    plt.ylabel('Spectral acceleration (g)', fontsize = 10)
    plt.show()

def NewmarkBeta(time, acc, T, damping):

    u0 = 0
    v0 = 0
    dt = time[1]-time[0]
    no_acc = len(acc)
    no_T = len(T)
    M = 1
    Sd = np.zeros(no_T)
    Sa = np.zeros(no_T)
    u = np.zeros(no_acc)
    a = np.zeros(no_acc)
    v = np.zeros(no_acc)
    at = np.zeros(no_acc)

    for i in range(no_T):
        if T[i] == 0:
            Sd[i] = 0
            Sa[i] = max(abs(np.array(acc)))
        else:
            wn = 2*math.pi/T[i]
            C = damping*2*M*wn
            K = ((2*math.pi)/T[i])**2*M
            u[0] = u0
            v[0] = v0
            a[0] = -acc[0]-C*v[0]-K*u[0]
            at[0] = acc[0]+a[0]
            for j in range(no_acc-1):
                u[j+1] = u[j] + dt*v[j] + dt**2 / 2*a[j]
                a[j+1] = (1/(M+dt*0.5*C)) * (-M*acc[j+1] - K*u[j+1] - C*(v[j]+dt*0.5*a[j]))
                v[j+1] = v[j] + dt*(0.5*a[j] + 0.5*a[j+1])
                at[j+1] = acc[j+1] + a[j+1]

            Sd[i] = max(abs(u))
            Sa[i] = max(abs(at))/9.81

    spectrum = {'Sd': None, 'Sa': None}
    spectrum['Sd'] = Sd
    spectrum['Sa'] = Sa

    return spectrum

def isNumber(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

def read_damage_model(input_file):

    damage_states = []
    type_damage_state = []
    distribution = []
    mean = []
    cov = []
    median = []
    dispersion = []

    with open(input_file) as f:
        data = f.readlines()
        line = data[0]
        line = line.strip().split(',')
        type_criteria = line[1]

    with open(input_file, 'rU') as f:
        data = [row for row in csv.reader(f)]
        if (type_criteria == 'interstorey drift'):
            if data[2][1]== 'TRUE':
                path_to_deformed_shape = data[2][2]
                index = input_file.rfind('/')
                entire_path_to_deformed_shape = input_file[0:index+1]+path_to_deformed_shape
            else:
                entire_path_to_deformed_shape = 'none'

            damage_states = [row[0] for row in data[3:]]
            distribution = [row[1] for row in data[3:]]
            no_capacity = (len(data[3])-2)/2
            for icc in range(0, no_capacity):
                submedian = []
                subdispersion = []
                for iline in data[3:]:
                    submedian.append(float(iline[icc*2+2]))
                    subdispersion.append(float(iline[icc*2+3]))
                median.append(submedian)
                dispersion.append(subdispersion)

            damage_model = {'type_criteria': None, 'damage_states': None,
                 'median': None, 'dispersion': None}
            damage_model['type_criteria'] = type_criteria
            damage_model['damage_states'] = damage_states
            damage_model['distribution'] = distribution
            damage_model['median'] = median
            damage_model['dispersion'] = dispersion
            damage_model['deformed shape'] = entire_path_to_deformed_shape

        else:
            for iline in range(len(data)-2):
                line = data[iline+2]
                damage_states.append(line[0])
                if type_criteria == 'capacity curve dependent':
                    print(line)
                    type_damage_state.append(line[1])
                    distribution.append(line[2])
                    if isNumber(line[3]):
                        mean.append(float(line[3]))
                    else:
                        mean.append(line[4])
                    cov.append(float(line[4]))

                if type_criteria == 'strain dependent':
                    type_damage_state.append(line[1])
                    distribution.append(line[2])
                    mean.append(extract_values_string(line[3]))
                    cov.append(extract_values_string(line[4]))

                if type_criteria == 'spectral displacement':
                    distribution.append(line[1])
                    no_capacity = (len(line)-2)/2
                    mean.append(float(line[2]))
                    cov.append(float(line[3]))

            damage_model = {'type_criteria': None, 'damage_states': None,
                'type_damage_state': None, 'mean': None, 'cov': None}
            damage_model['type_criteria'] = type_criteria
            damage_model['damage_states'] = damage_states
            damage_model['type_damage_state'] = type_damage_state
            damage_model['distribution'] = distribution
            damage_model['mean'] = mean
            damage_model['cov'] = cov

    return damage_model

def extract_values_string(string):

    string = string.split(' ')
    values = []
    if isNumber(string[0]):
        for value in string:
            values.append(float(value))

    return values

def define_limit_states(capacity_curves, icc, damage_model):

    no_damage_states = len(damage_model['damage_states'])
    limit_states = []
    assert(damage_model['type_criteria'] != 'strain dependent'), 'Strain dependent damage model cannot be used in this methodology'
    if damage_model['type_criteria'] == 'capacity curve dependent':
        Sd = capacity_curves['Sd'][icc]
        Sa = capacity_curves['Sa'][icc]
        assert(capacity_curves['Sdy'] != []), 'If you want to use capacity curve dependent damage model you should provide Sdy, Say in capacity curve input file'
        Sdy = capacity_curves['Sdy'][icc]
        Say = capacity_curves['Say'][icc]
        for ids in range(no_damage_states):
            type_damage_state = damage_model['type_damage_state'][ids]
            distribution = damage_model['distribution'][ids]
            mean = damage_model['mean'][ids]
            cov = damage_model['cov'][ids]
            limit_states.append(define_limit_state(Sd, Sa, Sdy, Say,
                                                   type_damage_state,
                                                   distribution, mean, cov))

    elif damage_model['type_criteria'] == 'spectral displacement':
        for ids in range(no_damage_states):
            mean = damage_model['mean'][ids]
            cov = damage_model['cov'][ids]
            limit_states.append(sample_value(damage_model['distribution'][ids], mean, cov,0, float('inf')))

    elif damage_model['type_criteria'] == 'interstorey drift':
        if len(damage_model['median'])==1:
            EDPlim = damage_model['median'][0]
            bUthd = damage_model['dispersion'][0]
        else:
            EDPlim = damage_model['median'][icc]
            bUthd = damage_model['dispersion'][icc]

        [ISDvec, Sdvec] = read_deformed_shape(damage_model, capacity_curves,icc)
        [ISDvec,indy] = np.unique(ISDvec,return_index=True);
        Sdvec=Sdvec[indy];
        duf = capacity_curves['Sd'][icc][-1]

        for ids in range(no_damage_states):
            if bUthd[ids]==0: EDPsample = EDPlim[ids]
            else: EDPsample = stats.lognorm.rvs(bUthd[ids], scale = EDPlim[ids])
            Sdlim = np.interp(EDPsample,ISDvec,Sdvec);
            if Sdlim>duf: Sdlim = duf
            limit_states.append(Sdlim)

    #print(limit_states)
    return limit_states

def define_limit_state(Sd, Sa, Sdy, Say, type_damage_state, distribution, mean, cov):
    #FIXME: calls to the function sample_value are missing arguments A and B
    if type_damage_state == 'Sdy':
        mean = Sdy
        limit_state = sample_value(distribution, mean, cov, 0, float('inf'))

    elif type_damage_state == 'Sdu':
        mean = max(Sd)
        limit_state = sample_value(distribution, mean, cov, 0, float('inf'))

    elif type_damage_state == 'fraction Sdy':
        mean = mean*Sdy
        limit_state = sample_value(distribution, mean, cov, 0, float('inf'))

    elif type_damage_state == 'fraction Sdu':
        #FIXME: Sdu is not defined
        mean = mean*Sdu
        limit_state = sample_value(distribution, mean, cov, 0, float('inf'))

    elif type_damage_state == 'mean Sdy Sdu':
        mean = (Sdy+max(Sd))/2
        limit_state = sample_value(distribution, mean, cov, 0, float('inf'))

    elif isNumber(type_damage_state[0]):
        values = type_damage_state.split()
        weight1 = float(values[0])
        weight2 = float(values[2])
        mean = (weight1*Sdy + weight2*max(Sd))/(weight1+weight2)
        limit_state = sample_value(distribution, mean, cov, 0, float('inf'))

    return limit_state

def sample_value(distribution, mean, cov, A, B):

    if cov == 0:
        result = mean
    else:
        result = float('-inf')

        while result <=  A or result > B:
            if distribution == 'normal':
                result = stats.norm.rvs(mean, mean*cov)
            elif distribution == 'lognormal':
                variance = (mean*cov)**2.0
                mu = math.log(mean ** 2.0 / math.sqrt(variance + mean ** 2.0) )
                sigma = math.sqrt(math.log((variance / mean ** 2.0) + 1.0))
                result = stats.lognorm.rvs(sigma, scale = math.exp(mu))
            elif distribution == 'gamma':
                beta = (mean*cov)**2/mean
                alpha = mean/beta
                result = stats.gamma.rvs(alpha, scale = beta)

    return result


def spread(array, no_steps):

    result = np.zeros((len(array)-1)*no_steps+1)

    for i in range(len(array)-1):
        result[i*no_steps] = array[i]
        step = (array[i+1] - array[i]) / no_steps
        for j in range(no_steps):
            result[i*no_steps + j + 1] = array[i] + (j+1)*step

    result[-1] = array[-1]

    return result

def allocate_damage(igmr, PDM, disp, limitStates):

    no_ls = len(limitStates)
    #print(PDM[igmr, :])
    PDM[igmr, 0] = PDM[igmr, 0]+1
    ds = 0
    for ils in range(no_ls):
        if disp > limitStates[no_ls - ils - 1]:
            ds = no_ls-ils
            PDM[igmr, ds] = PDM[igmr, ds]+1
            PDM[igmr, 0] = PDM[igmr, 0]-1
            break
            
    #print(disp)
    #print(PDM[igmr, :])
    return PDM, ds

def residuals(coeffs, y, x):
    # TODO: this function seems incomplete - x not used, IMLs not defined
    res = y - stats.lognorm.cdf(IMLs, coeffs[1], scale = math.exp(coeffs[0]))

    return res

def calculate_imls(gmrs, T, damping):

    Sa = []
    Sd = []
    pga = []
    HI = []

    for igmr in range(len(gmrs['time'])):
        time = gmrs['time'][igmr]
        acc = gmrs['acc'][igmr]
        try:
            spectrum = NigamJennings(time, acc, [T], damping)
            Sa.append(spectrum['Sa'][0])
            Sd.append(spectrum['Sd'][0])
            pga.append(max(abs(np.array(acc)))/9.81)
        except (ValueError):
            spectrum = NigamJennings(time, acc, T, damping)
            dT = T[1]- T[0]
            HI.append(sum(spectrum['PSv'])*dT)

    return Sa, Sd, pga, HI

def calculate_iml(igmr, gmrs, IMT, T, damping):

    time = gmrs['time'][igmr]
    acc = gmrs['acc'][igmr]
    
    try:
        spectrum = NigamJennings(time, acc, [T], damping)
    except (ValueError):
        spectrum = NigamJennings(time, acc, T, damping)

    if IMT == 'PGA':
        iml = max(abs(np.array(acc)))
    elif IMT == 'Sa':
        if len(spectrum['Sa'])== 1: iml = spectrum['Sa'][0]
        else: iml = spectrum['Sa']
    elif IMT == 'Sd':
        if len(spectrum['Sd'])== 1: iml = spectrum['Sd'][0]
        else: iml = spectrum['Sd']
    elif IMT == 'Sv':
        if len(spectrum['Sv'])== 1: iml = spectrum['Sv'][0]
        else: iml = spectrum['Sv']
    elif IMT == 'PSv':
        if len(spectrum['PSv'])== 1: iml = spectrum['PSv'][0]
        else: iml = spectrum['PSv']
    elif IMT == 'HI':
        # computing Housner Intensity
        dT = T[1]- T[0]
        iml = sum(spectrum['PSv'])*dT;

    return iml

def calculate_mean_fragility(gmrs, EDPs, T, damping, IMT, damage_model, method):

    imls = calculate_imls(gmrs, T, damping)

    if IMT == 'Sa':
        imls = imls[0]
        IMT = 'Sa('+str(T)+')'
    elif IMT == 'Sd':
        imls = imls[1]
        IMT = 'Sd('+str(T)+')'
    elif IMT == 'PGA':
        imls = imls[2]

    if method == 'cloud analysis':
        Sds = EDPs[1]
        #calculate log(SDs) versus log(IMLs)
        #regression analysis using censored
        #with 
        
    else:
        PDM = response[0]
        cumPDM = convert_damage_matrix_to_probability(PDM)

        logmeans, logstddev = [], []
        if method == 'least squares':
            for iDS in range(len(damage_model['damage_states'])):
                solution, _ = optimize.leastsq(residual_lognormal_dist,[0.1, 0.6],args=(imls, cumPDM[iDS+1]))
                logmeans.append(solution[0])
                logstddev.append(solution[1])

        elif method == 'max likelihood':
            # the imls and the PDM are sorted to be used in the maximum likelihood approach
            # the PDM should be a list of integers in this approach
            I = np.argsort(imls, axis=0)
            imls = np.array(imls)
            imls = imls[I]
            PDM_mle = []
            for iDS in range(len(damage_model['damage_states'])):
                temp = cumPDM[iDS+1][I]*no_assets
                temp2 = temp.astype(int)
                PDM_mle = temp2.tolist()
                solution = mle(PDM_mle,imls,no_assets,1)
                logmeans.append(solution[0])
                logstddev.append(solution[1])
            
        Rsquare = calculate_correlation(logmeans,logstddev,cumPDM,imls)


    fragility_model = {'damage_states': None, 'logmean': None,'logstddev': None, 'IMT': None}
    fragility_model['damage_states'] = damage_model['damage_states']
    fragility_model['logmean'] = logmeans
    fragility_model['logstddev'] = logstddev
    fragility_model['IMT'] = IMT
    fragility_model['Rsquare'] = Rsquare

    return fragility_model

def mle(num_collapse, iml, tot, g):
#This function gives maximum likelihood estimate of a lognormal function

    def func(x):
        p = [stats.lognorm.cdf(i, x[0], loc=0, scale=x[1]) for i in iml]
        return -np.sum(stats.binom.logpmf(num_collapse,tot,p))
    x0 = np.array([1, g])
    x = optimize.fmin(func, x0)
    sigma = x[0]
    mu = np.log(x[1])

    return [mu, sigma]

def save_mean_fragility(taxonomy, fragility_model, minIML, maxIML, output_type, output_path):

    damage_states = fragility_model['damage_states']
    logmeans = fragility_model['logmean']
    logstddev = fragility_model['logstddev']
    Rsquares = fragility_model['Rsquare']
    IMT = fragility_model['IMT']

    if output_type == 'csv':
        save_mean_fragility_csv(taxonomy, damage_states, logmeans, logstddev, Rsquares, minIML, maxIML, IMT, output_path)
    if output_type == 'nrml':
        save_mean_fragility_nrml(taxonomy, damage_states, logmeans, logstddev, minIML, maxIML, IMT, output_path)

def save_mean_fragility_csv(taxonomy, damage_states, logmeans, logstddev, Rsquares, minIML, maxIML, IMT, output_path):

    destination_file = os.path.join(output_path, taxonomy)
    with open(output_path+'/'+taxonomy+'_fra.csv', 'wb') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow([taxonomy, IMT, minIML, maxIML])
        writer.writerow(['Damage state', 'log mean', 'log stddev', 'mean', 'stddev', 'median', 'cov','Rsquare'])
        for iDS in range(len(damage_states)):
            mu = logmeans[iDS]
            sigma = logstddev[iDS]
            mean = math.exp(mu+sigma**2/2)
            stddev = math.sqrt(math.exp(2*mu+sigma**2)*(math.exp(sigma**2)-1))
            median = math.exp(mu)
            cov = abs(stddev/mean)
            Rsquare = Rsquares[iDS]
            writer.writerow([damage_states[iDS], mu, sigma, mean, stddev, median, cov, Rsquare])

def get_damage_states(csv_data):
    damage_states = []
    csv_data.next()
    csv_data.next()
    for line in csv_data:
        damage_states.append(line[0])
    return damage_states

def save_fragility_set_nrml(folder, destination_file):
    path = os.path.abspath(folder)
    destination_file = os.path.join(path, destination_file)
    nrml_file = open(destination_file, 'w')
    nrml_file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    nrml_file.write('<nrml xmlns="http://openquake.org/xmlns/nrml/0.5">\n\n')
    nrml_file.write('<fragilityModel id="fm" assetCategory="buildings" lossCategory="structural">\n\n')
    nrml_file.write('  <description>fragility model</description>\n')
    ds_str = '  <limitStates>'

    file_index = 0
    for f in os.listdir(folder):
        if f.endswith(".csv"):
            with open(os.path.join(path, f), 'rU') as fr:
                data = csv.reader(fr)
                if file_index == 0:
                    damage_states = get_damage_states(data)
                    fr.seek(0)
                    ds_str += ' '.join(damage_states)
                    nrml_file.write(ds_str + '</limitStates>\n\n')
                    file_index += 1
                taxonomy, IMT, minIML, maxIML = data.next()[0:4]
                data.next()

                if IMT == 'PGA' or IMT[:2] == 'Sa':
                    IMT_units = 'g'
                else:
                    IMT_units = 'm'

                nrml_file.write('  <fragilityFunction format="continuous" id="' + taxonomy + '">\n')
                nrml_file.write('    <imls imt="'+IMT+'" minIML="'+minIML+'" maxIML="'+maxIML+'"/>\n')

                for line in data:
                    ds, logmean, logstd, mean, stddev, median, cov = line[0:7]
                    nrml_file.write('    <params ls="'+ds+'" mean="'+mean+'" stddev="'+stddev+'"/>\n')
                nrml_file.write('  </fragilityFunction>\n\n')

    nrml_file.write('</fragilityModel>\n\n')
    nrml_file.write('</nrml>\n')
    nrml_file.close()

def save_mean_fragility_nrml(taxonomy, damage_states, logmeans, logstddev, minIML, maxIML, IMT, output_path):

    path = os.path.abspath(output_path)
    destination_file = os.path.join(path, taxonomy)
    nrml_file = open(destination_file+'_fra.xml', 'w')
    nrml_file.write('<?xml version = "1.0" encoding = "UTF-8"?>\n')
    nrml_file.write('<nrml xmlns = "http://openquake.org/xmlns/nrml/0.4">\n')
    nrml_file.write('<fragilityModel format = "continuous">\n')
    nrml_file.write('   <description>fragility model for '+ taxonomy +'</description>\n')
    ds_str = ' <limitStates>'
    for damage_state in damage_states:
        ds_str = ds_str + ' ' + damage_state
    nrml_file.write(ds_str + '</limitStates>\n')
    nrml_file.close()

    save_single_fragility_nrml(taxonomy, damage_states, logmeans, logstddev, minIML, maxIML, IMT)

    nrml_file = open(taxonomy+'_fra.xml', 'a')
    nrml_file.write('   </fragilityModel>\n')
    nrml_file.write('</nrml>\n')
    nrml_file.close()

def save_single_fragility_nrml(taxonomy, damage_states, logmeans, logstddev, minIML, maxIML, IMT):

    if IMT == 'PGA' or IMT[:2] == 'Sa':
        IMT_units = 'g'
    else:
        IMT_units = 'm'

    nrml_file = open(taxonomy+'_fra.xml', 'a')
    nrml_file.write('       <ffs type = "lognormal">\n')
    nrml_file.write('       <taxonomy>'+taxonomy+'</taxonomy>\n')
    nrml_file.write('       <IML IMT = "'+IMT+'" imlUnit = "'+IMT_units+'" minIML = "'+str(minIML)+'" maxIML = "'+str(maxIML)+'"/>\n')

    for iDS in range(len(damage_states)):
        mu = logmeans[iDS]
        sigma = logstddev[iDS]
        mean = math.exp(mu+sigma**2/2)
        stddev = math.sqrt(math.exp(2*mu+sigma**2)*(math.exp(sigma**2)-1))
        nrml_file.write('       <ffc ls = "'+damage_states[iDS]+'">\n')
        nrml_file.write('           <params mean = "'+str(mean)+'" stddev = "'+str(stddev)+'"/>\n')
        nrml_file.write('       </ffc>\n')
    nrml_file.write('       </ffs>\n')
    nrml_file.close()

def plot_fragility_model(fragility_model, minIML, maxIML):

    imls = np.linspace(minIML, maxIML, 100)
    color_scheme = ['g', 'b', 'y', 'orangered', 'r', 'k', 'm', 'c', '0.5', '0.75']
    for iDS in range(len(fragility_model['damage_states'])):
        mu = fragility_model['logmean'][iDS]
        sigma = fragility_model['logstddev'][iDS]
        if sigma > 0:
            plt.plot(imls, stats.lognorm.cdf(imls, sigma,
                                         scale=math.exp(mu)),
                                         color=color_scheme[iDS],
                                         linewidth=2,
                                         label= fragility_model['damage_states'][iDS])
        else: #sigma = 0
            PoE = []
            for ele in imls:
                if ele <=math.exp(mu): PoE.append(0)
                else: PoE.append(1)

            plt.plot(
                imls,PoE,color=color_scheme[iDS],
                linewidth=2,label= fragility_model['damage_states'][iDS])


        try:
            plt.plot(fragility_model['imls'],fragility_model['PoE points'][iDS+1],
                                    marker='o',color=color_scheme[iDS],linestyle='None')
        except:
            pass
        plt.xlabel('Sa(Tel) [g]', fontsize = 12)
        plt.ylabel('Probabilty of Exceedance', fontsize = 12)
        plt.legend(loc=4)
    plt.show()
    
    
def convert_damage_matrix_to_probability(PDM):
    
    no_assets = np.sum(PDM, axis=1, dtype=float)
    cumPDM = np.fliplr(np.cumsum(np.fliplr(PDM), axis=1))
    cumPDM = np.transpose(cumPDM)
    cumPDM = cumPDM/no_assets
    
    return cumPDM
        
def export_IMLs_PDM(gmrs,T,PDM,damping_ratio,damage_model,output_file):
    
    cumPDM = convert_damage_matrix_to_probability(PDM)
    Sa, Sd, PGA, HI = calculate_imls(gmrs, T, damping_ratio)
    header = []
    header.append('Sa('+str(T)+')')
    header.append('Sd('+str(T)+')')
    header.append('PGA')
    for iDS in range(len(damage_model['damage_states'])):
        header.append(damage_model['damage_states'][iDS])
            
    with open(output_file, 'wb') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(header)
        for irow in range(len(Sa)):
            row = []
            row.append(Sa[irow])
            row.append(Sd[irow])
            row.append(PGA[irow])
            for iDS in range(len(damage_model['damage_states'])):
                row.append(cumPDM[iDS+1][irow])
            writer.writerow(row)

def plot_fragility_scatter(fragility_model, minIML, maxIML, PDM, gmrs, IMT, T, damping_ratio):

    imls = np.linspace(minIML, maxIML, 100)
    Sa, Sd, PGA, HI = calculate_imls(gmrs, T, damping_ratio)
    cumPDM = convert_damage_matrix_to_probability(PDM)
    color_scheme = ['g', 'b', 'y', 'orangered', 'r', 'k', 'm', 'c', '0.5', '0.75']
    
    for iDS in range(len(fragility_model['damage_states'])):
        mu = fragility_model['logmean'][iDS]
        sigma = fragility_model['logstddev'][iDS]
        plt.scatter(Sa, cumPDM[iDS+1], s=20, c=color_scheme[iDS], alpha=0.5)
        if sigma > 0:
            plt.plot(imls, stats.lognorm.cdf(imls, sigma,
                                         scale=math.exp(mu)),
                                         color=color_scheme[iDS],
                                         linewidth=2,
                                         label= fragility_model['damage_states'][iDS])
        else: #sigma = 0
            PoE = []
            for ele in imls:
                if ele <=math.exp(mu): PoE.append(0)
                else: PoE.append(1)

            plt.plot(
                imls,PoE,color=color_scheme[iDS],
                linewidth=2,label= fragility_model['damage_states'][iDS])


        try:
            plt.plot(fragility_model['imls'],fragility_model['PoE points'][iDS+1],
                                    marker='o',color=color_scheme[iDS],linestyle='None')
        except:
            pass
        plt.xlabel('Sa(Tel) [g]', fontsize = 12)
        plt.ylabel('Probabilty of Exceedance', fontsize = 12)
        plt.legend(loc=4)
        plt.ylim((0,1))
        plt.xlim((minIML,maxIML))
    plt.show()
    
def save_result(result,output_file):
    
    np.savetxt(output_file,result,delimiter=',')
    
def import_result(input_file):
    
    result = np.genfromtxt(input_file,delimiter=',')
    
    return result 


def NigamJennings(time, acc, periods, damping):

    add_PGA = False
    if periods[0] == 0:
        periods = np.delete(periods, 0)
        add_PGA = True

    dt = time[1]-time[0]
    num_steps = len(acc)
    num_per = len(periods)
    vel, disp = calculate_velocity_displacement(time, acc)
    omega = (2. * np.pi) / np.array(periods)
    omega2 = omega ** 2.
    omega3 = omega ** 3.
    omega_d = omega * math.sqrt(1.0 - (damping ** 2.))
    const = {'f1': (2.0 * damping) / (omega3 * dt),
            'f2': 1.0 / omega2,
            'f3': damping * omega,
            'f4': 1.0 / omega_d}
    const['f5'] = const['f3'] * const['f4']
    const['f6'] = 2.0 * const['f3']
    const['e'] = np.exp(-const['f3'] * dt)
    const['s'] = np.sin(omega_d * dt)
    const['c'] = np.cos(omega_d * dt)
    const['g1'] = const['e'] * const['s']
    const['g2'] = const['e'] * const['c']
    const['h1'] = (omega_d * const['g2']) - (const['f3'] * const['g1'])
    const['h2'] = (omega_d * const['g1']) + (const['f3'] * const['g2'])
    x_a, x_v, x_d = calculate_time_series(num_steps, num_per, time, acc, const, omega2)

    spectrum = {'Sa': None, 'Sv': None, 'Sd': None, 'T': None}
    spectrum['Sa'] = np.max(np.fabs(x_a)/9.81, axis = 0)
    spectrum['Sv'] = np.max(np.fabs(x_v), axis = 0)
    spectrum['Sd'] = np.max(np.fabs(x_d), axis = 0)
    spectrum['PSv'] = spectrum['Sd']*omega
    spectrum['T'] = periods

    if add_PGA:
        spectrum = add_PGA_spectrum(spectrum, acc)

    return spectrum

def add_PGA_spectrum(spectrum, acc):

    spectrum['Sa'] = np.append(np.max(np.fabs(acc)/9.81), spectrum['Sa'])
    spectrum['Sv'] = np.append(0, spectrum['Sv'])
    spectrum['Sd'] = np.append(0, spectrum['Sd'])
    spectrum['T'] = np.append(0, spectrum['T'])

    return spectrum

def calculate_time_series(num_steps, num_per, time, acc, const, omega2):
    """
    Calculates the acceleration, velocity and displacement time series for
    the SDOF oscillator
    :param dict const:
        Constants of the algorithm
    :param np.ndarray omega2:
        Square of the oscillator period
    :returns:
        x_a = Acceleration time series
        x_v = Velocity time series
        x_d = Displacement time series
    """
    dt = time[1]-time[0]
    x_d = np.zeros([num_steps - 1, num_per], dtype = float)
    x_v = np.zeros_like(x_d)
    x_a = np.zeros_like(x_d)

    for k in range(0, num_steps - 1):
        yval = k - 1
        dug = acc[k + 1] - acc[k]
        z_1 = const['f2'] * dug
        z_2 = const['f2'] * acc[k]
        z_3 = const['f1'] * dug
        z_4 = z_1 / dt
        if k == 0:
            b_val = z_2 - z_3
            a_val = (const['f5'] * b_val) + (const['f4'] * z_4)
        else:
            b_val = x_d[k - 1, :] + z_2 - z_3
            a_val = (const['f4'] * x_v[k - 1, :]) +\
                (const['f5'] * b_val) + (const['f4'] * z_4)

        x_d[k, :] = (a_val * const['g1']) + (b_val * const['g2']) +\
            z_3 - z_2 - z_1
        x_v[k, :] = (a_val * const['h1']) - (b_val * const['h2']) - z_4
        x_a[k, :] = (-const['f6'] * x_v[k, :]) - (omega2 * x_d[k, :])

    return x_a, x_v, x_d

def calculate_velocity_displacement(time, acc):
    '''
    Returns the velocity and displacment time series using simple integration
    :param float time_step:
        Time-series time-step (s)
    :param np.ndarray acceleration:
        Acceleration time-history
    :returns:
        velocity - Velocity Time series (cm/s)
        displacement - Displacement Time series (cm)
    '''
    dt = time[1]-time[0]
    velocity = dt * integrate.cumtrapz(acc, initial = 0.)
    displacement = dt * integrate.cumtrapz(velocity, initial = 0.)
    return velocity, displacement

def residual_lognormal_dist(coeffs, imls, fractions):

    mu = coeffs[0]
    sigma = coeffs[1]
    residual = abs(np.array(fractions) - stats.lognorm.cdf(imls, sigma, scale = math.exp(mu)))

    return residual


def calculate_fragility_statistics(gmrs, PDM, T, damping, IMT, damage_model,
                                   method, no_samples, size_sample):

    imls = calculate_imls(gmrs, T, damping)

    if IMT == 'Sa':
        imls = imls[0]
        IMT = 'Sa('+str(T)+')'
    elif IMT == 'Sd':
        imls = imls[1]
        IMT = 'Sd('+str(T)+')'
    elif IMT == 'PGA':
        imls = imls[2]

    no_gmrs = len(imls)
    no_DS = len(damage_model['damage_states'])
    cumPDM = convert_damage_matrix_to_probability(PDM)

    statistics = []
    for isample in range(no_samples):
        sample_indices = np.random.random_integers(0, no_gmrs-1, size_sample)
        sample_imls = imls[sample_indices]
        sample_cumPDM = cumPDM[:, sample_indices]
        fragility_model = []
        if method == 'least squares':
            for iDS in range(no_DS):
                solution, _ = optimize.leastsq(residual_lognormal_dist,
                                               [0.1, 0.6],
                                               args=(sample_imls, sample_cumPDM[iDS+1]))
                fragility_model.append(solution)
        elif method == 'max likelihood':
            I = np.argsort(sample_imls, axis=0)
            imls = np.array(sample_imls)
            imls = sample_imls[I]
            sample_PDM_mle = []
            for iDS in range(no_DS):
                temp = sample_cumPDM[iDS+1][I]*no_assets
                temp2 = temp.astype(int)
                sample_PDM_mle = temp2.tolist()
                solution = mle(sample_PDM_mle, sample_imls, no_assets, 1)
                fragility_model.append(solution)
        statistics.append(fragility_model)

    statistics = np.array(statistics)
    means = np.mean(statistics, axis=0)
    stddev = np.std(statistics, axis=0)

    fragility_stats = {'damage_states': None, 'mean': None, 'stddev': None,
        'correlation': None, 'IMT': None}
    fragility_stats['damage_states'] = damage_model['damage_states']
    fragility_stats['mean'] = np.reshape(means, (no_DS, 2))
    fragility_stats['stddev'] = np.reshape(stddev, (no_DS, 2))
    fragility_stats['correlation'] = np.corrcoef(np.transpose(statistics))
    fragility_stats['statistics'] = statistics
    fragility_stats['IMT'] = IMT

    return fragility_stats

def plot_fragility_stats(fragility_stats, minIML, maxIML):

    imls = np.linspace(minIML, maxIML, 100)
    color_scheme = ['g', 'b', 'y', 'orangered', 'r', 'k', 'm', 'c', '0.5', '0.75']
    for iDS in range(len(fragility_stats['damage_states'])):
        mu = fragility_stats['mean'][iDS][0]
        sigma = fragility_stats['mean'][iDS][1]
        plt.plot(imls, stats.lognorm.cdf(imls, sigma, scale = math.exp(mu)),
                 color = color_scheme[iDS],  linewidth = 2,
                 label = fragility_stats['damage_states'][iDS])
        plt.legend(loc = 4)
    plt.show()

def read_consequence_model(input_file):

    damage_states, distribution, mean, cov, A, B = [], [], [], [], [], []
    with open(input_file, 'rU') as f:
        data = csv.reader(f)
        for iline, row in enumerate(data):
            if iline > 0:
                damage_states.append(row[0])
                distribution.append(row[1])
                mean.append(float(row[2]))
                cov.append(float(row[3]))
                A.append(float(row[4]))
                B.append(float(row[5]))

    cons_model = {'damage_states': None, 'distribution': None, 'mean': None,
        'cov': None, 'A': None, 'B': None}
    cons_model['damage_states'] = damage_states
    cons_model['distribution'] = distribution
    cons_model['mean'] = mean
    cons_model['cov'] = cov
    cons_model['A'] = A
    cons_model['B'] = B

    return cons_model

def convert_fragility_vulnerability(fragility_model, cons_model, imls, dist_type):

    vulnerability_model = []
    check_damage_states(fragility_model, cons_model)
    no_samples = 100
    loss_ratios = []
    for isample in range(no_samples):
        loss_ratios.append(sample_loss_ratios(fragility_model, cons_model, imls))
    if dist_type == 'lognormal' or dist_type == 'beta':
        vulnerability_model = create_parametric_vul_model(imls, loss_ratios, dist_type, fragility_model['IMT'])
    elif dist_type == 'PMF':
        vulnerability_model = create_nonparametric_vul_model(imls, loss_ratios, dist_type, fragility_model['IMT'])

    return vulnerability_model

def create_parametric_vul_model(imls, loss_ratios, dist_type, IMT):

    mean = np.mean(np.array(loss_ratios), axis=0)
    stddev = np.std(np.array(loss_ratios), axis=0)
    cov = np.zeros(len(mean))
    for icov in range(len(cov)):
        if mean[icov] > 0:
            if stddev[icov]/mean[icov] > 10**-4:
                cov[icov] = stddev[icov]/mean[icov]

    vulnerability_model = {'imls': None, 'distribution': None,
        'mean': None, 'cov': None, 'IMT': None}
    vulnerability_model['imls'] = imls
    vulnerability_model['distribution'] = dist_type
    vulnerability_model['mean'] = mean
    vulnerability_model['cov'] = cov
    vulnerability_model['IMT'] = IMT

    return vulnerability_model

def create_nonparametric_vul_model(imls, sampled_loss_ratios, dist_type, IMT):

    no_values = 20
    no_samples = len(sampled_loss_ratios)
    loss_ratios = np.linspace(0.0, 1.0, no_values)
    probs = np.zeros((no_values, len(imls)))

    for isample in range(no_samples):
        for iiml in range(len(imls)):
            if sampled_loss_ratios[isample][iiml] == 0:
                probs[0][iiml] = probs[0][iiml] + 1
            elif sampled_loss_ratios[isample][iiml] == 1:
                probs[-1][iiml] = probs[-1][iiml] + 1
            else:
                idx = (np.abs(loss_ratios[1:-1]-sampled_loss_ratios[isample][iiml])).argmin()
                probs[idx+1][iiml] =  probs[idx+1][iiml] + 1

    probs = probs/no_samples
    vulnerability_model = {'imls': None, 'distribution': None,
        'loss_ratios': None, 'probabilities': None, 'IMT': None}
    vulnerability_model['imls'] = imls
    vulnerability_model['distribution'] = dist_type
    vulnerability_model['loss_ratios'] = loss_ratios
    vulnerability_model['probabilities'] = probs
    vulnerability_model['IMT'] = IMT

    return vulnerability_model

def plot_vulnerability_model(vulnerability_model):
    imls = vulnerability_model['imls']
    imt = vulnerability_model['IMT']
    if vulnerability_model['distribution'] == 'lognormal':
        mean_lrs = vulnerability_model['mean']
        cov_lrs = vulnerability_model['cov']
        plt.errorbar(imls, mean_lrs, yerr=cov_lrs, color='0.4', fmt='o')
    elif vulnerability_model['distribution'] == 'beta':
        mean_lrs = vulnerability_model['mean']
        cov_lrs = vulnerability_model['cov']
        plt.errorbar(imls, mean_lrs, yerr=cov_lrs, color='0.4', fmt='o')
    elif vulnerability_model['distribution'] == 'PMF':
        lrs = vulnerability_model['loss_ratios']
        probs = vulnerability_model['probabilities']
        mean_lrs = np.dot(lrs, probs)
        imls.insert(0, 0)
        mean_lrs = np.insert(mean_lrs, 0, 0)
    plt.plot(imls, mean_lrs, linewidth=2)
    plt.plot(imls, mean_lrs, 'ro')
    plt.ylim(0.0, 1.0)
    plt.xlabel(imt, fontsize=12)
    plt.ylabel('Mean Loss Ratio', fontsize=12)
    plt.suptitle('Vulnerability function', fontsize=16)
    plt.show()

def check_damage_states(model1, model2):

    for iDS in range(len(model1['damage_states'])):
        assert(model1['damage_states'][iDS] ==  model2['damage_states'][iDS]), 'The fragility model and the consequence model are not compatible'

    return

def sample_loss_ratios(fragility_model, cons_model, imls):

    no_damage_states = len(fragility_model['damage_states'])
    loss_ratios = np.zeros(len(imls))

    for iDS in range(no_damage_states):
        distribution = cons_model['distribution'][iDS]
        mean = cons_model['mean'][iDS]
        cov = cons_model['cov'][iDS]
        A = cons_model['A'][iDS]
        B = cons_model['B'][iDS]
        damage_ratio = sample_value(distribution, mean, cov, A, B)
        mu = fragility_model['logmean'][iDS]
        sigma = fragility_model['logstddev'][iDS]

        if iDS == no_damage_states-1:
            fraction = stats.lognorm.cdf(imls, sigma, scale = math.exp(mu))
        else:
            mu2 = fragility_model['logmean'][iDS + 1]
            sigma2 = fragility_model['logstddev'][iDS + 1]
            fraction = stats.lognorm.cdf(imls, sigma, scale = math.exp(mu))-stats.lognorm.cdf(imls, sigma2, scale = math.exp(mu2))
        loss_ratios = loss_ratios + fraction*damage_ratio

    return loss_ratios

def save_vulnerability(taxonomy, vulnerability_model, output_type, output_path):

    imls = vulnerability_model['imls']
    IMT = vulnerability_model['IMT']
    distribution = vulnerability_model['distribution']
    if distribution == 'lognormal' or distribution == 'beta':
        values1 = vulnerability_model['mean']
        values2 = vulnerability_model['cov']
    elif distribution == 'PMF':
        values1 = vulnerability_model['loss_ratios']
        values2 = vulnerability_model['probabilities']

    if output_type == 'csv':
        save_vulnerability_csv(taxonomy, imls, values1, values2, distribution, IMT, output_path)
    if output_type == 'nrml':
        save_vulnerability_nrml(taxonomy, imls, values1, values2, distribution, IMT, output_path)

def save_vulnerability_csv(taxonomy, imls, values1, values2, distribution, IMT, output_path):

    path = os.path.abspath(output_path)
    destination_file = os.path.join(path, taxonomy)
    with open(destination_file+'_vul.csv', 'wb') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow([taxonomy, IMT, distribution])
        if distribution == 'lognormal' or distribution == 'beta':
            writer.writerow(np.append('imls', imls))
            writer.writerow(np.append('mean', values1))
            writer.writerow(np.append('cov', values2))

        elif distribution == 'PMF':
            writer.writerow(['loss ratio', 'probabilities'])
            for iLR in range(len(values1)):
                writer.writerow(np.append(values1[iLR], values2[iLR, :]))


def save_vulnerability_nrml(taxonomy, imls, values1, values2, distribution, IMT, output_path):

    path = os.path.abspath(output_path)
    destination_file = os.path.join(path, taxonomy)
    nrml_file = open(destination_file+'_vul.xml', 'w')
    nrml_file.write('<?xml version = "1.0" encoding = "utf-8"?>\n')
    nrml_file.write('<nrml xmlns = "http://openquake.org/xmlns/nrml/0.5">\n')
    nrml_file.write('<vulnerabilityModel id = "'+taxonomy+'" assetCategory = "buildings" lossCategory = "economic">\n')
    nrml_file.write('   <description>vulnerability model for '+ taxonomy +'</description>\n')
    nrml_file.close()

    save_single_vulnerability_nrml(taxonomy, imls, values1, values2, distribution, IMT)

    nrml_file = open(taxonomy+'_vul.xml', 'a')
    nrml_file.write('</vulnerabilityModel>\n')
    nrml_file.write('</nrml>\n')
    nrml_file.close()

def save_single_vulnerability_nrml(taxonomy, imls, values1, values2, distribution, IMT):

    imls_str = convert_array_to_string(imls)
    nrml_file = open(taxonomy+'_vul.xml', 'a')

    if distribution == 'lognormal':
        dist = 'LN'
    elif distribution == 'beta':
        dist = 'BT'
    elif distribution == 'PMF':
        dist = 'PM'

    nrml_file.write('       <vulnerabilityFunction id = "'
                    + taxonomy + '" dist = "' + dist + '">\n')

    if distribution == 'lognormal' or distribution == 'beta':
        lrs_str = convert_array_to_string(values1)
        cov_str = convert_array_to_string(values2)
        nrml_file.write('       <imls imt = "'+IMT+'">'+imls_str+'</imls>\n')
        nrml_file.write('       <meanLRs>'+lrs_str+'</meanLRs>\n')
        nrml_file.write('       <covLRs>'+cov_str+'</covLRs>\n')

    elif distribution == 'PMF':
        for iLR in range(len(values1)):
            probs = convert_array_to_string(values2[iLR])
            nrml_file.write('       <probabilities lr = "'
                            + str(values1[iLR]) + '">' + probs
                            + '</probabilities>)\n')

    nrml_file.write('       </vulnerabilityFunction>\n')

def save_vulnerability_set_nrml(folder, destination_file):
    path = os.path.abspath(folder)
    destination_file = os.path.join(path, destination_file)
    nrml_file = open(destination_file, 'w')
    nrml_file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    nrml_file.write('<nrml xmlns="http://openquake.org/xmlns/nrml/0.5">\n\n')
    nrml_file.write('<vulnerabilityModel assetCategory="buildings" lossCategory="economic">\n')
    nrml_file.write('  <description>vulnerability model</description>\n\n')

    for f in os.listdir(folder):
        if f.endswith(".csv"):
            with open(os.path.join(path, f), 'rU') as fr:
                data = csv.reader(fr)
                taxonomy, IMT, distribution = data.next()[0:3]
                imls = data.next()
                imls.remove("imls")
                imls_str = " ".join(imls)
                if distribution == 'lognormal':
                    dist = 'LN'
                elif distribution == 'beta':
                    dist = 'BT'
                elif distribution == 'PMF':
                    dist = 'PM'

                nrml_file.write('  <vulnerabilityFunction id="'
                                + taxonomy + '" dist="' + dist + '">\n')
                nrml_file.write('    <imls imt="'+IMT+'">'+imls_str+'</imls>\n')

                if distribution == 'lognormal' or distribution == 'beta':
                    lrs = data.next()
                    lrs.remove("mean")
                    lrs_str = " ".join(lrs)
                    covs = data.next()
                    covs.remove("cov")
                    cov_str = " ".join(covs)
                    nrml_file.write('    <meanLRs>'+lrs_str+'</meanLRs>\n')
                    nrml_file.write('    <covLRs>'+cov_str+'</covLRs>\n')
                elif distribution == 'PMF':
                    data.next()
                    for line in data:
                        lr = line.pop(0)
                        probs = " ".join(line)
                        nrml_file.write('    <probabilities lr="' + lr + '">' + probs + '</probabilities>\n')

                nrml_file.write('  </vulnerabilityFunction>\n\n')

    nrml_file.write('</vulnerabilityModel>\n\n')
    nrml_file.write('</nrml>\n')
    nrml_file.close()

def convert_array_to_string(array):
    string = ''
    for i in range(len(array)):
        string = string + ' ' + str(array[i])

    return string

def read_hazard(input_file):
#This function reads a hazard curve and stores it in a dictionary

    file = open(input_file)
    data = csv.reader(file)
    IMLs = []
    prob_exceedance = []
    IM_type = []
    for line in data:
        if line[0] == 'PGA':
            IM_type.append(line[0])
            for value in line[1:]:
                if isNumber(value):
                    IMLs.append(float(value))
        if line[0] == 'Sa':
            IM_type.append(line[0])
            for value in line[1:]:
                if isNumber(value):
                    IMLs.append(float(value))
        if line[0] == 'PoE':
            for value in line[1:]:
                if isNumber(value):
                    prob_exceedance.append(float(value))

    #Store all the data in the dictionary
    hazard_curve = {'IMLs':None,'PoE':None,'IM_Type':None}
    hazard_curve['IMLs'] = IMLs
    hazard_curve['PoE'] = prob_exceedance
    hazard_curve['IM_Type'] = IM_type

    return hazard_curve

def plot_hazard_curve(hazard_curve):
#This function plots the hazard curve

    IMLs = hazard_curve['IMLs']
    PoE = hazard_curve['PoE']
    plt.plot(IMLs,PoE,color='g',linewidth=2)
    plt.xlabel(hazard_curve['IM_Type'][0] + ' [g]',fontsize = 10)
    plt.ylabel('Probability of Exceedance',fontsize = 10)

def read_frag_model(input_file):
#This function reads a fragility model and stores it in a dictionary

    damage_states = []
    model_type = []
    cent_value = []
    dispersion = []
    log_mean = []
    log_stdev = []

    file = open(input_file)
    data = file.readlines()
    line = data[0]
    line = line.strip().split(',')
    model_type = line[1] + ' - ' + line[2]

    file = open(input_file)
    data = csv.reader(file)
    data = [row for row in data]
    for iline in range(len(data)-1):
        line = data[iline+1]
        damage_states.append(line[0])
        cent_value.append(line[1])
        dispersion.append(line[2])

    if model_type == ' median -  dispersion':
        for ids in range(len(cent_value)):
            mean = math.log(cent.value[ids])
            stdev = dispersion[ids]
            log_mean.append(mean)
            log_stdev.append(stdev)
    elif model_type == 'mean of x - cov of x':
        for ids in range(len(cent_value)):
            mu = cent.value[ids]
            cov = dispersion[ids]
            median = (mu**2)/math.sqrt(mu**2+cov**2)
            mean = math.log(median)
            stdev = math.sqrt(math.log(1+(cov**2)/mu**2))
            log_mean.append(mean)
            log_stdev.append(stdev)
    elif model_type == 'mean of ln(x) - st. dev. of ln(x)':
        for ids in range(len(cent_value)):
            mean = cent_value[ids]
            stdev = line[ids]
            log_mean.append(mean)
            log_stdev.append(stdev)

    #Store all the data in the dictionary
    fragility_model =  {'Damage_states':None,'log_mean':None,'log_stdev':None}
    fragility_model['Damage_states'] = damage_states
    fragility_model['logmean'] = log_mean
    fragility_model['logstdev'] = log_stdev

    return fragility_model
