#!/usr/bin/perl

use lib 'd:/Programs/Perl/lib';

use strict;
use Sci qw($kB $e);
use Sci::Material;
use Sci::TFT;
use Sci::Optimize;

#================================
# Global parameters
#================================
my $ElementsCSV = "ElementsIV.csv";
my $CircuitCSV  = "CircuitIV.csv";

my $material = new Material;

#================================
# Diode parameters
#================================
my $T  = 300.0;
my $S  = 60e-6 * 20e-6;
my $I0 = 1.0e-30 * $S;
my $nd = 1.0;
my $Rs = 1.0e5;
my $kd = $e / ($nd*$kB*$T);

#================================
# TFT parameters
#================================
my $TFTModel = 'GCA';
#my $TFTModel = 'Exact';
my $TFTW     = 200e-6;
my $TFTL     =  50e-6;
my $TFTNes   = 1.0e15; # cm-3
my $TFTEpss  = 13.0;
my $TFTuFE   = 10.0;   # cm2/Vs
my $TFTEpsg  = 4.0;
my $TFTdg    = 150.0;  # nm
my $Vth      = 0.0;    # V
my $VFB      = 0.0;    # V
my $VB       = 0.0;    # V
my $TFTCox   = $material->CalCapacitance($TFTEpsg, 0.01*0.01, $TFTdg*1.0e-9); # F/cm2
print "Cox: $TFTCox F/cm2\n";

my $tft = new TFT;
$tft->SetParameters(
	T     => $T,
	W     => $TFTW,
	L     => $TFTL,
	Nes   => $TFTNes,
	Epss  => $TFTEpss,
	uFE   => $TFTuFE,
	Cox   => $TFTCox,
	Vth   => $Vth,
	VFB   => $VFB,
	VB    => $VB,
	Model => $TFTModel,
	);
$tft->PrepareForCalculation();

#================================
# Optimization parameters
#================================
#my $Method = "PDL::Simplex";
my $Method = "Amoeba::Simplex";
#my $Method = "ModifiedNewton";
#my $Method = "LinearOptimization"; # 線形パラメータのみ。

my $EPS         = 1.0e-4;
my $nMaxIter    = 300;
my $iPrintLevel = -1;
print "use $Method\n";

#================================
# Main
#================================
print "Open $ElementsCSV\n";
my $out  = JFile->new($ElementsCSV, "w") or die "$!: Can not write to [$ElementsCSV].\n";
print "Open $CircuitCSV\n";
my $cout = JFile->new($CircuitCSV, "w") or die "$!: Can not write to [$CircuitCSV].\n";

#&DiodeI(0.1, 0);
print("Diode I-V from I\n");
$out->print("Diode I-V from I\n");
$out->print("V,Vd,I\n");
for(my $I = 0.0 ; $I <= 5.0e-6 ; $I += 0.2e-6) {
	my $V  = &DiodeV($I, 0);
	my $Vd = &DiodeV($I, 1);
	$out->print("$V,$Vd,$I\n");
}

$out->print("\n");
print("Diode I-V from V\n");
$out->print("Diode I-V from V\n");
$out->print("V,I,I(Rs=0)\n");
my $Iprev;
for(my $V = 0.0 ; $V <= 5.0 ; $V += 0.2) {
	my $I   = &DiodeI($V, 0, $Iprev);
	my $I00 = &DiodeI($V, 1);
	$out->print("$V,$I,$I00\n");
	$Iprev = $I;
}

$out->print("\n");
print("TFT I-V\n");
$out->print("TFT I-V\n");
$out->print("Vds");
for(my $Vgs = 2.0 ; $Vgs <= 20.0 ; $Vgs += 2.0) {
	$out->print(",Ids(Vgs=$Vgs)");
}
$out->print("\n");
for(my $Vds = 0.0 ; $Vds <= 20.0 ; $Vds += 1.0) {
	$out->printf("$Vds");
	for(my $Vgs = 2.0 ; $Vgs <= 20.0 ; $Vgs += 2.0) {
		my $Ids = &TFTI($Vds, $Vgs, $TFTModel);
		$out->print(",$Ids");
	}
	$out->print("\n");
}


my $Vdd = 10.0;
$out->print("Circuit I-V (Vdd=$Vdd)\n");
my $V1  = $Vdd / 2.0;
#$cout->print("Vg,I(OLED),I(TFT),V1,V(OLED),Vgs(TFT),Vds(TFT)\n");
#for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
#	my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &nTFTNormalCircuitI($Vdd, $Vg, $V1);
#	$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
#}
#$cout->print("Vg,I(OLED),I(TFT),V1,V(OLED),Vgs(TFT),Vds(TFT)\n");
#for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
#	my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &pTFTNormalCircuitI($Vdd, $Vg, $V1);
#	$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
#}
$cout->print("Vg,I(OLED),I(TFT),V1,V(OLED),Vgs(TFT),Vds(TFT)\n");
for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
	my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &nTFTInvertedCircuitI($Vdd, $Vg, $V1);
	$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
}

