def runTest7():

    L = 4e-4  # length of the system in the x-direction [m]
    dd = .05e-4
    Ly = 2e-4

    # Mesh
    x = np.concatenate(
        (np.linspace(0, 1e-4 - dd, 70, endpoint=False),
         np.linspace(1e-4 - dd, 1e-4 + dd, 20, endpoint=False),
         np.linspace(1e-4 + dd, 3e-4 - dd, 140, endpoint=False),
         np.linspace(3e-4 - dd, 3e-4 + dd, 20,
                     endpoint=False), np.linspace(3e-4 + dd, L, 70)))
    y = np.concatenate(
        (np.linspace(0, 1e-4 - dd, 70, endpoint=False),
         np.linspace(1e-4 - dd, 1e-4 + dd, 20,
                     endpoint=False), np.linspace(1e-4 + dd, 2e-4, 70)))

    #x = np.linspace(0,L, 1000)

    # Create a system
    sys = sesame.Builder(x, y)

    tau = 1e-8
    vt = 0.025851991024560

    Nc1 = 1e17
    Nv1 = 1e18

    Nc2 = 1e17
    Nv2 = 1e18

    # Dictionary with the material parameters
    mat1 = {
        'Nc': Nc1,
        'Nv': Nv1,
        'Eg': 1.,
        'epsilon': 10,
        'Et': 0,
        'mu_e': 100,
        'mu_h': 40,
        'tau_e': tau,
        'tau_h': tau,
        'affinity': 4.15
    }

    mat2 = {
        'Nc': Nc2,
        'Nv': Nv2,
        'Eg': 1.1,
        'epsilon': 100,
        'Et': 0,
        'mu_e': 100,
        'mu_h': 40,
        'tau_e': tau,
        'tau_h': tau,
        'affinity': 4.05
    }

    junction = 2e-4  # extent of the junction from the left contact [m]

    def region1(pos):
        x, y = pos
        val = x <= 1e-4
        return val

    def region2(pos):
        x, y = pos
        val = (x > 1e-4) & (x < 3e-4) & (y >= 1e-4)
        return val

    def region3(pos):
        x, y = pos
        val = (x > 1e-4) & (x < 3e-4) & (y < 1e-4)
        return val

    def region4(pos):
        x, y = pos
        val = x >= 3e-4
        return val

    # Add the material to the system
    sys.add_material(mat1, region1)
    sys.add_material(mat1, region2)
    sys.add_material(mat2, region3)
    sys.add_material(mat2, region4)

    # Add the donors
    nD1 = 1e15  # [cm^-3]
    sys.add_donor(nD1, region1)
    sys.add_donor(nD1, region2)
    nD2 = 1e15  # [cm^-3]
    sys.add_acceptor(nD2, region3)
    sys.add_acceptor(nD2, region4)

    # Define the surface recombination velocities for electrons and holes [m/s]
    sys.contact_type('Ohmic', 'Ohmic')
    SS = 1e50
    Sn_left, Sp_left, Sn_right, Sp_right = SS, SS, SS, SS
    sys.contact_S(Sn_left, Sp_left, Sn_right, Sp_right)

    # Electrostatic potential dimensionless

    solution = sesame.solve_equilibrium(sys, periodic_bcs=False, verbose=False)
    veq = np.copy(solution['v'])

    solution.update({
        'x': sys.xpts,
        'y': sys.ypts,
        'chi': sys.bl,
        'eg': sys.Eg,
        'Nc': sys.Nc,
        'Nv': sys.Nv,
        'epsilon': sys.epsilon
    })

    # IV curve

    solution.update({
        'efn': np.zeros((sys.nx * sys.ny, )),
        'efp': np.zeros((sys.nx * sys.ny, ))
    })

    G = 1 * 1e18
    f = lambda x, y: G

    sys.generation(f)
    solution = sesame.solve(sys,
                            solution,
                            maxiter=5000,
                            periodic_bcs=False,
                            verbose=False)
    az = sesame.Analyzer(sys, solution)
    tj = -az.full_current()

    voltages = np.linspace(.0, .8, 9)

    result = solution

    # sites of the right contact
    nx = sys.nx
    s = [nx - 1 + j * nx + k * nx * sys.ny for k in range(sys.nz) \
         for j in range(sys.ny)]

    # sign of the voltage to apply
    if sys.rho[nx - 1] < 0:
        q = 1
    else:
        q = -1

    j = []
    # Loop over the applied potentials made dimensionless
    Vapp = voltages / sys.scaling.energy
    for idx, vapp in enumerate(Vapp):

        # Apply the voltage on the right contact
        result['v'][s] = veq[s] + q * vapp
        # Call the Drift Diffusion Poisson solver
        result = sesame.solve(sys,
                              result,
                              maxiter=1000,
                              periodic_bcs=False,
                              verbose=False)
        # Compute current
        az = sesame.Analyzer(sys, result)
        tj = az.full_current() * sys.scaling.current * sys.scaling.length / (
            2e-4)
        j.append(tj)

    jcomsol = np.array([
        0.50272, 0.48515, 0.40623, -0.16696, -5.1204, -58.859, -819.11,
        -7024.4, -27657
    ])
    jcomsol = jcomsol * 1e-4
    error = np.max(
        np.abs(
            (jcomsol - np.transpose(j)) / (.5 * (jcomsol + np.transpose(j)))))
