diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java index 5e606862..5743b9d6 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/extensions/fmi3/Fmi3Interpreter.java @@ -78,6 +78,10 @@ public class Fmi3Interpreter { static final ExternalReflectCallHelper.ArgMapping intArrayOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Int, 2, ExternalReflectCallHelper.ArgMapping.InOut.Output, null); + static final ExternalReflectCallHelper.ArgMapping boolOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Bool, 1, + ExternalReflectCallHelper.ArgMapping.InOut.Output, null); + static final ExternalReflectCallHelper.ArgMapping doubleOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Real, 1, + ExternalReflectCallHelper.ArgMapping.InOut.Output, null); final static Logger logger = LoggerFactory.getLogger(Interpreter.class); private final File workingDirectory; @@ -529,18 +533,19 @@ private static Value getFmuInstanceValue(BufferedOutputStream fmuLogOutputStream })); functions.put("updateDiscreteStates", new FunctionValue.ExternalFunctionValue(fcargs -> { -// int updateDiscreteStates(out bool[] discreteStatesNeedUpdate, out bool[] terminateSimulation, -// out bool[] nominalsOfContinuousStatesChanged, out bool[] valuesOfContinuousStatesChanged, out bool[] nextEventTimeDefined, -// out real[] nextEventTime); +// int updateDiscreteStates(out bool discreteStatesNeedUpdate, out bool terminateSimulation, +// out bool nominalsOfContinuousStatesChanged, out bool valuesOfContinuousStatesChanged, out bool nextEventTimeDefined, +// out real nextEventTime); checkArgLength(fcargs, 6); try { FmuResult res = instance.updateDiscreteStates(); - boolArrayOutArgMapper.mapOut(fcargs.get(0), new boolean[]{res.result.isDiscreteStatesNeedUpdate()}); - boolArrayOutArgMapper.mapOut(fcargs.get(1), new boolean[]{res.result.isTerminateSimulation()}); - boolArrayOutArgMapper.mapOut(fcargs.get(2), new boolean[]{res.result.isNominalsOfContinuousStatesChanged()}); - boolArrayOutArgMapper.mapOut(fcargs.get(3), new boolean[]{res.result.isValuesOfContinuousStatesChanged()}); - boolArrayOutArgMapper.mapOut(fcargs.get(4), new boolean[]{res.result.isNextEventTimeDefined()}); - doubleArrayOutArgMapper.mapOut(fcargs.get(5), new double[]{res.result.getNextEventTime()}); + + boolOutArgMapper.mapOut(fcargs.get(0), res.result.isDiscreteStatesNeedUpdate()); + boolOutArgMapper.mapOut(fcargs.get(1), res.result.isTerminateSimulation()); + boolOutArgMapper.mapOut(fcargs.get(2), res.result.isNominalsOfContinuousStatesChanged()); + boolOutArgMapper.mapOut(fcargs.get(3), res.result.isValuesOfContinuousStatesChanged()); + boolOutArgMapper.mapOut(fcargs.get(4), res.result.isNextEventTimeDefined()); + doubleOutArgMapper.mapOut(fcargs.get(5), res.result.getNextEventTime()); return status2IntValue(res.status); } catch (FmuInvocationException e) { throw new InterpreterException(e); @@ -691,7 +696,11 @@ private static Value getFmuInstanceValue(BufferedOutputStream fmuLogOutputStream checkArgLength(fcargs, 1); try { FmuResult res = instance.getNumberOfEventIndicators(); - intArrayOutArgMapper.mapOut(fcargs.get(0), res.result); + final ExternalReflectCallHelper.ArgMapping uintOutArgMapper = new ExternalReflectCallHelper.ArgMapping(TP.Long, 1, + ExternalReflectCallHelper.ArgMapping.InOut.Output, null); + uintOutArgMapper.mapOut(fcargs.get(0), res.result); + UpdatableValue v= (UpdatableValue) fcargs.get(0); + v.setValue(new UnsignedIntegerValue(((LongValue)v.deref()).getValue())); return status2IntValue(res.status); } catch (FmuInvocationException e) { throw new InterpreterException(e); diff --git a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java index 0c36f6b3..cd6025b9 100644 --- a/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java +++ b/interpreter/src/main/java/org/intocps/maestro/interpreter/external/ExternalReflectCallHelper.java @@ -469,6 +469,36 @@ public void mapOut(Value original, Object value) { break; } ref.setValue(new ArrayValue<>(values)); + }else if(dimension==1) + { + Value mappedValue = null; + switch (type){ + case Bool: + mappedValue=new BooleanValue(value.getClass().isPrimitive()?(boolean)value:(Boolean)value); + break; + case Byte: + mappedValue=new ByteValue(value.getClass().isPrimitive()?(byte)value:(Byte)value); + break; + case Float: + mappedValue=new FloatValue(value.getClass().isPrimitive()?(float)value:(Float)value); + break; + case Int: + mappedValue=new IntegerValue(value.getClass().isPrimitive()?(int)value:(Integer)value); + break; + case Long: + mappedValue=new LongValue(value.getClass().isPrimitive()?(long)value:(Long)value); + break; + case Real: + mappedValue=new RealValue(value.getClass().isPrimitive()?(double)value:(Double)value); + break; + case Short: + mappedValue=new ShortValue(value.getClass().isPrimitive()?(short)value:(Short)value); + break; + case String: + mappedValue=new StringValue((String)value); + break; + } + ref.setValue(mappedValue); } } diff --git a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java index 5cbe0154..acf4b22b 100644 --- a/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java +++ b/maestro/src/test/java/org/intocps/maestro/fmi3/BuilderFmi3Test.java @@ -3,7 +3,6 @@ import org.antlr.v4.runtime.CharStreams; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.intocps.fmi.FmuInvocationException; import org.intocps.fmi.jnifmuapi.fmi3.Fmu3; import org.intocps.maestro.Mabl; import org.intocps.maestro.ast.display.PrettyPrinter; @@ -11,14 +10,12 @@ import org.intocps.maestro.core.Framework; import org.intocps.maestro.core.messages.ErrorReporter; import org.intocps.maestro.core.messages.IErrorReporter; -import org.intocps.maestro.fmi.fmi3.Fmi3Causality; -import org.intocps.maestro.fmi.fmi3.Fmi3ModelDescription; -import org.intocps.maestro.fmi.fmi3.Fmi3TypeEnum; +import org.intocps.maestro.fmi.fmi3.*; import org.intocps.maestro.framework.fmi2.api.FmiBuilder; +import org.intocps.maestro.framework.fmi2.api.mabl.LoggerFmi2Api; import org.intocps.maestro.framework.fmi2.api.mabl.MablApiBuilder; import org.intocps.maestro.framework.fmi2.api.mabl.PortFmi3Api; import org.intocps.maestro.framework.fmi2.api.mabl.scoping.DynamicActiveBuilderScope; -import org.intocps.maestro.framework.fmi2.api.mabl.scoping.IfMaBlScope; import org.intocps.maestro.framework.fmi2.api.mabl.values.BooleanExpressionValue; import org.intocps.maestro.framework.fmi2.api.mabl.values.IntExpressionValue; import org.intocps.maestro.framework.fmi2.api.mabl.variables.*; @@ -29,6 +26,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; import java.io.File; import java.io.IOException; @@ -39,6 +38,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import java.util.stream.Collectors; public class BuilderFmi3Test { @@ -48,7 +48,7 @@ public static void before() throws IOException { } - public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, String name, URI uri) throws Exception { + public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, String name, URI uri, boolean eventModeUsed) throws Exception { Fmi3ModelDescription md = new Fmi3ModelDescription(new Fmu3(new File(uri)).getModelDescription()); @@ -56,7 +56,7 @@ public static InstanceVariableFmi3Api createInstance(MablApiBuilder builder, Str boolean visible = true; boolean loggingOn = true; - boolean eventModeUsed = true; + boolean earlyReturnAllowed = true; ArrayVariableFmi2Api requiredIntermediateVariables = builder.getDynamicScope().store("requiredIntermediateVariables", new Long[]{1L}); InstanceVariableFmi3Api instance = @@ -70,9 +70,9 @@ public void test() throws Exception { MablApiBuilder builder = new MablApiBuilder(); InstanceVariableFmi3Api fd = createInstance(builder, "fd", - new File("target/Fmi3ModuleReferenceFmusTest/cache/Feedthrough.fmu").getAbsoluteFile().toURI()); + new File("target/Fmi3ModuleReferenceFmusTest/cache/Feedthrough.fmu").getAbsoluteFile().toURI(), false); InstanceVariableFmi3Api sg = createInstance(builder, "sg", - new File("src/test/resources/fmi3/reference/siggen-feedthrough/SignalGenerator.fmu").getAbsoluteFile().toURI()); + new File("src/test/resources/fmi3/reference/siggen-feedthrough/SignalGenerator.fmu").getAbsoluteFile().toURI(), false); // fd.enterInitializationMode(false, 0.0, 0.0, true, 10.0); @@ -113,8 +113,9 @@ public void testClocks() throws Exception { DynamicActiveBuilderScope scope = builder.getDynamicScope(); InstanceVariableFmi3Api instance = createInstance(builder, "clocks", - new File("target/Fmi3ModuleReferenceFmusTest/cache/Clocks.fmu").getAbsoluteFile().toURI()); + new File("src/test/resources/fmi3/sinewave_array.fmu").getAbsoluteFile().toURI(), true); + //we are not in event mode as eventModeUsed was true // fd.enterInitializationMode(false, 0.0, 0.0, true, 10.0); // sg.enterInitializationMode(false, 0.0, 0.0, true, 10.0); @@ -129,7 +130,7 @@ public void testClocks() throws Exception { instance.enterEventMode(); - FmiBuilder.IntVariable nEventIndicators = builder.getDynamicScope().store(0); + FmiBuilder.UIntVariable nEventIndicators = builder.getDynamicScope().storeUInt(0); instance.getNumberOfEventIndicators(builder.getDynamicScope(), nEventIndicators); @@ -198,13 +199,17 @@ public void testClocks() throws Exception { } @Test + @EnabledOnOs(OS.LINUX) public void testSimulateClocks() throws Exception { MablApiBuilder builder = new MablApiBuilder(); DynamicActiveBuilderScope scope = builder.getDynamicScope(); + var log = builder.getLogger(); - InstanceVariableFmi3Api instance = createInstance(builder, "clocks", - new File("target/Fmi3ModuleReferenceFmusTest/cache/Clocks.fmu").getAbsoluteFile().toURI()); + InstanceVariableFmi3Api instance = createInstance(builder, "i", + new File("src/test/resources/fmi3/periodic_clock.fmu").getAbsoluteFile().toURI(), true); + //we are not in event mode as eventModeUsed was true + log.log(LoggerFmi2Api.Level.INFO, "Instantiated"); // fd.enterInitializationMode(false, 0.0, 0.0, true, 10.0); // sg.enterInitializationMode(false, 0.0, 0.0, true, 10.0); @@ -217,17 +222,22 @@ public void testSimulateClocks() throws Exception { sgOutputs.stream().map(PortFmi3Api::getName).forEach(System.out::println); - instance.enterEventMode(); +// log.log(LoggerFmi2Api.Level.INFO,"Enter event mode again"); +// instance.enterEventMode(); - FmiBuilder.IntVariable nEventIndicators = builder.getDynamicScope().store(0); - instance.getNumberOfEventIndicators(builder.getDynamicScope(), nEventIndicators); - - ArrayVariableFmi2Api eventIndicators = builder.getDynamicScope() - .createArray("eventIndicators", UIntVariableFmi2Api.class, nEventIndicators); - -// FmiBuilder.ArrayVariable eventIndicators=builder.getDynamicScope().store(builder.n).storeInArray(); - instance.getEventIndicators(scope, eventIndicators, nEventIndicators); +// FmiBuilder.UIntVariable nEventIndicators = builder.getDynamicScope().storeUInt(0L); +// +// log.log(LoggerFmi2Api.Level.INFO,"getNumberOfEventIndicators"); +// instance.getNumberOfEventIndicators(builder.getDynamicScope(), nEventIndicators); +// +// +// ArrayVariableFmi2Api eventIndicators = builder.getDynamicScope() +// .createArray("eventIndicators", UIntVariableFmi2Api.class, nEventIndicators); +// +//// FmiBuilder.ArrayVariable eventIndicators=builder.getDynamicScope().store(builder.n).storeInArray(); +// log.log(LoggerFmi2Api.Level.INFO,"getEventIndicators"); +// instance.getEventIndicators(scope, eventIndicators, nEventIndicators); List outClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Output) @@ -238,14 +248,16 @@ public void testSimulateClocks() throws Exception { vrs.setValue(new IntExpressionValue(i), new IntExpressionValue((int) outClocks.get(i).scalarVariable.getVariable().getValueReferenceAsLong())); } ArrayVariableFmi2Api triggeredClocks = scope.createArray("clock_get_vrs", BooleanVariableFmi2Api.class, nvr); + //this shows us the clock state of the output clocks + log.log(LoggerFmi2Api.Level.INFO, "getClock"); instance.getClock(vrs, nvr, triggeredClocks); List inClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input) .collect(Collectors.toList()); FmiBuilder.IntVariable clock_set_nvr = scope.store("clock_in_nvr", inClocks.size()); - ArrayVariableFmi2Api clock_set_vrs = scope.createArray("clock_in_vrs", UIntVariableFmi2Api.class, nvr); - ArrayVariableFmi2Api clock_set_Clocks = scope.createArray("clock_in_vrs", BooleanVariableFmi2Api.class, nvr); + ArrayVariableFmi2Api clock_set_vrs = scope.createArray("clock_in_vrs", UIntVariableFmi2Api.class, clock_set_nvr); + ArrayVariableFmi2Api clock_set_Clocks = scope.createArray("clock_in_vrs_values", BooleanVariableFmi2Api.class, clock_set_nvr); for (int i = 0; i < inClocks.size(); i++) { clock_set_vrs.setValue(new IntExpressionValue(i), new IntExpressionValue((int) inClocks.get(i).scalarVariable.getVariable().getValueReferenceAsLong())); @@ -253,51 +265,81 @@ public void testSimulateClocks() throws Exception { } instance.setClock(clock_set_vrs, clock_set_nvr, clock_set_Clocks); + instance.setDebugLogging(instance.getModelDescription().getLogCategories().stream().map(lc -> lc.getName()).collect(Collectors.toList()), true); - FmiBuilder.DoubleVariable currentCommunicationPoint = scope.store("time", 0d); - FmiBuilder.DoubleVariable stepSize = scope.store("step", 0.1d); - Map.Entry, InstanceVariableFmi3Api.StepResult> stepRes = instance.step(scope, currentCommunicationPoint, - stepSize, new ABoolLiteralExp(false)); + //initialize + instance.enterInitializationMode(false, 0.0, 0.0, true, 10d); - IfMaBlScope eventHandlingScope = scope.enterIf( - stepRes.getValue().getEventHandlingNeeded().toPredicate()); + instance.exitInitializationMode(); - instance.enterEventMode(); + //prepare for event handling + BooleanVariableFmi2Api stopSimulation = scope.store(false); + + BooleanVariableFmi2Api discreteStatesNeedUpdate = scope.store("discreteStatesNeedUpdate", true); + BooleanVariableFmi2Api terminateSimulation = scope.store("terminateSimulation", false); + BooleanVariableFmi2Api nominalsOfContinuousStatesChanged = scope.store("nominalsOfContinuousStatesChanged", false); + BooleanVariableFmi2Api valuesOfContinuousStatesChanged = scope.store("valuesOfContinuousStatesChanged", false); + BooleanVariableFmi2Api nextEventTimeDefined = scope.store("nextEventTimeDefined", false); + DoubleVariableFmi2Api nextEventTime = scope.store("nextEventTime", 0d); + + Supplier updateDiscreteStates=()->{ + scope.enterWhile(discreteStatesNeedUpdate.toPredicate()); + instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, + nextEventTimeDefined, nextEventTime); -//handle events - BooleanVariableFmi2Api discreteStatesNeedUpdate = scope.store("discreteStatesNeedUpdate",false); - BooleanVariableFmi2Api terminateSimulation = scope.store("terminateSimulation",false); - BooleanVariableFmi2Api nominalsOfContinuousStatesChanged = scope.store("nominalsOfContinuousStatesChanged",false); - BooleanVariableFmi2Api valuesOfContinuousStatesChanged = scope.store("valuesOfContinuousStatesChanged",false); - BooleanVariableFmi2Api nextEventTimeDefined = scope.store("nextEventTimeDefined",false); - DoubleVariableFmi2Api nextEventTime = scope.store("nextEventTime",0d); - instance.updateDiscreteStates(scope, discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, - nextEventTimeDefined, nextEventTime); + scope.enterIf(terminateSimulation.toPredicate()); + stopSimulation.setValue(scope,BooleanExpressionValue.of(true)); + scope.add(new ABreakStm()); + scope.leave(); + scope.leave(); + return scope; + }; + + //handle initial events + updateDiscreteStates.get(); + + //switch to step mode instance.enterStepMode(); + List periodicConstInClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval()== Fmi3ClockInterval.Constant) + .collect(Collectors.toList()); + List periodicConstInClocksInterval = clocks.stream().filter(p -> p.scalarVariable.getVariable().getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval()== Fmi3ClockInterval.Constant) + .map(p->((ClockVariable) p.scalarVariable.getVariable()).getIntervalDecimal()) .collect(Collectors.toList()); -// stepRes.getValue().getLastSuccessfulTime() + List constantPeriodicClocks = clocks.stream().filter(p -> p.scalarVariable.getVariable() + .getCausality() == Fmi3Causality.Input && p.scalarVariable.getVariable() instanceof ClockVariable && ((ClockVariable) p.scalarVariable.getVariable()).getInterval() == Fmi3ClockInterval.Constant) + .collect( + Collectors.toList()); -// instance.g -// for (PortFmi3Api o : sgOutputs) { -// sg.get(o); -// } + DoubleVariableFmi2Api currentCommunicationPoint = scope.store("time", 0d); + DoubleVariableFmi2Api endTime = scope.store(4d); -// System.out.println("Linked ports"); -// sg.getPorts().stream().filter(PortFmi3Api::isLinked).forEach(System.out::println); -// System.out.println("---Linked ports"); -// sg.getPort("Int8_output").linkTo(fd.getPort("Int8_input")); -// sg.getPort("UInt8_output").linkTo(fd.getPort("UInt8_input")); -// System.out.println("Linked ports"); -// sg.getPorts().stream().filter(PortFmi3Api::isLinked).forEach(System.out::println); -// System.out.println("---Linked ports"); -// sg.getAndShare(); -// fd.setLinked(); + //loop for co-simulation + scope.enterWhile(terminateSimulation.toPredicate().not().and(currentCommunicationPoint.toMath().lessThan(endTime))); -// fd.exitInitializationMode(); + //determine a step size + DoubleVariableFmi2Api stepSize = scope.store("stepSize", Collections.min(periodicConstInClocksInterval)); + + +// //step the instance + Map.Entry, InstanceVariableFmi3Api.StepResult> stepRes = instance.step(scope, currentCommunicationPoint, + stepSize, new ABoolLiteralExp(false)); + + currentCommunicationPoint.setValue(currentCommunicationPoint.toMath().addition(stepSize)); + + //handle events of required + scope.enterIf( + stepRes.getValue().getEventHandlingNeeded().toPredicate()); + instance.enterEventMode(); + updateDiscreteStates.get(); + + //exit to step mode + instance.enterStepMode(); + + //program done; ASimulationSpecificationCompilationUnit program = builder.build(); @@ -305,15 +347,15 @@ public void testSimulateClocks() throws Exception { System.out.println(PrettyPrinter.printLineNumbers(program)); - File workingDirectory=getWorkingDirectory(null,this.getClass()); + File workingDirectory = getWorkingDirectory(null, this.getClass()); File specFile = new File(workingDirectory, "spec.mabl"); - FileUtils.write(specFile,PrettyPrinter.print(program),StandardCharsets.UTF_8); + FileUtils.write(specFile, PrettyPrinter.print(program), StandardCharsets.UTF_8); IErrorReporter reporter = new ErrorReporter(); Mabl mabl = new Mabl(workingDirectory, workingDirectory); mabl.setReporter(reporter); // mabl.setVerbose(getMablVerbose()); -mabl.parse(Collections.singletonList(specFile)); + mabl.parse(Collections.singletonList(specFile)); mabl.expand(); var tcRes = mabl.typeCheck(); mabl.verify(Framework.FMI2); @@ -331,10 +373,12 @@ public void testSimulateClocks() throws Exception { Map types = tcRes.getValue(); new MableInterpreter(new DefaultExternalValueFactory(workingDirectory, name -> TypeChecker.findModule(types, name), - IOUtils.toInputStream(mabl.getRuntimeDataAsJsonString(), StandardCharsets.UTF_8))).execute(MablParserUtil.parse(CharStreams.fromString(PrettyPrinter.print(program)))); + IOUtils.toInputStream(mabl.getRuntimeDataAsJsonString(), StandardCharsets.UTF_8))).execute( + MablParserUtil.parse(CharStreams.fromString(PrettyPrinter.print(program)))); } + static File getWorkingDirectory(File base, Class cls) throws IOException { - String s = Paths.get("target", cls.getSimpleName()).toString() + File.separatorChar + (base==null?"":base.getAbsolutePath().substring( + String s = Paths.get("target", cls.getSimpleName()).toString() + File.separatorChar + (base == null ? "" : base.getAbsolutePath().substring( base.getAbsolutePath().replace(File.separatorChar, '/').indexOf("src/test/resources/") + ("src" + "/test" + "/resources/").length())); File workingDir = new File(s.replace('/', File.separatorChar)); diff --git a/maestro/src/test/resources/fmi3/periodic_clock.fmu b/maestro/src/test/resources/fmi3/periodic_clock.fmu new file mode 100644 index 00000000..b71c3c94 Binary files /dev/null and b/maestro/src/test/resources/fmi3/periodic_clock.fmu differ diff --git a/maestro/src/test/resources/fmi3/sinewave_array.fmu b/maestro/src/test/resources/fmi3/sinewave_array.fmu new file mode 100644 index 00000000..0d91d667 Binary files /dev/null and b/maestro/src/test/resources/fmi3/sinewave_array.fmu differ