$cout->Close();
$out->Close();

exit;

#==============================================
# Subroutines
#==============================================
sub nTFTInvertedCircuitI
{
	my ($Vdd, $Vg, $V1) = @_;

	my $optimize = new Optimize;
	$optimize->AddParameters(
#name, var, ID, scale, min, max
		"V1",  \$V1, 1, 0.1, 0.0, undef,
		sub {
			$V1 = $_[0];
		},
		);

	my ($OptVars, $MinVal) = $optimize->Optimize(
		$Method, undef, undef, undef,
		$EPS, $nMaxIter, $iPrintLevel,
		sub { &CalSFornTFTInvertedCurcuitI($Vdd, $Vg, @_); },
		undef,
		sub { Optimize::BuildDifferentialMatrixes(@_); },
		);
	$optimize->RecoverParameters($OptVars);
if($EPS < $MinVal or $MinVal < 0) {
	print "Error in CircuitI: Not converged for Vdd = $Vdd, Vg = $Vg [V1=$V1, S=$MinVal (EPS=$EPS)\n";
	exit;
}
#	print "\nOptimized at I = $I for V = $V (S = $MinVal):\n";
#	$optimize->PrintParameters(1, $OptVars, $MinVal, 1);

	my $VGND  = 0.0;
	my $Vgs   = $Vg  - $VGND;
	my $Vds   = $V1  - $VGND;
	my $VOLED = $Vdd - $V1;

	my $Itft  = $tft->IdsByModel($Vgs, $Vds);
	my $IOLED = &DiodeI($VOLED, 0, $Itft);
print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd)\tItft=$Itft\tIOLED=$IOLED\n";

	return ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds);
}

sub CalSFornTFTInvertedCurcuitI {
	my ($Vdd, $Vg, $pVars, $iPrintLevel) = @_;
	my ($V1) = @$pVars;
#print "V,I=$V,$I\n";

	my $VGND  = 0.0;
	my $Vgs   = $Vg  - $VGND;
	my $Vds   = $V1  - $VGND;
	my $VOLED = $Vdd - $V1;

	my $Itft  = $tft->IdsByModel($Vgs, $Vds);
	my $IOLED = &DiodeI($VOLED, 0, $Itft);
#print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd)\tItft=$Itft\tIOLED=$IOLED\n";

	my $S   = ($Itft - $IOLED)**2;
#printf("a,b,c=%6.3f\t%6.3f\t%6.3f\t(%g)\n", $a, $b, $c, $S) if($iPrintLevel >= 2);

	return $S;
}

sub pTFTNormalCircuitI
{
	my ($Vdd, $Vg, $V1) = @_;

	my $optimize = new Optimize;
	$optimize->AddParameters(
#name, var, ID, scale, min, max
		"V1",  \$V1, 1, 0.1, 0.0, undef,
		sub {
			$V1 = $_[0];
		},
		);

	my ($OptVars, $MinVal) = $optimize->Optimize(
		$Method, undef, undef, undef,
		$EPS, $nMaxIter, $iPrintLevel,
		sub { &CalSForpTFTNormalCurcuitI($Vdd, $Vg, @_); },
		undef,
		sub { Optimize::BuildDifferentialMatrixes(@_); },
		);
	$optimize->RecoverParameters($OptVars);
if($EPS < $MinVal or $MinVal < 0) {
	print "Error in CircuitI: Not converged for Vdd = $Vdd, Vg = $Vg [V1=$V1, S=$MinVal (EPS=$EPS)\n";
	exit;
}
#	print "\nOptimized at I = $I for V = $V (S = $MinVal):\n";
#	$optimize->PrintParameters(1, $OptVars, $MinVal, 1);

	my $VGND  = 0.0;
	my $Vgs   = $Vg  - $Vdd;
	my $Vds   = $Vdd - $V1;
	my $VOLED = $V1  - $VGND;

	my $Itft  = $tft->IdsByModel(-$Vgs, $Vds);
	my $IOLED = &DiodeI($VOLED, 0, $Itft);
print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd)\tItft=$Itft\tIOLED=$IOLED\n";

	return ($IOLED, $Itft, $V1, $VOLED, -$Vgs, $Vds);
}

sub CalSForpTFTNormalCurcuitI {
	my ($Vdd, $Vg, $pVars, $iPrintLevel) = @_;
	my ($V1) = @$pVars;
#print "V,I=$V,$I\n";

	my $VGND  = 0.0;
	my $Vgs   = $Vg  - $Vdd;
	my $Vds   = $Vdd - $V1;
	my $VOLED = $V1  - $VGND;

	my $Itft  = $tft->IdsByModel(-$Vgs, $Vds);
	my $IOLED = &DiodeI($VOLED, 0, $Itft);
#print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd)\tItft=$Itft\tIOLED=$IOLED\n";

	my $S   = ($Itft - $IOLED)**2;
#printf("a,b,c=%6.3f\t%6.3f\t%6.3f\t(%g)\n", $a, $b, $c, $S) if($iPrintLevel >= 2);

	return $S;
}

