@@ -869,6 +869,31 @@ def runnerSuffix(name):
869869 publishes the workspace itself, so its files keep the plain names."""
870870 return "_%s" % name if name and runners and name != runners [0 ][0 ] else ""
871871
872+ def runnerDir (name ):
873+ """A working directory of this runner's own, or None when there is only one.
874+
875+ A file the model writes on its first run and reads back on the next --
876+ Buildings' ground temperature-response matrix under `tmp/`, an EnergyPlus
877+ scratch directory -- is otherwise computed by whichever runner goes first and
878+ found already cached by the rest, which reads as the later ones being faster.
879+ The build stays in the directory above and every runner reaches it as `..`, so
880+ the result files stay where the report expects them.
881+ """
882+ if len (runners ) < 2 or not name :
883+ return None
884+ d = "run_%s" % name
885+ if not os .path .isdir (d ):
886+ os .mkdir (d )
887+ return d
888+
889+ def fromRunnerDir (runDir , path ):
890+ """`path`, which names something in the build directory, as a runner sees it."""
891+ return path if not runDir or os .path .isabs (path ) else "../%s" % path
892+
893+ def inRunnerDir (runDir , cmd ):
894+ """`cmd` run in the runner's directory. The pipe and the log stay outside it."""
895+ return "(cd %s && %s)" % (runDir , cmd ) if runDir else cmd
896+
872897def resultFile (name = None ):
873898 """Where a simulator writes its results."""
874899 # Only an FMI tool decides the format; the others write what the model was
@@ -885,23 +910,25 @@ def artifactPrefix(name=None):
885910def simulateFmu (name , command , resFile , simFile ):
886911 """Run the FMU with one simulator, writing what it says to simFile."""
887912 suffix = runnerSuffix (name )
913+ runDir = runnerDir (name )
888914 fmitmpdir = "temp_%s%s_fmu" % (conf ["fileName" ].replace ("." ,"_" ), suffix )
889915 with open ("%s.tmpfiles" % conf ["fileName" ], "a+" ) as fp :
890- fp .write ("%s\n " % fmitmpdir )
916+ fp .write ("%s\n " % ( os . path . join ( runDir , fmitmpdir ) if runDir else fmitmpdir ) )
891917 cmd = shared .fmiSimulatorCommand (name , command ,
892- fmu = "%s.fmu" % conf ["fileName" ].replace ("." ,"_" ),
893- result = resFile ,
894- requestedResult = resFile if outputFormat != "empty" else "" ,
918+ fmu = fromRunnerDir ( runDir , "%s.fmu" % conf ["fileName" ].replace ("." ,"_" ) ),
919+ result = fromRunnerDir ( runDir , resFile ) ,
920+ requestedResult = fromRunnerDir ( runDir , resFile ) if outputFormat != "empty" else "" ,
895921 tempDir = fmitmpdir , startTime = startTime , stopTime = stopTime ,
896922 tolerance = tolerance , timeout = conf ["ulimitExe" ],
897923 stepSize = stepSize )
924+ cmd = inRunnerDir (runDir , cmd )
898925 with open (simFile ,"w" ) as fp :
899926 fp .write ("%s\n " % cmd )
900927 pipe = "%s%s" % (conf ["fileName" ], suffix )
901928 return checkOutputTimeout ("(rm -f %s.pipe ; mkfifo %s.pipe ; head -c 1048576 < %s.pipe >> %s & %s > %s.pipe 2>&1)"
902929 % (pipe ,pipe ,pipe ,simFile ,cmd ,pipe ), 1.05 * conf ["ulimitExe" ], conf )
903930
904- def wasmFmuCmd (runnerFlags , resFile ):
931+ def wasmFmuCmd (runnerFlags , resFile , runDir ):
905932 """The simulate() that runs the exported FMU one way.
906933
907934 Nothing is translated: `resimulateExecutable` points at the FMU, `-s
@@ -910,43 +937,60 @@ def wasmFmuCmd(runnerFlags, resFile):
910937 """
911938 # An empty output format is a run with nothing to compare against, so it is
912939 # asked for no result file at all rather than one nobody reads.
913- resultArgument = "-noemit" if outputFormat == "empty" else "-r=%s" % resFile
940+ resultArgument = "-noemit" if outputFormat == "empty" else "-r=%s" % fromRunnerDir ( runDir , resFile )
914941 # The export baked in no filter, and the variableFilter argument below only
915942 # reaches a model through the build this run skips.
916943 filterArgument = "" if variableFilter in ("" , ".*" ) else "-variableFilter=%s" % variableFilter
917944 simflags = " " .join (x for x in (annotationSimFlags , conf ["simFlags" ], emit_protected ,
918945 "-lv LOG_STATS" ,
919946 "-startTime=%g -stopTime=%g -tolerance=%g -stepSize=%g" % (startTime ,stopTime ,tolerance ,stepSize ),
920947 filterArgument , resultArgument , runnerFlags ) if x .strip ())
921- return 'simulate(%s,startTime=%g,stopTime=%g,tolerance=%g,numberOfIntervals=%d,outputFormat="%s",variableFilter="%s",fileNamePrefix="%s",simflags="%s",resimulateExecutable="%s.fmu ")' % (
922- conf ["modelName" ],startTime ,stopTime ,tolerance ,numberOfIntervals ,outputFormat ,variableFilter ,conf ["fileName" ],simflags ,conf ["fileName" ].replace ("." ,"_" ))
948+ return 'simulate(%s,startTime=%g,stopTime=%g,tolerance=%g,numberOfIntervals=%d,outputFormat="%s",variableFilter="%s",fileNamePrefix="%s",simflags="%s",resimulateExecutable="%s")' % (
949+ conf ["modelName" ],startTime ,stopTime ,tolerance ,numberOfIntervals ,outputFormat ,variableFilter ,conf ["fileName" ],simflags ,fromRunnerDir ( runDir , "%s.fmu" % conf ["fileName" ].replace ("." ,"_" ) ))
923950
924951def simulateWasmFmu (name , runnerFlags , resFile , simFile ):
925952 """Run the FMU one way, writing what omc says to simFile.
926953
927954 Returns what simulate() answered; an empty resultFile is a failed run.
928955 """
929- cmd = wasmFmuCmd (runnerFlags , resFile )
956+ runDir = runnerDir (name )
957+ cmd = wasmFmuCmd (runnerFlags , resFile , runDir )
930958 # The export is a directory, and a zipped one unpacks itself beside the .fmu on
931959 # the first run; the cleanup removes what this file names.
932960 with open ("%s.tmpfiles" % conf ["fileName" ], "a+" ) as fp :
933961 fp .write ("%s.fmu\n %s_artifact\n " % (conf ["fileName" ].replace ("." ,"_" ), conf ["fileName" ].replace ("." ,"_" )))
934962 with open (simFile , "w" ) as fp :
935963 fp .write ("startTime=%g\n stopTime=%g\n tolerance=%g\n numberOfIntervals=%d\n stepSize=%g\n " % (startTime ,stopTime ,tolerance ,numberOfIntervals ,stepSize ))
936964 fp .write ("wasm FMU (%s: %s): %s\n " % (name , shared .wasmFmuRunner (name ).get ("description" ) or "" , cmd ))
937- res = sendExpressionTimeout (omc , cmd , conf ["ulimitExe" ]) or {}
965+ try :
966+ if runDir :
967+ sendExpressionTimeout (omc , 'cd("%s")' % runDir , conf ["ulimitOmc" ])
968+ res = sendExpressionTimeout (omc , cmd , conf ["ulimitExe" ]) or {}
969+ finally :
970+ # A run that timed out took omc with it; there is then nothing to change back.
971+ if runDir :
972+ try :
973+ sendExpressionTimeout (omc , 'cd("..")' , conf ["ulimitOmc" ])
974+ except Exception :
975+ pass
938976 with open (simFile , "a+" ) as fp :
939977 fp .write (res .get ("messages" ) or "" )
940978 return res
941979
942980def simulateExecutable (name , solverFlags , resFile , simFile ):
943981 """Run the built executable once, with the flags of one solver."""
944- exe = ".\\ %s.bat" % conf ["fileName" ] if isWin else "./%s" % conf ["fileName" ]
982+ runDir = runnerDir (name )
983+ exeName = "%s.bat" % conf ["fileName" ] if isWin else conf ["fileName" ]
984+ exe = fromRunnerDir (runDir , exeName ) if runDir else (".\\ %s" % exeName if isWin else "./%s" % exeName )
945985 # A run sharing the directory with other solvers needs a result file of its own.
946- resultArgument = "-r=%s" % resFile if runnerSuffix (name ) and outputFormat != "empty" else ""
947- cmd = " " .join (x for x in (exe , annotationSimFlags , conf ["simFlags" ], emit_protected ,
986+ # A run in a directory of its own needs one named explicitly either way: the
987+ # default lands beside the executable, which is no longer the working directory.
988+ resultArgument = "-r=%s" % fromRunnerDir (runDir , resFile ) if (runDir or runnerSuffix (name )) and outputFormat != "empty" else ""
989+ # `-f` because the setup XML the executable reads by default is beside it too.
990+ setupArgument = "-f=%s" % fromRunnerDir (runDir , "%s_init.xml" % conf ["fileName" ]) if runDir else ""
991+ cmd = inRunnerDir (runDir , " " .join (x for x in (exe , annotationSimFlags , conf ["simFlags" ], emit_protected ,
948992 "-lv LOG_STATS" if conf ["simCodeTarget" ] in ("C" ,"C+Rust" ) else "" ,
949- resultArgument , solverFlags ) if x .strip ())
993+ setupArgument , resultArgument , solverFlags ) if x .strip () ))
950994 with open (simFile ,"w" ) as fp :
951995 fp .write ("Environment - simulationEnvironment:\n " )
952996 for e in conf ["environmentSimulation" ]:
0 commit comments