Ejemplo n.º 2
0
# add donor defect along GB
sys.add_line_defects([p1, p2], rho_GB, S_GB, E=E_GB, transition=(1, 0))
# add acceptor defect along GB
sys.add_line_defects([p1, p2], rho_GB, S_GB, E=E_GB, transition=(0, -1))

# Solve equilibirum problem first
solution = sesame.solve_equilibrium(sys)

# define a function for generation profile
f = lambda x, y: 2.3e21 * np.exp(-2.3e4 * x)
# add generation to the system
sys.generation(f)

# Solve problem under short-circuit current conditions
solution = sesame.solve(sys, solution)
# Get analyzer object to compute observables
az = sesame.Analyzer(sys, solution)
# Compute short-circuit current
j = az.full_current()
# Print Jsc
print(j * sys.scaling.current * sys.scaling.length * 1e3)

# specify applied voltages
voltages = np.linspace(0, .1, 1)
# find j-v
j = sesame.IVcurve(sys, voltages, solution, '2dGB_V')

# save the result
result = {'voltages': voltages, 'j': j}
np.save('IV_values', result)
Ejemplo n.º 3
0
def runTest4():

    rhoGBlist = np.linspace(1e6*1e-4,1e18*1e-4,2)

    sys = system(rhoGBlist[0])

    solution = sesame.solve_equilibrium(sys, periodic_bcs=False, verbose=False)



    s0 = 1e-18*1e4
    rhoGBlist = [1e6*1e-4, 1e18*1e-4]
    for idx, rhoGB in enumerate(rhoGBlist):
        sys = system(rhoGB,s0)
        solution = sesame.solve_equilibrium(sys, solution, maxiter=5000, periodic_bcs=False, verbose=False)
    veq = np.copy(solution['v'])

    efn = np.zeros((sys.nx * sys.ny,))
    efp = np.zeros((sys.nx * sys.ny,))
    solution.update({'efn': efn, 'efp': efp})

    junction = .1e-6*1e2
    # Define a function for the generation rate

    G = 1
    phi0 = 1e21 * G * 1e-4
    alpha = 2.3e6 * 1e-2  # alpha = 2e4 cm^-1 for CdTe
    f = lambda x, y: phi0 * alpha * np.exp(-alpha * x)
    sys.generation(f)

    slist = [1e-18 * 1e4]

    sys = system(rhoGBlist[1],slist[0])

    sys.generation(f)
    solution = sesame.solve(sys, solution, maxiter=5000, verbose=False)
    az = sesame.Analyzer(sys, solution)
    tj = -az.full_current()

    voltages = np.linspace(.0, .8, 9)

    result = solution

    # sites of the right contact
    nx = sys.nx
    s = [nx - 1 + j * nx + k * nx * sys.ny for k in range(sys.nz) \
         for j in range(sys.ny)]

    # sign of the voltage to apply
    if sys.rho[nx - 1] < 0:
        q = 1
    else:
        q = -1

    j = []
    # Loop over the applied potentials made dimensionless
    Vapp = voltages / sys.scaling.energy
    for idx, vapp in enumerate(Vapp):

        # Apply the voltage on the right contact
        result['v'][s] = veq[s] + q * vapp
        # Call the Drift Diffusion Poisson solver
        result = sesame.solve(sys, result, maxiter=1000, periodic_bcs=False, verbose=False)
        # Compute current
        az = sesame.Analyzer(sys, result)
        tj = az.full_current() * sys.scaling.current * sys.scaling.length / (3e-6*1e2) * 1e4
        j.append(tj)
        


    jcomsol = np.array([-136.07, -135.73, -135.30, -134.73, -133.61, -129.76, -119.33, -83.149, 115.90])
    jcomsol = -jcomsol
    error = np.max(np.abs((jcomsol - np.transpose(j)) / (.5 * (jcomsol + np.transpose(j)))))
    print("error = {0}".format(error))