sub nTFTNormalCircuitI
{
	my ($Vdd, $Vg, $V1) = @_;

	my $optimize = new Optimize;
	$optimize->AddParameters(
#name, var, ID, scale, min, max
		"V1",  \$V1, 1, 0.1, 0.0, undef,
		sub {
			$V1 = $_[0];
		},
		);

	my ($OptVars, $MinVal) = $optimize->Optimize(
		$Method, undef, undef, undef,
		$EPS, $nMaxIter, $iPrintLevel,
		sub { &CalSFornTFTNormalCurcuitI($Vdd, $Vg, @_); },
		undef,
		sub { Optimize::BuildDifferentialMatrixes(@_); },
		);
	$optimize->RecoverParameters($OptVars);
if($EPS < $MinVal or $MinVal < 0) {
	print "Error in CircuitI: Not converged for Vdd = $Vdd, Vg = $Vg [V1=$V1, S=$MinVal (EPS=$EPS)\n";
	exit;
}
#	print "\nOptimized at I = $I for V = $V (S = $MinVal):\n";
#	$optimize->PrintParameters(1, $OptVars, $MinVal, 1);

	my $VGND  = 0.0;
	my $Vgs   = $Vg  - $V1;
	my $Vds   = $Vdd - $V1;
	my $VOLED = $V1  - $VGND;

	my $Itft  = $tft->IdsByModel($Vgs, $Vds);
	my $IOLED = &DiodeI($VOLED, 0, $Itft);
print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd)\tItft=$Itft\tIOLED=$IOLED\n";

	return ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds);
}

sub CalSFornTFTNormalCurcuitI {
	my ($Vdd, $Vg, $pVars, $iPrintLevel) = @_;
	my ($V1) = @$pVars;
#print "V,I=$V,$I\n";

	my $VGND  = 0.0;
	my $Vgs   = $Vg  - $V1;
	my $Vds   = $Vdd - $V1;
	my $VOLED = $V1  - $VGND;

	my $Itft  = $tft->IdsByModel($Vgs, $Vds);
	my $IOLED = &DiodeI($VOLED, 0, $Itft);
#print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd)\tItft=$Itft\tIOLED=$IOLED\n";

	my $S   = ($Itft - $IOLED)**2;
#printf("a,b,c=%6.3f\t%6.3f\t%6.3f\t(%g)\n", $a, $b, $c, $S) if($iPrintLevel >= 2);

	return $S;
}

sub DiodeV
{
	my ($I, $IgnoreRs) = @_;

# I = I0(exp(kVd)-1)
# Vd = ln((I+1)/I0)/k
	return -1e10 if($I <= -$I0);

	my $Vd = log($I / $I0 + 1.0) / $kd;

	return $Vd if($IgnoreRs);
	return $Rs * $I + $Vd;
}

sub DiodeI
{
	my ($V, $IgnoreRs, $Iini) = @_;

	my $I = $I0 * (exp($kd * $V) - 1.0);
	return $I  if($IgnoreRs or $V <= 0.0);

	$I = $Iini if(defined $Iini);

	my $optimize = new Optimize;
	$optimize->AddParameters(
#name, var, ID, scale, min, max
		"I",  \$I, 1, 0.1, 0.0, undef,
		sub {
			$I = $_[0];
		},
		);

	my ($OptVars, $MinVal) = $optimize->Optimize(
		$Method, undef, undef, undef,
		$EPS, $nMaxIter, $iPrintLevel,
		sub { &CalSForDiodeI($V, @_); },
		undef,
		sub { Optimize::BuildDifferentialMatrixes(@_); },
		);
if($EPS < $MinVal or $MinVal < 0) {
	print "Error in DiodeI: Not converged for V = $V [I=$I, S=$MinVal (EPS=$EPS)\n";
	exit;
}
#	print "\nOptimized at I = $I for V = $V (S = $MinVal):\n";
	$optimize->RecoverParameters($OptVars);
#	$optimize->PrintParameters(1, $OptVars, $MinVal, 1);

	return $I;
}

sub CalSForDiodeI {
	my ($V, $pVars, $iPrintLevel) = @_;
	my ($I) = @$pVars;
#print "V,I=$V,$I\n";
	my $S   = (&DiodeV($I, 0) - $V)**2;
#printf("a,b,c=%6.3f\t%6.3f\t%6.3f\t(%g)\n", $a, $b, $c, $S) if($iPrintLevel >= 2);
	return $S;
}

sub TFTI
{
	my ($Vds, $Vgs, $Model) = @_;

	return $tft->IdsByModel($Vgs, $Vds);
}
