from pyopus.evaluator.aggregate import formatParameters
from pyopus.design.cbd import CornerBasedDesign, generateCorners
from pyopus.parallel.cooperative import cOS

heads = {
	'opus': {
		'simulator': 'SpiceOpus', 
		'settings': {
			'debug': 0
		},
		'moddefs': {
			'def':  { 'file': 'bjtamp.inc' }, 
		}, 
		'options': {
		}, 
		'params': {
		}
	}
}

analyses = {
	'dc': {
		'head': 'opus', 
		'modules': [ 'def' ], 
		'command': "dc(-100e-6, 100e-6, 'lin', 100, 'i1', 'dc')"
	}, 
}

# Define performance measures, dependencies, and design requirements (lower and upper bounds)
# Gain should be above 16kV/A, nonlinearity should be below 75mV
# This is a less strict requirement compared to nominal design because it is 
# harder to satisfy the design requirements across multiple corners. 
measures = {
	'gain': {
		'analysis': 'dc', 
		'expression': """
# Get response
x, y = scale(), v('out')

# Construct line from first to last point, extract gain
gain=(y[-1]-y[0])/(x[-1]-x[0])
""", 
		'lower':16.4e3
	}, 
	'nonlinearity': {
		'analysis': 'dc', 
		'expression': """
# Get response
x, y = scale(), v('out')

# Construct line from first to last point
ylin=y[0]+(x-x[0])*(y[-1]-y[0])/(x[-1]-x[0])

# Maximal deviation from line
nonlinearity=(np.abs(y-ylin)).max()
""", 
		'upper': 80e-3
	}, 
}

# Design parameters, lower bounds, upper bounds, and initial values
designParams={
	'r1': {
		'lo':	5e3, 
		'hi':	50e3, 
		'init': 45e3,
	}, 
	'r2': {
		'lo':	20e3, 
		'hi':	200e3, 
		'init': 195e3,
	}, 
}
	
# Operatng parameters
opParams={
	'vcc': {
		'lo': 10, 
		'hi': 14, 
		'init': 12
	},
	'temperature': {
		'lo': 0, 
		'hi': 100, 
		'init': 25
	}
}

# Extreme corners (2 temperatures, 2 voltages) = 4 corners
corners, names = generateCorners(
	specs=[
		(
			'param', 'vcc', 
			[opParams['vcc']['lo'], opParams['vcc']['hi']], 
			['vl', 'vh'], 
		), 
		(
			'param', 'temperature', 
			[opParams['temperature']['lo'], opParams['temperature']['hi']], 
			['tl', 'th'], 
		), 
	], 
	heads=[ 'opus' ], 
)

# Add nominal corner
corners.update({
	'nominal': {
		'heads': ['opus'], 
		'params': {
			'vcc': opParams['vcc']['init'], 
			'temperature': opParams['temperature']['init']
		}, 
		'modules': []
	}
})

		
if __name__=='__main__':
	# Uncomment if you want to run it in parallel using MPI
	from pyopus.parallel.mpi import MPI
	cOS.setVM(MPI())
	
	# Design the circuit so that it satisfies the design requirements in nominal corner
	designer=CornerBasedDesign(
		designParams, heads, analyses, measures, corners, 
		initial={ name: value['init'] for name, value in designParams.items() }, 
		method='global', 
		debug=1
	)
	
	# Run optimization
	atDesign, aggregator, analysisCount = designer()
	
	# Print result
	print(formatParameters(atDesign))
	print(aggregator.formatResults())
	
	# Finalize cOS parallel environment
	cOS.finalize()