Ejemplo n.º 4
0
# GB defect state properties
rho_GB = 1e14  # defect density [1/cm^2]
S_GB = 1e-14  # capture cross section [cm^2]
E_GB = 0.4  # energy of gap state from intrinsic level [eV]
# Specify the two points that make the line containing additional charges
p1 = (.1e-4, 1.5 * 1e-4)  # [cm]
p2 = (2.9e-4, 1.5 * 1e-4)  # [cm]

# add donor defect along GB
sys.add_defects([p1, p2], rho_GB, S_GB, E=E_GB, transition=(1, 0))
# add acceptor defect along GB
sys.add_defects([p1, p2], rho_GB, S_GB, E=E_GB, transition=(0, -1))

# Solve equilibirum problem first
solution = sesame.solve(sys, 'Poisson')

# define a function for generation profile
f = lambda x, y: 2.3e21 * np.exp(-2.3e4 * x)
# add generation to the system
sys.generation(f)

# Solve problem under short-circuit current conditions
solution = sesame.solve(sys, guess=solution)
# Get analyzer object to compute observables
az = sesame.Analyzer(sys, solution)
# Compute short-circuit current
j = az.full_current()
# Print Jsc
print(j * sys.scaling.current * sys.scaling.length * 1e3)
def runTest2():
    L = 4e-6*1e2 # length of the system in the x-direction [m]
    dd = .005e-6*1e2

    # Mesh
    x = np.concatenate((np.linspace(0,L/2-dd, 100, endpoint=False),
                        np.linspace(L/2-dd, L/2+dd, 20, endpoint=False),
                        np.linspace(L/2+dd,L, 100)))


    # Create a system
    sys = sesame.Builder(x, input_length='cm')

    tau = 1e8
    vt = 0.025851991024560;

    Nc1 = 2.2*1e18
    Nv1 = 2.2*1e18

    Nc2 = 2.2*1e18
    Nv2 = 2.2*1e18

    # Dictionary with the material parameters
    mat1 = {'Nc':Nc1, 'Nv':Nv1, 'Eg':1.5, 'epsilon':1000, 'Et': 0,
            'mu_e':100, 'mu_h':40, 'tau_e':tau, 'tau_h':tau,
            'band_offset': 4.05}

    mat2 = {'Nc':Nc2, 'Nv':Nv2, 'Eg':1.5, 'epsilon':10000, 'Et': 0,
            'mu_e':100, 'mu_h':40, 'tau_e':tau, 'tau_h':tau,
            'band_offset': 4.05}

    junction = 2e-6*1e2 # extent of the junction from the left contact [m]

    def region1(pos):
        return pos < junction

    # Add the acceptors
    region2 = lambda pos: 1 - region1(pos)

    # Add the material to the system
    sys.add_material(mat1, region1)
    sys.add_material(mat2, region2)

    # Define Ohmic contacts
    sys.contact_type('Ohmic', 'Ohmic')

    # Add the donors
    nD1 = 1e15 # [m^-3]
    sys.add_donor(nD1, region1)
    nD2 = 1e15 # [m^-3]
    sys.add_acceptor(nD2, region2)

    # Define the surface recombination velocities for electrons and holes [m/s]
    SS = 1e50
    Sn_left, Sp_left, Sn_right, Sp_right = SS, SS, SS, SS
    sys.contact_S(Sn_left, Sp_left, Sn_right, Sp_right)

    # Electrostatic potential dimensionless
    solution = sesame.solve_equilibrium(sys, verbose=False)
    veq = np.copy(solution['v'])

    G = 1*1e24*1e-6
    f = lambda x: G
    sys.generation(f)

    solution = sesame.solve(sys, solution, verbose=False)
    solution.update({'x': sys.xpts, 'chi': sys.bl, 'eg': sys.Eg, 'Nc': sys.Nc, 'Nv': sys.Nv})

    voltages = np.linspace(0, 0.9, 10)

    result = solution

    # sites of the right contact
    nx = sys.nx
    s = [nx-1 + j*nx + k*nx*sys.ny for k in range(sys.nz)\
                                   for j in range(sys.ny)]

    # sign of the voltage to apply
    if sys.rho[nx-1] < 0:
        q = 1
    else:
        q = -1

    j = []
    # Loop over the applied potentials made dimensionless
    Vapp = voltages / sys.scaling.energy
    for idx, vapp in enumerate(Vapp):
        # Apply the voltage on the right contact
        result['v'][s] = veq[s] + q*vapp
        # Call the Drift Diffusion Poisson solver
        result = sesame.solve(sys, result, maxiter=1000, verbose=False)
        # Compute current
        az = sesame.Analyzer(sys, result)
        tj = az.full_current()* sys.scaling.current
        j.append(tj)


    jcomsol = np.array([0.55569,0.54937,0.5423,0.53436,0.52535,0.51499,0.50217,0.4622,-0.47448,-31.281])
    jcomsol = jcomsol * 1e-4
    error = np.max(np.abs((jcomsol-np.transpose(j))/(.5*(jcomsol+np.transpose(j)))))
    print("error = {0}".format(error))
