#!/usr/bin/perl

use lib 'd:/git/tkProg/tklib/Perl/lib';
use lib '/home/share/tkProg/tklib/Perl/lib';

use strict;
use Sci qw($kB $e);
use Sci::DiodeRs;
use Sci::TFT;
use Sci::Optimize;

#================================
# Global parameters
#================================
my $ElementsCSV = "ElementsIV.csv";
my $CircuitCSV  = "CircuitIV.csv";

my $T  = 300.0;

#================================
# Diode parameters
#================================
my $S  = 60e-6 * 20e-6;
my $I0 = 1.0e-30 * $S;
my $nd = 1.0;
my $Rs = 1.0e5;

#================================
# 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

#================================
# Circuit parameters
#================================
#my $Circuit = "pTFTNormal";
my $Circuit = "pTFTNormal2";
#my $Circuit = "nTFTNormal";
#my $Circuit = "nTFTInverted";

my $VGND = 0.0;
my $Vdd  = 10.0;

#================================
# Optimization parameters
#================================
#my $Method = "PDL::Simplex";
my $Method = "Amoeba::Simplex";
#my $Method = "ModifiedNewton";
#my $Method = "LinearOptimization"; # 線形パラメータのみ。

my $EPS         = 1.0e-4;
my $IEPS        = 1.0e-12;
my $nMaxIter    = 300;
my $iPrintLevel = -1;

#================================
# Build objects
#================================
my $diode = new DiodeRs;
$diode->SetParameters(
	T                 => $T,
	I0                => $I0,
	nd                => $nd,
	Rs                => $Rs,
	BisectionV1EPS    => 0.01,
	IEPS              => $IEPS,
	BisectionnMaxIter => 5,
	NewtonDiffV1      => 0.001,
	NewtonDump        => 0.0,
	V1EPS             => 1.0e-4,
	nMaxIter          => 30,
	iPrintLevel       => $iPrintLevel,
	);
$diode->PrepareForCalculation();

my $tft = new TFT;
$tft->SetParameters(
	T     => $T,
	W     => $TFTW,
	L     => $TFTL,
	Nes   => $TFTNes,
	Epss  => $TFTEpss,
	dg    => $TFTdg,
	Epsg  => $TFTEpsg,
	uFE   => $TFTuFE,
	Vth   => $Vth,
	VFB   => $VFB,
	VB    => $VB,
	Model => $TFTModel,
	);
$tft->PrepareForCalculation();

#================================
# Main
#================================
print "Optimization method: $Method\n";
print "Circuit: $Circuit\n";
print "\n";

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";

$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  , $Ir,   $V1O)   = $diode->DiodeRsI($V, 0);
	my ($I00, $Ir00, $V1O00) = $diode->DiodeRsI($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 = $tft->TFTIds($Vgs, $Vds);
		$out->print(",$Ids");
	}
	$out->print("\n");
}

print("Circuit I-V (Vdd=$Vdd)\n");
$cout->print("Vg,I(OLED),I(TFT),V1,V(OLED),Vgs(TFT),Vds(TFT)\n");

my $V1  = $Vdd / 2.0;
if($Circuit eq "pTFTNormal") {
	$tft->SetPolarity(-1);
	for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
		my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &TFTCircuitI(\&GetPotentialsForpTFTNormal, $Vdd, $Vg, $V1);
		$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
	}
}
elsif($Circuit eq "pTFTNormal2") {
	$tft->SetPolarity(-1);
	for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
		my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &TFTCircuitI(\&GetPotentialsForpTFTNormal2, -$Vdd, -$Vg, -$V1);
		$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
	}
}
elsif($Circuit eq "nTFTNormal") {
	$tft->SetPolarity(1);
	for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
		my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &TFTCircuitI(\&GetPotentialsFornTFTNormal, $Vdd, $Vg, $V1);
		$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
	}
}
elsif($Circuit eq "nTFTInverted") {
	$tft->SetPolarity(1);
	for(my $Vg = 0.0 ; $Vg <= 20.0 ; $Vg += 1.0) {
		my ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds) = &TFTCircuitI(\&GetPotentialsFornTFTInverted, $Vdd, $Vg, $V1);
		$cout->printf("$Vg,$IOLED,$Itft,$V1, $VOLED, $Vgs, $Vds\n");
	}
}

$cout->Close();
$out->Close();

exit;

#==============================================
# Subroutines
#==============================================
sub GetPotentialsFornTFTInverted
{
	my ($VGND, $Vdd, $Vg, $V1) = @_;

	my $Vgs   = $Vg  - $VGND;
	my $Vds   = $V1  - $VGND;
	my $VOLED = $Vdd - $V1;

	return ($VOLED, $Vds, $Vgs);
}

sub GetPotentialsForpTFTNormal
{
	my ($VGND, $Vdd, $Vg, $V1) = @_;

	my $Vgs   = $Vg  - $Vdd;
	my $Vds   = $Vdd - $V1;
	my $VOLED = $V1  - $VGND;

	return ($VOLED, $Vds, $Vgs);
}

sub GetPotentialsForpTFTNormal2
{
	my ($VGND, $Vdd, $Vg, $V1) = @_;

	my $Vgs   = $Vg   - $VGND;
	my $Vds   = $VGND - $V1;
	my $VOLED = $V1   - $Vdd;

	return ($VOLED, $Vds, $Vgs);
}

sub GetPotentialsFornTFTNormal
{
	my ($VGND, $Vdd, $Vg, $V1) = @_;

	my $Vgs   = $Vg  - $V1;
	my $Vds   = $Vdd - $V1;
	my $VOLED = $V1  - $VGND;

	return ($VOLED, $Vds, $Vgs);
}

sub TFTCircuitI
{
	my ($pGetPotentialFunc, $Vdd, $Vg, $V1) = @_;

	my $optimize = new Optimize;
#	$optimize->SetDumpingFactor($DumpingFactor, $DumpingFactor, 1);
#	$optimize->SetDiffV($DiffV);
	$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 { &CalSForTFTCurcuitI($pGetPotentialFunc, $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 ($VOLED, $Vds, $Vgs) = &$pGetPotentialFunc($VGND, $Vdd, $Vg, $V1);
	my $Itft  = $tft->TFTIds($Vgs, $Vds);
	my ($IOLED, $Ir, $V1O) = $diode->DiodeRsI($VOLED, 0);
printf "Vg=%5.2f VOLED=%5.2f Vds=%5.2f Vgs=%5.2f (%5.2f - %5.2f - %5.2f) Itft=%8.3e IOLED=%8.3e\n",
		$Vg, $VOLED, $Vds, $Vgs, $VGND, $V1, $Vdd, $Itft, $IOLED;

	return ($IOLED, $Itft, $V1, $VOLED, $Vgs, $Vds);
}

sub CalSForTFTCurcuitI {
	my ($pGetPotentialFunc, $Vdd, $Vg, $pVars, $iPrintLevel) = @_;
	my ($V1) = @$pVars;
#print "V,I=$V,$I\n";

	my ($VOLED, $Vds, $Vgs) = &$pGetPotentialFunc($VGND, $Vdd, $Vg, $V1);
	my $Itft               = $tft->TFTIds($Vgs, $Vds);
	my ($IOLED, $Ir, $V1O) = $diode->DiodeRsI($VOLED, 0);
#print "VOLED=$VOLED\tVg=$Vg\tVds=$Vds\tVgs=$Vgs ($VGND - $V1 - $Vdd) Itft=$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;
}
