Преглед на файлове

adaptedfmu now supports initialization

Claudio Gomes преди 7 години
родител
ревизия
c34e10ea20

+ 30 - 4
HintCO/src/ua/ansymo/hintco/AdaptedFMUInstance.xtend

@@ -13,12 +13,23 @@ class AdaptedFMUInstance extends FmuInstance {
 	val Map<String, List<IAdaptedInputPort>> adaptedInPorts = newHashMap()
 	val Map<String, IAdaptedOutputPort> adaptedOutPorts = newHashMap()
 	
-	new(IFmiComponent f, CosimUnitInstance unit) {
+	val INITIALIZING = 1
+	val INITIALIZED = 2
+	val INSTANTIATED = 0
+	
+	int currentMode
+	
+	new(IFmiComponent f, UnitInstance unit) {
 		super(f)
 		instantiateAdaptations(unit)
+		currentMode = INSTANTIATED
+	}
+	
+	new(UnitInstance unit) {
+		instantiateAdaptations(unit)
 	}
 	
-	def instantiateAdaptations(CosimUnitInstance unit) {
+	def instantiateAdaptations(UnitInstance unit) {
 		val unitAdaptations = unit.selectedAdaptations
 		
 		if (!unitAdaptations.empty){
@@ -67,10 +78,17 @@ class AdaptedFMUInstance extends FmuInstance {
 			}
 		}
 		
-		// TODO Instantiate each adaptation output port of the unit.
 	}
 	
+	override enterInitializationMode() throws FmuInvocationException {
+		currentMode = INITIALIZING
+		super.enterInitializationMode()
+	}
 	
+	override exitInitializationMode() throws FmuInvocationException {
+		currentMode = INITIALIZED
+		super.exitInitializationMode()
+	}
 	
 	override doStep(double currentCommunicationPoint, double communicationStepSize) throws FmuInvocationException {
 		val micro_step = communicationStepSize/innerRate
@@ -124,7 +142,11 @@ class AdaptedFMUInstance extends FmuInstance {
 		val adaptedPorts = adaptedInPorts.get(portName)
 		if (adaptedPorts !== null){
 			for (p : adaptedPorts){
-				p.setReal(v)				
+				p.setReal(v)
+				// In initialization mode, the values of the adaptation must be propagated to the internal fmus.
+				if (currentMode == INITIALIZING){
+					p.computeReal(0.0, 0.0, 0.0, 0.0)	
+				}
 			}
 		} else {
 			setInnerReal(portName, v)
@@ -134,6 +156,10 @@ class AdaptedFMUInstance extends FmuInstance {
 	override getReal(String portName){
 		val adaptedPort = adaptedOutPorts.get(portName)
 		if (adaptedPort !== null){
+			// In initialization mode, the values of the adaptation must be obtained from the internal fmus.
+			if (currentMode == INITIALIZING){
+				adaptedPort.recordReal(0.0,0.0,0.0,0.0)
+			}
 			adaptedPort.getReal()
 		} else {
 			getInnerReal(portName)

+ 37 - 19
HintCO/src/ua/ansymo/hintco/CosimRunner.xtend

@@ -11,7 +11,7 @@ import org.slf4j.LoggerFactory
 
 class CosimRunner implements ICosimRunner, Closeable {
 	
-	val HashMap<CosimUnitInstance, IFmuInstance> fmuInstanceMap = newHashMap()
+	val HashMap<UnitInstance, IFmuInstance> fmuInstanceMap = newHashMap()
 	
 	IOutputProcessor outProcessor
 	
@@ -24,37 +24,51 @@ class CosimRunner implements ICosimRunner, Closeable {
 		fmuLoader = l
 	}
 	
-	override run(RootCandidateScenario scenario, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap, String variantID) {
-		Assert.isTrue(scenarioNodesMap.values.forall[sortedNodes | sortedNodes.forall[n1 | scenario.eAllContents.exists[n2 | n1===n2]]])
+	override run(RootCandidateScenario rootScenario, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap, String variantID) {
+		Assert.isTrue(scenarioNodesMap.values.forall[sortedNodes | sortedNodes.forall[n1 | rootScenario.eAllContents.exists[n2 | n1===n2]]])
 		
-		val rootScenario = scenarioNodesMap.keySet.filter(RootCandidateScenario).head
 		val nodes = scenarioNodesMap.get(rootScenario)
 		
-		val units = nodes.filter(CosimUnitInstance).toList
+		val units = nodes.filter(UnitInstance).toList
 		
-		// Instantiate each fmu, and record the instance reference.
-		for (unit : units) {
-			val fmuInstance = fmuLoader.instantiate(unit)
+		// Instantiate each unit, and record the instance reference.
+		for (unit : units) {
+			val fmuInstance = fmuLoader.instantiate(unit, scenarioNodesMap)
 			logger.debug("Unit {} instantiated", unit)
 			
+			fmuInstanceMap.put(unit, fmuInstance)
+		}
+		
+		// TODO Mode these to protected methods of this class.
+		// setup experiment
+		for (unit: units){
+			val fmuInstance = fmuInstanceMap.get(unit)
 			fmuInstance.setupExperiment()
 			logger.debug("Unit {} experiment setup", unit)
 			
 			// TODO Set the configured parameter values.
-			
-			// Go into init mode
+		}
+		
+		// Go into init mode
+		for (unit: units){
+			val fmuInstance = fmuInstanceMap.get(unit)
 			fmuInstance.enterInitializationMode()
 			logger.debug("Unit {} enter initialization mode", unit)
-			
-			fmuInstanceMap.put(unit, fmuInstance)
 		}
 		
 		// Fixpoint initial values, by following the same order of get.values as specified in nodes.
 		// Uses a map to store the previous value of each output. Then, whenever a new value of that output is obtained, checks if is the same as the previous value.
 		val outVals = newHashMap()
-		val outputPorts = nodes.filter(OutputPortInstance).toList
+		// Takes into account hierarchical unit ports.
+		val childOutputPorts = nodes.filter(OutputPortInstance).filter[
+			if (it instanceof HierarchicalUnitPort){
+				! it.isInput
+			} else {
+				true
+			}
+		].toList
 		// Initialize
-		for (out : outputPorts){
+		for (out : childOutputPorts){
 			outVals.put(out, Double.POSITIVE_INFINITY)
 			logger.debug("Output port {}.{} initialized to infinity", out.unit.identifier, out.identifier)
 		}
@@ -68,7 +82,8 @@ class CosimRunner implements ICosimRunner, Closeable {
 		while (!converged && i<MAX_IT){
 			logger.debug("Iteration {} start.", i)
 			converged = true
-			for (out : outputPorts){
+			for (out : childOutputPorts){
+				// TODO The getReal method of the hierarchical units needs to pull the latest value from the internal unit.
 				val outV = fmuInstanceMap.get(out.unit).getReal(out.identifier)
 				logger.debug("GetReal {}.{} at iteration {} yields {}.", out.unit.identifier, out.identifier, i, outV)
 				// Check if outV has changed.
@@ -81,6 +96,7 @@ class CosimRunner implements ICosimRunner, Closeable {
 				// Propagate outV to inputs.
 				// TODO: Take types into account. For now we assume everything is real.
 				for (trgPort : out.valueTo){
+					// TODO The set real method of the hierarchical unit will propagate the values to the internal units, and trigger a full propagation of the effects. This is only during the init mode
 					fmuInstanceMap.get(trgPort.unit).setReal(trgPort.identifier, outV)
 					logger.debug("SetReal {}.{} to {} at iteration {}.", trgPort.unit.identifier, trgPort.identifier, outV, i)
 				}
@@ -94,10 +110,11 @@ class CosimRunner implements ICosimRunner, Closeable {
 		}
 		
 		// Init output processor with experiment parameters
-		outProcessor.initialize(scenario, variantID)
+		outProcessor.initialize(rootScenario, variantID)
 		logger.debug("Output processor initialized.")
 		
 		// Add first line
+		// TODO gather all the outputs from ALL output ports.
 		outProcessor.setOutputs(0, 0.0, outVals)
 		logger.debug("Output line recorded for time {}.", 0.0)
 		
@@ -109,8 +126,8 @@ class CosimRunner implements ICosimRunner, Closeable {
 		
 		// Run the co-sim scenario
 		var time = 0.0d
-		val stepSize = scenario.stepSize
-		val stopTime = scenario.stopTime
+		val stepSize = rootScenario.stepSize
+		val stopTime = rootScenario.stopTime
 		i = 0
 		while (time < stopTime || MathUtils.isApproximatelyEqual(time, stopTime, stepSize*1e-3)){
 			logger.debug("Co-simulation step start at time {} -> {}.", time, time + stepSize)
@@ -156,7 +173,8 @@ class CosimRunner implements ICosimRunner, Closeable {
 			// If traceOutput is enabled, we print every value set to the fmu, and the time at which it was set. 
 			
 			// Refresh outputs
-			for (n : outputPorts) {
+			// TODO Get ALL OUTPUT Ports.
+			for (n : childOutputPorts) {
 				val outV = fmuInstanceMap.get(n.unit).getReal(n.identifier)
 				logger.debug("GetReal {}.{} at time {} yields {}.", n.unit.identifier, n.identifier, time, outV)
 				outVals.put(n, outV)					

+ 5 - 1
HintCO/src/ua/ansymo/hintco/FmuInstance.xtend

@@ -8,8 +8,8 @@ import org.intocps.fmi.IFmiComponent
 import org.intocps.orchestration.coe.modeldefinition.ModelDescription
 import org.intocps.orchestration.coe.modeldefinition.ModelDescription.ScalarVariable
 
-class FmuInstance implements IFmuInstance  {
 	
+class FmuInstance implements IFmuInstance  {
 	var IFmiComponent f
 	var Map<String, ScalarVariable> varsByName = newHashMap()
 	
@@ -18,6 +18,10 @@ class FmuInstance implements IFmuInstance  {
 		loadVariables()
 	}
 	
+	new(){
+		this.f = null
+	}
+	
 	def loadVariables(){
 		val modDesc = new ModelDescription(f.fmu.modelDescription)
 		for (v : modDesc.scalarVariables){

+ 24 - 12
HintCO/src/ua/ansymo/hintco/FmuLoader.xtend

@@ -1,13 +1,16 @@
 package ua.ansymo.hintco
 
 import java.io.File
+import java.util.List
 import java.util.Map
+import org.eclipse.core.runtime.Assert
 import org.intocps.fmi.Fmi2Status
 import org.intocps.fmi.IFmu
 import org.intocps.fmi.IFmuCallback
 import org.intocps.fmi.jnifmuapi.Factory
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
+import org.intocps.fmi.IFmiComponent
 
 class FmuLoader implements IFmuLoader {
 	
@@ -29,20 +32,29 @@ class FmuLoader implements IFmuLoader {
 		loadedFMUs.get(u)
 	}
 	
-	override instantiate(CosimUnitInstance instance) {
+	override instantiate(UnitInstance instance, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap) {
 		logger.debug("Instantiating {}.", instance.identifier)
-		val fmu = getOrLoad(instance.declaration)
+		
+		var AdaptedFMUInstance fmuInstance
+		switch (instance) {
+			HierarchicalCosimUnit: {
+				fmuInstance = new HierarchicalInstance(instance, scenarioNodesMap, this)
+			}
+			CosimUnitInstance: {
+				val fmu = getOrLoad(instance.declaration)
+				val internalFmuComponent = fmu.instantiate(instance.declaration.guid, instance.identifier, true, logger.isDebugEnabled, new IFmuCallback() {
+								override log(String instanceName, Fmi2Status status, String category, String message) {
+									logger.debug(instanceName + ", " + status + ", " + category + ", " + message)
+								}
+								override stepFinished(Fmi2Status status) {}
+							})
+				fmuInstance = new AdaptedFMUInstance(internalFmuComponent, instance)
+			}
+			default: {
+				Assert.isTrue(false)
+			}
+		}
 		// Inject the AdaptedFMU object, which is a wrapper to the real FMU, containing the adaptations declared.
-		val fmuInstance = new AdaptedFMUInstance(
-					fmu.instantiate(instance.declaration.guid, instance.identifier, true, logger.isDebugEnabled, new IFmuCallback() {
-						override log(String instanceName, Fmi2Status status, String category, String message) {
-							println("log: " + instanceName + " " + status + " " + category + " " + message);
-						}
-				
-						override stepFinished(Fmi2Status status) {
-						}
-					}), 
-					instance)
 		fmuInstance
 	}
 	

+ 91 - 0
HintCO/src/ua/ansymo/hintco/HierarchicalInstance.xtend

@@ -0,0 +1,91 @@
+package ua.ansymo.hintco
+
+import java.util.List
+import java.util.Map
+import org.eclipse.core.runtime.Assert
+import org.intocps.fmi.Fmi2Status
+import org.intocps.fmi.FmuInvocationException
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+// TODO Possible reuse mechanism: extends CosimRunner
+class HierarchicalInstance extends AdaptedFMUInstance {
+	
+	FmuLoader loader
+	List<PrecendenceNode> nodes
+	HierarchicalCosimUnit unit
+	Map<UnitInstance, IFmuInstance> fmuInstanceMap = newHashMap()
+	
+	Logger logger = LoggerFactory.getLogger(typeof(HierarchicalInstance))
+	
+	List<UnitInstance> units
+	
+	new(HierarchicalCosimUnit u, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap, FmuLoader l) {
+		super(u)
+		loader = l
+		unit = u
+		Assert.isTrue(scenarioNodesMap.containsKey(unit))
+		nodes = scenarioNodesMap.get(unit)
+		
+		// Instantiate subunits.
+		// TODO Can be refactored from the cosim runner.
+		units = nodes.filter(UnitInstance).toList
+		
+		// Instantiate each unit, and record the instance reference.
+		for (subUnit : units) {
+			val fmuInstance = loader.instantiate(subUnit, scenarioNodesMap)
+			logger.debug("Unit {} instantiated", subUnit)
+			
+			fmuInstanceMap.put(subUnit, fmuInstance)
+		}
+	}
+
+	override setupExperiment() {
+		// setup experiment
+		for (unit: units){
+			val fmuInstance = fmuInstanceMap.get(unit)
+			fmuInstance.setupExperiment()
+			logger.debug("Unit {} experiment setup", unit)
+			
+			// TODO Set the configured parameter values.
+		}
+		return Fmi2Status.OK
+	}
+	
+	override enterInitializationMode() throws FmuInvocationException {
+		// Go into init mode
+		for (unit: units){
+			val fmuInstance = fmuInstanceMap.get(unit)
+			fmuInstance.enterInitializationMode()
+			logger.debug("Unit {} enter initialization mode", unit)
+		}
+		return Fmi2Status.OK
+	}
+	
+	override exitInitializationMode() throws FmuInvocationException {
+		// Go into init mode
+		for (unit: units){
+			val fmuInstance = fmuInstanceMap.get(unit)
+			fmuInstance.exitInitializationMode()
+			logger.debug("Unit {} enter initialization mode", unit)
+		}
+		return Fmi2Status.OK
+	}
+	
+	override setInnerReal(String portName, double v){
+		/*
+		 * This method is called after the adaptations are processed.
+		 * If we are in initialization mode, then we need to propagate the values to the internal units, and trigger their propagation.
+		 * Otherwise, just set the value on the internal ports.
+		 */
+	}
+	
+	override getInnerReal(String portName){
+		/*
+		 * This method is called after the adaptations are processed.
+		 * Just get the value on the internal ports.
+		 */
+		 return 0.0
+	}
+	
+}

+ 1 - 1
HintCO/src/ua/ansymo/hintco/ICosimRunner.xtend

@@ -4,5 +4,5 @@ import java.util.List
 import java.util.Map
 
 interface ICosimRunner {
-	def void run(RootCandidateScenario scenario, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap, String variantID)
+	def void run(RootCandidateScenario rootScenario, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap, String variantID)
 }

+ 4 - 1
HintCO/src/ua/ansymo/hintco/IFmuLoader.xtend

@@ -1,7 +1,10 @@
 package ua.ansymo.hintco
 
+import java.util.List
+import java.util.Map
+
 interface IFmuLoader {
-	def IFmuInstance instantiate(CosimUnitInstance instance)
+	def IFmuInstance instantiate(UnitInstance instance, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap)
 	
 	def void free()
 	

+ 2 - 0
HintCO/src/ua/ansymo/hintco/OutputProcessor.xtend

@@ -46,6 +46,7 @@ class OutputProcessor implements IOutputProcessor {
 		Assert.isTrue(outDir.mkdirs())
 		
 		// Open csv files
+		// TODO Has to include Hierarchical Units as well.
 		for (u : scenario.eAllContents.filter(CosimUnitInstance).toIterable){
 			val f = new CSVWriter(new FileWriter(new File(outDirPath + "/" + u.identifier + ".csv")), ";", CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END) 
 			// Write header
@@ -57,6 +58,7 @@ class OutputProcessor implements IOutputProcessor {
 	}
 	
 	override setOutputs(int step, double time, Map<OutputPortInstance, Double> values) {
+		// TODO Account for step 0.
 		outputCounter--
 		if (outputCounter == 0){
 			outputCounter = outputInterval

+ 3 - 3
HintCO/src/ua/ansymo/hintco/VariantProcessor.xtend

@@ -15,7 +15,7 @@ class VariantProcessor implements IVariantProcessor {
 		runner = r
 	}
 	
-	def findScenario(Deque<VariantDiagram> ns){
+	def findRootScenario(Deque<VariantDiagram> ns){
 		// Locate scenario where computation order should be generated. Search ns and it's parents for a CandidateScenario alternative.
 		val scenarios = ns.filter[n | n.alternative instanceof RootCandidateScenario]
 		Assert.isTrue(1 == scenarios.size)
@@ -29,7 +29,7 @@ class VariantProcessor implements IVariantProcessor {
 		 */
 		 
 		 // Locate scenario where computation order should be generated. Search ns and it's parents for a CandidateScenario alternative.
-		 val RootCandidateScenario rootScenario = findScenario(ns)
+		 val RootCandidateScenario rootScenario = findRootScenario(ns)
 		 
 		 val scenarioSortedNodesMap = computeHierarchicalTopologicalSort(rootScenario)
 		 
@@ -105,8 +105,8 @@ class VariantProcessor implements IVariantProcessor {
 	}
 	
 	override process(Deque<VariantDiagram> ns, String variantID, ConstraintsStack constraints, Candidates cs) {
-		val scenario = findScenario(ns)
 		val order = computeOperationOrder(ns, cs)
+		val scenario = findRootScenario(ns)
 		runner.run(scenario, order, variantID)
 	}
 	

+ 7 - 3
HintCO/test/ua/ansymo/hintco/test/AdaptedFMUTest.xtend

@@ -33,6 +33,10 @@ import ua.ansymo.hintco.VariantProcessor
 import ua.ansymo.hintco.VariantValidator
 
 import static org.junit.Assert.*
+import ua.ansymo.hintco.UnitInstance
+import ua.ansymo.hintco.Scenario
+import java.util.List
+import ua.ansymo.hintco.PrecendenceNode
 
 class AdaptedFMUTest {
 	
@@ -48,7 +52,7 @@ class AdaptedFMUTest {
 		val numInnerSteps = new LinkedList()
 		
 		val mockLoader = new IFmuLoader{
-			override instantiate(CosimUnitInstance u) {
+			override instantiate(UnitInstance u, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap) {
 				assertTrue(u===source || u===sink)
 				if (u===source){
 					return new AdaptedFMUInstance(new MockSourceFmuInstance[t, H | 
@@ -94,7 +98,7 @@ class AdaptedFMUTest {
 		val numInnerSteps = new LinkedList()
 		
 		val mockLoader = new IFmuLoader{
-			override instantiate(CosimUnitInstance u) {
+			override instantiate(UnitInstance u, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap) {
 				assertTrue(u===source || u===sink)
 				if (u===source){
 					return new AdaptedFMUInstance(new MockSourceFmuInstance[t, H | 
@@ -141,7 +145,7 @@ class AdaptedFMUTest {
 		val numInnerSteps = new LinkedList()
 		
 		val mockLoader = new IFmuLoader{
-			override instantiate(CosimUnitInstance u) {
+			override instantiate(UnitInstance u, Map<Scenario, List<PrecendenceNode>> scenarioNodesMap) {
 				assertTrue(u===source || u===sink)
 				if (u===source){
 					return new AdaptedFMUInstance(new MockSourceFmuInstance[t, H | 

+ 1 - 1
HintCO/test/ua/ansymo/hintco/test/CandidatesGeneratorTest.xtend

@@ -60,7 +60,7 @@ class CandidatesGeneratorTest {
 				val vp = new VariantProcessor([])
 				val orderMap = vp.computeOperationOrder(ns, cs)
 				
-				val rootScenario = vp.findScenario(ns)
+				val rootScenario = vp.findRootScenario(ns)
 				val topOrder = orderMap.get(rootScenario)
 				
 				comesBefore("B", "C", topOrder, cs)