Ejemplo n.º 6
0
    # Define array to store computed J-V values
    jvset_local = np.zeros([njobs, len(voltages)])
    jvset = np.zeros([njobs, len(voltages)])

    my_param_indices = range(mpirank, njobs, mpisize)

    # cycle over all parameter sets
    for myjobcounter in my_param_indices:

        # Get system for given set of parameters
        params = paramlist[myjobcounter]
        sys = system(params)

        # Get equilibrium solution
        eqsolution = sesame.solve(sys, 'Poisson')

        # Define a function for generation profile
        f = lambda x, y: 2.3e21 * np.exp(-2.3e4 * x)
        # add generation to the system
        sys.generation(f)

        # Specify output filename for given parameter set
        outputfile = ''
        for paramvalue in params:
            outputfile = outputfile + '{0}_'.format(paramvalue)

        # Compute J-V curve
        jv = sesame.IVcurve(sys, voltages, eqsolution, outputfile)
        # Save computed J-V in array
        jvset_local[myjobcounter, :] = jv
Ejemplo n.º 7
0
# gap state characteristics
E = 0  # energy of gap state (eV) from midgap
rhoGB = 1e14  # density of defect states
s = 1e-14  # defect capture cross section

# this implies a surface recombination velocity S = rhoGB*s*vthermal = 1e5 [cm/s]

# Specify the two points that make the line containing additional recombination centers
p1 = (0, Ly)
p2 = (Lx, Ly)

# add neutral defect along surface (surface recombination boundary condition)
sys.add_defects([p1, p2], rhoGB, s, E=E, transition=(0, 0))

# find equilibrium solution with GB.  Note we provide the GB-free equilibrium solution as a starting guess
solution = sesame.solve(sys, compute='Poisson', periodic_bcs=False)

######################################################
##      EBIC generation profile parameters
######################################################

