Module MAPLEAF.Main
Script to run flight simulations from the command line If MAPLEAF has been installed with pip, this script is accessible through the 'mapleaf' command
Expand source code
'''
Script to run flight simulations from the command line
If MAPLEAF has been installed with pip, this script is accessible through the 'mapleaf' command
'''
import argparse
import os
import sys
import time
from pathlib import Path
from typing import List
import MAPLEAF.IO.Logging as Logging
import MAPLEAF.IO.Plotting as Plotting
from MAPLEAF.IO import SimDefinition, getAbsoluteFilePath
from MAPLEAF.SimulationRunners import (ConvergenceSimRunner, Simulation,
optimizationRunnerFactory,
runMonteCarloSimulation)
from MAPLEAF.SimulationRunners.Batch import main as batchMain
def buildParser() -> argparse.ArgumentParser:
''' Builds the command-line argument parser using argparse '''
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description="""
Run individual MAPLEAF simulations.
Expects simulations to be defined by simulation definition files like those in ./MAPLEAF/Examples/Simulations
See ./SimDefinitionTemplate.mapleaf for definition of all possible options
""")
mutexGroup = parser.add_mutually_exclusive_group()
mutexGroup.add_argument(
"--converge",
action='store_true',
help="Runs the current simulation using successively finer time steps, attempting to provide a converged final location"
)
mutexGroup.add_argument(
"--compareIntegrationSchemes",
action='store_true',
help="Attempts to converge the current simulation using a variety of classical integration schemes."
)
mutexGroup.add_argument(
"--compareAdaptiveIntegrationSchemes",
action='store_true',
help="Attempts to converge the current simulation using a variety of adaptive time integration schemes"
)
mutexGroup.add_argument(
"--plotFromLog",
nargs=2,
default=[],
metavar=("plotDefinition", "pathToLogFile"),
help="plotDefinition works the same way as SimControl.plot entries in simulation definition files"
)
parser.add_argument(
"--parallel",
action='store_true',
help="Use to run Monte Carlo or Optimization studies in parallel using ray. Check whether ray's Windows support has exited alpha, or use only on Linux/Mac."
)
parser.add_argument(
"--silent",
action='store_true',
help="If present, does not output to console - faster on windows"
)
parser.add_argument(
"simDefinitionFile",
nargs='?',
default="NASATwoStageOrbitalRocket.mapleaf",
help="Path to a simulation definition (.mapleaf) file. Not required if using --plotFromLog"
)
return parser
def findSimDefinitionFile(providedPath):
# It is already a path, just return it
if os.path.isfile(providedPath):
return providedPath
# Track if it is a relative path, relative to a different location than the current terminal (example/default cases)
installationLocation = Path(__file__).parent.parent
alternateLocations = [
installationLocation / "MAPLEAF/Examples/Simulations/",
installationLocation / "MAPLEAF/Examples/BatchSims/"
]
possibleRelativePaths = [ providedPath ]
if len(providedPath) < 8 or providedPath[-8:] != ".mapleaf":
# If it's just the case name (ex: 'Staging') try also adding the file extension
possibleRelativePaths = [ providedPath, providedPath + ".mapleaf" ]
for path in possibleRelativePaths:
for alternateLocation in alternateLocations:
absPath = getAbsoluteFilePath(path, alternateLocation, silent=True)
if os.path.isfile(absPath):
# We've located the file!
return absPath
print("ERROR: Unable to locate simulation definition file: {}! Checked whether the path was relative to the current command line location, the MAPLEAF installation directory, or one of the example cases. To be sure that your file will be found, try using an absolute path.".format(providedPath))
sys.exit()
def isOptimizationProblem(simDefinition) -> bool:
try:
simDefinition.getValue("Optimization.costFunction")
return True
except KeyError:
return False
def isMonteCarloSimulation(simDefinition) -> bool:
try:
nRuns = float(simDefinition.getValue("MonteCarlo.numberRuns"))
if nRuns > 1:
return True
except (KeyError, ValueError):
pass
return False
def isBatchSim(batchDefinition) -> bool:
''' Checks whether the file does not contain a 'Rocket' dictionary, and instead contains dictionaries that have a simDefinitionFile key '''
rootDicts = batchDefinition.getImmediateSubDicts("")
for rootDict in rootDicts:
if rootDict == 'Rocket' and 'Rocket.simDefinitionFile' not in batchDefinition:
return False
return True
def main(argv=None) -> int:
'''
Main function to run a MAPLEAF simulation.
Expects to be called from the command line, usually using the `mapleaf` command
For testing purposes, can also pass a list of command line arguments into the argv parameter
'''
startTime = time.time()
# Parse command line call, check for errors
parser = buildParser()
args = parser.parse_args(argv)
if len(args.plotFromLog):
# Just plot a column from a log file, and not run a whole simulation
Plotting.plotFromLogFiles([args.plotFromLog[1]], args.plotFromLog[0])
print("Exiting")
sys.exit()
# Load simulation definition file
simDefPath = findSimDefinitionFile(args.simDefinitionFile)
simDef = SimDefinition(simDefPath)
#### Run simulation(s) ####
if args.parallel:
try:
import ray
except:
print("""
Error importing ray.
Ensure ray is installed (`pip install -U ray`) and importable (`import ray` should not throw an error).
If on windows, consider trying Linux or running in WSL, at the time this was written, ray on windows was still in beta and unreliable.
Alternatively, run without parallelization.
""")
if isOptimizationProblem(simDef):
optSimRunner = optimizationRunnerFactory(simDefinition=simDef, silent=args.silent, parallel=args.parallel)
optSimRunner.runOptimization()
elif isMonteCarloSimulation(simDef):
if not args.parallel:
nCores = 1
else:
import multiprocessing
nCores = multiprocessing.cpu_count()
runMonteCarloSimulation(simDefinition=simDef, silent=args.silent, nCores=nCores)
elif args.parallel:
raise ValueError("ERROR: Can only run Monte Carlo of Optimization-type simulations in multi-threaded mode. Support for multi-threaded batch simulations coming soon.")
elif isBatchSim(simDef):
print("Batch Simulation\n")
batchMain([ simDef.fileName ])
elif args.converge or args.compareIntegrationSchemes or args.compareAdaptiveIntegrationSchemes:
cSimRunner = ConvergenceSimRunner(simDefinition=simDef, silent=args.silent)
if args.converge:
cSimRunner.convergeSimEndPosition()
elif args.compareIntegrationSchemes:
cSimRunner.compareClassicalIntegrationSchemes(convergenceResultFilePath='convergenceResult.csv')
elif args.compareAdaptiveIntegrationSchemes:
cSimRunner.compareAdaptiveIntegrationSchemes(convergenceResultFilePath='adaptiveConvergenceResult.csv')
else:
# Run a regular, single simulation
sim = Simulation(simDefinition=simDef, silent=args.silent)
sim.run()
Logging.removeLogger()
print("Run time: {:1.2f} seconds".format(time.time() - startTime))
print("Exiting")
if __name__ == "__main__":
main()
Functions
def buildParser() ‑> argparse.ArgumentParser
-
Builds the command-line argument parser using argparse
Expand source code
def buildParser() -> argparse.ArgumentParser: ''' Builds the command-line argument parser using argparse ''' parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=""" Run individual MAPLEAF simulations. Expects simulations to be defined by simulation definition files like those in ./MAPLEAF/Examples/Simulations See ./SimDefinitionTemplate.mapleaf for definition of all possible options """) mutexGroup = parser.add_mutually_exclusive_group() mutexGroup.add_argument( "--converge", action='store_true', help="Runs the current simulation using successively finer time steps, attempting to provide a converged final location" ) mutexGroup.add_argument( "--compareIntegrationSchemes", action='store_true', help="Attempts to converge the current simulation using a variety of classical integration schemes." ) mutexGroup.add_argument( "--compareAdaptiveIntegrationSchemes", action='store_true', help="Attempts to converge the current simulation using a variety of adaptive time integration schemes" ) mutexGroup.add_argument( "--plotFromLog", nargs=2, default=[], metavar=("plotDefinition", "pathToLogFile"), help="plotDefinition works the same way as SimControl.plot entries in simulation definition files" ) parser.add_argument( "--parallel", action='store_true', help="Use to run Monte Carlo or Optimization studies in parallel using ray. Check whether ray's Windows support has exited alpha, or use only on Linux/Mac." ) parser.add_argument( "--silent", action='store_true', help="If present, does not output to console - faster on windows" ) parser.add_argument( "simDefinitionFile", nargs='?', default="NASATwoStageOrbitalRocket.mapleaf", help="Path to a simulation definition (.mapleaf) file. Not required if using --plotFromLog" ) return parser
def findSimDefinitionFile(providedPath)
-
Expand source code
def findSimDefinitionFile(providedPath): # It is already a path, just return it if os.path.isfile(providedPath): return providedPath # Track if it is a relative path, relative to a different location than the current terminal (example/default cases) installationLocation = Path(__file__).parent.parent alternateLocations = [ installationLocation / "MAPLEAF/Examples/Simulations/", installationLocation / "MAPLEAF/Examples/BatchSims/" ] possibleRelativePaths = [ providedPath ] if len(providedPath) < 8 or providedPath[-8:] != ".mapleaf": # If it's just the case name (ex: 'Staging') try also adding the file extension possibleRelativePaths = [ providedPath, providedPath + ".mapleaf" ] for path in possibleRelativePaths: for alternateLocation in alternateLocations: absPath = getAbsoluteFilePath(path, alternateLocation, silent=True) if os.path.isfile(absPath): # We've located the file! return absPath print("ERROR: Unable to locate simulation definition file: {}! Checked whether the path was relative to the current command line location, the MAPLEAF installation directory, or one of the example cases. To be sure that your file will be found, try using an absolute path.".format(providedPath)) sys.exit()
def isBatchSim(batchDefinition) ‑> bool
-
Checks whether the file does not contain a 'Rocket' dictionary, and instead contains dictionaries that have a simDefinitionFile key
Expand source code
def isBatchSim(batchDefinition) -> bool: ''' Checks whether the file does not contain a 'Rocket' dictionary, and instead contains dictionaries that have a simDefinitionFile key ''' rootDicts = batchDefinition.getImmediateSubDicts("") for rootDict in rootDicts: if rootDict == 'Rocket' and 'Rocket.simDefinitionFile' not in batchDefinition: return False return True
def isMonteCarloSimulation(simDefinition) ‑> bool
-
Expand source code
def isMonteCarloSimulation(simDefinition) -> bool: try: nRuns = float(simDefinition.getValue("MonteCarlo.numberRuns")) if nRuns > 1: return True except (KeyError, ValueError): pass return False
def isOptimizationProblem(simDefinition) ‑> bool
-
Expand source code
def isOptimizationProblem(simDefinition) -> bool: try: simDefinition.getValue("Optimization.costFunction") return True except KeyError: return False
def main(argv=None) ‑> int
-
Main function to run a MAPLEAF simulation. Expects to be called from the command line, usually using the
mapleaf
commandFor testing purposes, can also pass a list of command line arguments into the argv parameter
Expand source code
def main(argv=None) -> int: ''' Main function to run a MAPLEAF simulation. Expects to be called from the command line, usually using the `mapleaf` command For testing purposes, can also pass a list of command line arguments into the argv parameter ''' startTime = time.time() # Parse command line call, check for errors parser = buildParser() args = parser.parse_args(argv) if len(args.plotFromLog): # Just plot a column from a log file, and not run a whole simulation Plotting.plotFromLogFiles([args.plotFromLog[1]], args.plotFromLog[0]) print("Exiting") sys.exit() # Load simulation definition file simDefPath = findSimDefinitionFile(args.simDefinitionFile) simDef = SimDefinition(simDefPath) #### Run simulation(s) #### if args.parallel: try: import ray except: print(""" Error importing ray. Ensure ray is installed (`pip install -U ray`) and importable (`import ray` should not throw an error). If on windows, consider trying Linux or running in WSL, at the time this was written, ray on windows was still in beta and unreliable. Alternatively, run without parallelization. """) if isOptimizationProblem(simDef): optSimRunner = optimizationRunnerFactory(simDefinition=simDef, silent=args.silent, parallel=args.parallel) optSimRunner.runOptimization() elif isMonteCarloSimulation(simDef): if not args.parallel: nCores = 1 else: import multiprocessing nCores = multiprocessing.cpu_count() runMonteCarloSimulation(simDefinition=simDef, silent=args.silent, nCores=nCores) elif args.parallel: raise ValueError("ERROR: Can only run Monte Carlo of Optimization-type simulations in multi-threaded mode. Support for multi-threaded batch simulations coming soon.") elif isBatchSim(simDef): print("Batch Simulation\n") batchMain([ simDef.fileName ]) elif args.converge or args.compareIntegrationSchemes or args.compareAdaptiveIntegrationSchemes: cSimRunner = ConvergenceSimRunner(simDefinition=simDef, silent=args.silent) if args.converge: cSimRunner.convergeSimEndPosition() elif args.compareIntegrationSchemes: cSimRunner.compareClassicalIntegrationSchemes(convergenceResultFilePath='convergenceResult.csv') elif args.compareAdaptiveIntegrationSchemes: cSimRunner.compareAdaptiveIntegrationSchemes(convergenceResultFilePath='adaptiveConvergenceResult.csv') else: # Run a regular, single simulation sim = Simulation(simDefinition=simDef, silent=args.silent) sim.run() Logging.removeLogger() print("Run time: {:1.2f} seconds".format(time.time() - startTime)) print("Exiting")