q = 1.6e-19  # C
ibeam = 10e-12  # A
Ebeam = 15e3  # eV
eg = 1.5  # eV
density = 5.85  # g/cm^3
kev = 1e3  # eV
# rough approximation for total carrier generation rate from electron beam
Gtot = ibeam / q * Ebeam / (3 * eg)
# length scale of generation volume
Rbulb = 0.043 / density * (Ebeam / kev)**1.75  # given in micron
def runTest3():

    rhoGBlist = np.linspace(1e6 * 1e-4, 1e18 * 1e-4, 2)

    sys = system(rhoGBlist[0])

    solution = sesame.solve_equilibrium(sys, verbose=False)

    s0 = 1e-18 * 1e4
    rhoGBlist = [1e6 * 1e-4, 1e18 * 1e-4]
    for idx, rhoGB in enumerate(rhoGBlist):
        sys = system(rhoGB, s0)
        solution = sesame.solve_equilibrium(sys,
                                            solution,
                                            maxiter=5000,
                                            verbose=False)
    veq = np.copy(solution['v'])

    efn = np.zeros((sys.nx * sys.ny, ))
    efp = np.zeros((sys.nx * sys.ny, ))
    solution.update({'efn': efn, 'efp': efp})

    junction = .1e-6 * 1e2
    # Define a function for the generation rate

    G = 1
    phi0 = 1e21 * G * 1e-4
    alpha = 2.3e6 * 1e-2  # alpha = 2e4 cm^-1 for CdTe
    f = lambda x, y: phi0 * alpha * np.exp(-alpha * x)
    sys.generation(f)

    slist = [1e-18 * 1e4]

    sys = system(rhoGBlist[1], slist[0])

    sys.generation(f)
    solution = sesame.solve(sys, solution, maxiter=5000, verbose=False)
    az = sesame.Analyzer(sys, solution)
    tj = -az.full_current()

    voltages = np.linspace(.0, .8, 9)

    result = solution

    # sites of the right contact
    nx = sys.nx
    s = [nx - 1 + j * nx + k * nx * sys.ny for k in range(sys.nz) \
         for j in range(sys.ny)]

    # sign of the voltage to apply
    if sys.rho[nx - 1] < 0:
        q = 1
    else:
        q = -1

    j = []
    # Loop over the applied potentials made dimensionless
    Vapp = voltages / sys.scaling.energy
    for idx, vapp in enumerate(Vapp):

        # Apply the voltage on the right contact
        result['v'][s] = veq[s] + q * vapp
        # Call the Drift Diffusion Poisson solver
        result = sesame.solve(sys, result, maxiter=1000, verbose=False)
        # Compute current
        az = sesame.Analyzer(sys, result)
        tj = az.full_current() * sys.scaling.current * sys.scaling.length / (
            3e-6 * 1e2)
        j.append(tj)

    jSesame_12_4_2017 = np.array([
        135.14066065175203, 134.97430561196626, 134.70499402818209,
        134.28271667573679, 133.27884008619145, 129.49875552490002,
        119.14704988797484, 83.157765739151415, -114.57979137988193
    ])
    jSesame_12_4_2017 = jSesame_12_4_2017 * 1e-4
    error = np.max(
        np.abs((jSesame_12_4_2017 - np.transpose(j)) /
               (.5 * (jSesame_12_4_2017 + np.transpose(j)))))
    print("error = {0}".format(error))
Ejemplo n.º 9
0
def runTest1():

    L = 3e-4 # length of the system in the x-direction [cm]

    dd = 1e-7*1e2
    # Mesh
    x = np.concatenate((np.linspace(0,5e-7*1e2-dd,50, endpoint=False),
                        np.linspace(5e-7*1e2-dd,5e-7*1e2+dd,20, endpoint=False),
                        np.linspace(5e-7*1e2+dd,1e-6*1e2, 100, endpoint=False),
                        np.linspace(1e-6*1e2, L, 200)))

    # Create a system
    sys = sesame.Builder(x,input_length='cm')

    tau = 1e-8
    vt = 0.025851991024560

    Nc1 = 8*1e17
    Nv1 = 1.8*1e19

    Nc2 = 8*1e17
    Nv2 = 1.8*1e19

    # Dictionary with the material parameters
    mat1 = {'Nc':Nc1, 'Nv':Nv1, 'Eg':2.4, 'epsilon':10, 'Et': 0,
            'mu_e':320, 'mu_h':40, 'tau_e':tau, 'tau_h':tau,
            'affinity': 4.15}

    mat2 = {'Nc':Nc2, 'Nv':Nv2, 'Eg':1.5, 'epsilon':10, 'Et': 0,
            'mu_e':320, 'mu_h':40, 'tau_e':tau, 'tau_h':tau,
            'affinity': 4.05}

    junction = 5e-7*1e2 # extent of the junction from the left contact [m]
    def region1(pos):
        x = pos
        return x < junction

    # Add the acceptors
    region2 = lambda pos: 1 - region1(pos)

    # Add the material to the system
    sys.add_material(mat1, region1)
    sys.add_material(mat2, region2)

    # Add the donors
    nD1 = 1e17 # [cm^-3]
    sys.add_donor(nD1, region1)
    nD2 = 1e15 # [cm^-3]
    sys.add_acceptor(nD2, region2)

    # Define Ohmic contacts
    sys.contact_type('Ohmic', 'Ohmic')

    # Define the surface recombination velocities for electrons and holes [m/s]
    SS = 1e50*1e2
    Sn_left, Sp_left, Sn_right, Sp_right = SS, SS, SS, SS
    sys.contact_S(Sn_left, Sp_left, Sn_right, Sp_right)

    solution = sesame.solve(sys, compute='Poisson', verbose=False)
    veq = np.copy(solution['v'])

    G = 1*1e24*1e-6
    f = lambda x: G
    sys.generation(f)

    solution = sesame.solve(sys, guess=solution, verbose=False)
    solution.update({'x': sys.xpts, 'chi': sys.bl, 'eg': sys.Eg, 'Nc': sys.Nc, 'Nv': sys.Nv})

    voltages = np.linspace(0, 0.8, 9)

    result = solution

    # sites of the right contact
    nx = sys.nx
    s = [nx-1 + j*nx  for j in range(sys.ny)]

    # sign of the voltage to apply
    if sys.rho[nx-1] < 0:
        q = 1
    else:
        q = -1

    j = []
    # Loop over the applied potentials made dimensionless
    Vapp = voltages / sys.scaling.energy
    for idx, vapp in enumerate(Vapp):

        # Apply the voltage on the right contact
        result['v'][s] = veq[s] + q*vapp
        # Call the Drift Diffusion Poisson solver
        result = sesame.solve(sys, guess=result, maxiter=1000, verbose=False)
        # Compute current
        az = sesame.Analyzer(sys, result)
        tj = az.full_current()* sys.scaling.current
        j.append(tj)



    jcomsol = np.array([0.32117,0.31672,0.31198,0.30683,0.30031,0.28562,0.20949,-0.39374,-7.0681])
    jcomsol = jcomsol * 1e-4 # converting to A/cm^2

    error = np.max(np.abs((jcomsol-np.transpose(j))/(.5*(jcomsol+np.transpose(j)))))
    print("error = {0}".format(error))
def runTest8():

    L = 4e-4  # length of the system in the x-direction [m]
    dd = .05e-4
    Ly = 2e-4

    # Mesh
    x = np.concatenate(
        (np.linspace(0, 1e-4 - dd, 70, endpoint=False),
         np.linspace(1e-4 - dd, 1e-4 + dd, 20, endpoint=False),
         np.linspace(1e-4 + dd, 3e-4 - dd, 140, endpoint=False),
         np.linspace(3e-4 - dd, 3e-4 + dd, 20,
                     endpoint=False), np.linspace(3e-4 + dd, L, 70)))
    y = np.concatenate(
        (np.linspace(0, 1e-4 - dd, 70, endpoint=False),
         np.linspace(1e-4 - dd, 1e-4 + dd, 20,
                     endpoint=False), np.linspace(1e-4 + dd, 2e-4, 70)))

    # Create a system
    sys = sesame.Builder(x, y)

    tau = 1e-8
    vt = 0.025851991024560

    Nc1 = 1e17
    Nv1 = 1e18

    Nc2 = 1e17
    Nv2 = 1e18

    # Dictionary with the material parameters
    mat1 = {
        'Nc': Nc1,
        'Nv': Nv1,
        'Eg': 1.,
        'epsilon': 10,
        'Et': 0,
        'mu_e': 100,
        'mu_h': 40,
        'tau_e': tau,
        'tau_h': tau,
        'affinity': 4.15
    }

    mat2 = {
        'Nc': Nc2,
        'Nv': Nv2,
        'Eg': 1.1,
        'epsilon': 100,
        'Et': 0,
        'mu_e': 100,
        'mu_h': 40,
        'tau_e': tau,
        'tau_h': tau,
        'affinity': 4.05
    }

    junction = 2e-4  # extent of the junction from the left contact [m]

    def region1(pos):
        x, y = pos
        val = x <= 1e-4
        return val

    def region2(pos):
        x, y = pos
        val = (x > 1e-4) & (x < 3e-4) & (y >= 1e-4)
        return val

    def region3(pos):
        x, y = pos
        val = (x > 1e-4) & (x < 3e-4) & (y < 1e-4)
        return val

    def region4(pos):
        x, y = pos
        val = x >= 3e-4
        return val

    # Add the material to the system
    sys.add_material(mat1, region1)
    sys.add_material(mat1, region2)
    sys.add_material(mat2, region3)
    sys.add_material(mat2, region4)

    # Add the donors
    nD1 = 1e15  # [cm^-3]
    sys.add_donor(nD1, region1)
    sys.add_donor(nD1, region2)
    nD2 = 1e15  # [cm^-3]
    sys.add_acceptor(nD2, region3)
    sys.add_acceptor(nD2, region4)

    # Define the surface recombination velocities for electrons and holes [m/s]
    sys.contact_type('Ohmic', 'Ohmic')
    SS = 1e50
    Sn_left, Sp_left, Sn_right, Sp_right = SS, SS, SS, SS
    sys.contact_S(Sn_left, Sp_left, Sn_right, Sp_right)

    # Electrostatic potential dimensionless

    solution = sesame.solve(sys,
                            compute='Poisson',
                            periodic_bcs=False,
                            verbose=False)
    veq = np.copy(solution['v'])

    solution.update({
        'x': sys.xpts,
        'y': sys.ypts,
        'chi': sys.bl,
        'eg': sys.Eg,
        'Nc': sys.Nc,
        'Nv': sys.Nv,
        'epsilon': sys.epsilon
    })

    # IV curve

    solution.update({
        'efn': np.zeros((sys.nx * sys.ny, )),
        'efp': np.zeros((sys.nx * sys.ny, ))
    })

    G = 1 * 1e18
    f = lambda x, y: G

    sys.generation(f)
    solution = sesame.solve(sys,
                            guess=solution,
                            maxiter=5000,
                            periodic_bcs=True,
                            verbose=False)
    az = sesame.Analyzer(sys, solution)
    tj = -az.full_current()

    voltages = np.linspace(.0, .8, 9)

    result = solution

    # sites of the right contact
    nx = sys.nx
    s = [nx - 1 + j * nx for j in range(sys.ny)]

    # sign of the voltage to apply
    if sys.rho[nx - 1] < 0:
        q = 1
    else:
        q = -1

    j = []
    # Loop over the applied potentials made dimensionless
    Vapp = voltages / sys.scaling.energy
    for idx, vapp in enumerate(Vapp):

        # Apply the voltage on the right contact
        result['v'][s] = veq[s] + q * vapp
        # Call the Drift Diffusion Poisson solver
        result = sesame.solve(sys,
                              guess=result,
                              maxiter=1000,
                              periodic_bcs=True,
                              verbose=False)
        # Compute current
        az = sesame.Analyzer(sys, result)
        tj = az.full_current() * sys.scaling.current * sys.scaling.length / (
            2e-4)
        j.append(tj)

    jSesame_12_4_2017 = np.array([
        0.51880926865443222, 0.49724822874328478, 0.38634212450640715,
        -0.41864449697811151, -7.1679242861918242, -76.107867495994327,
        -919.58279216747349, -7114.3078754855478, -28453.412760553809
    ])
    jSesame_12_4_2017 = jSesame_12_4_2017 * 1e-4
    error = np.max(
        np.abs((jSesame_12_4_2017 - np.transpose(j)) /
               (.5 * (jSesame_12_4_2017 + np.transpose(j)))))
    print("error = {0}".format(error))