Module: interactive
Utilities for interactive simulation and results monitoring; NOTE: does not work on MacOS!
Author: Johannes Gerstmayr
Date: 2021-01-17 (created)
Function: ConfigureDialogColumns
ConfigureDialogColumns(tkWindow)
function description: give the columns of a dialog’s grid their weights and minimum widths; returns the number of columns
Function: AnimateModes
function description: animate modes of ObjectFFRFreducedOrder, of nodal coordinates (changes periodically one nodal coordinate) or of a list of system modes provided as list of lists; for creating snapshots, press ‘Static’ and ‘Record frames’ and press ‘Run’ to save one figure in the image subfolder; for creating animations for one mode, use the same procedure but use ‘One Cycle’. Modes may be inverted by pressing according ‘+’ and ‘-’ buttons next to Amplitude.
input:
systemContainer: system container (usually SC) of your model, containing visualization settingsmainSystem: system (usually mbs) containing your modelnodeNumber: node number of which the coordinates shall be animated. In case of ObjectFFRFreducedOrder, this is the generic node, e.g., ‘nGenericODE2’ in the dictionary returned by the function AddObjectFFRFreducedOrderWithUserFunctions(…); if nodeNumber=None, then the systemEigenVectors list is usedperiod: delay for animation of every frame; the default of 0.04 results in approximately 25 frames per secondstepsPerPeriod: number of steps into which the animation of one cycle of the mode is split intoshowTime: show a virtual time running from 0 to 2*pi during one mode cyclerenderWindowText: additional text written into renderwindow before ‘Mode X’ (use \(\backslash\)n to add line breaks)runOnStart: immediately go into ‘Run’ moderunMode: 0=continuous run, 1=static continuous, 2=one cycle, 3=static (use slider/mouse to vary time steps)scaleAmplitude: additional scaling for amplitude if necessaryfontSize: define font size for labels in InteractiveDialogtitle: if empty, it uses default; otherwise define specific titlecheckRenderEngineStopFlag: if True, stopping renderer (pressing Q or Escape) also causes stopping the interactive dialogsystemEigenVectors: may be a list of lists of system eigenvectors for ODE2 (and possibly ODE1) coordinates or a eigenvector matrix containing mode vectors in columns; if nodeNumber=None, these eigenvectors are then used to be animated
output: opens interactive dialog with further settings
notes: Uses class InteractiveDialog in the background, which can be used to adjust animation creation. If meshes are large, animation artifacts may appear, which are resolved by using a larger update period. Press ‘Run’ to start animation; Chose ‘Mode shape’, according component for contour plot; to record one cycle for animation, choose ‘One cycle’, run once to get the according range in the contour plot, press ‘Record frames’ and press ‘Run’, now images can be found in subfolder ‘images’ (for further info on animation creation see Storing images and generating animations); now deactivate ‘Record frames’ by pressing ‘Off’ and chose another mode
Relevant Examples (Ex) and TestModels (TM) with weblink to github: CMSexampleCourse.py (Ex), netgenSTLtest.py (Ex), NGsolveCMStutorial.py (Ex), NGsolveCraigBampton.py (Ex), NGsolveFFRF.py (Ex), ANCFThinPlateTests.py (TM), objectFFRFreducedOrderShowModes.py (TM), …
Function: SolutionViewer
function description: open interactive dialog and visulation (animate) solution loaded with LoadSolutionFile(…); Change slider ‘Increment’ to change the automatic increment of time frames; Change mode between continuous run, one cycle (fits perfect for animation recording) or ‘Static’ (to change Solution steps manually with the mouse); update period also lets you change the speed of animation; Press Run / Stop button to start/stop interactive mode (updating of grpahics)
input:
mainSystem: the system used for visualization of solution (solution is loaded into visualization state of that system)solution: solution dictionary previously loaded with exudyn.utilities.LoadSolutionFile(…); will be played from first to last row; if solution==None, it tries to load the file coordinatesSolutionFileName as stored in mbs.sys[‘simulationSettings’], which are the simulationSettings of the previous simulationrowIncrement: can be set larger than 1 in order to skip solution frames: e.g. rowIncrement=10 visualizes every 10th row (frame)timeout: in seconds is used between frames in order to limit the speed of animation; e.g. use timeout=0.04 to achieve approximately 25 frames per secondrunOnStart: immediately go into ‘Run’ moderunMode: 0=continuous run, 1=one cycle, 2=static (use slider/mouse to vary time steps)fontSize: define font size for labels in InteractiveDialogtitle: if empty, it uses default; otherwise define specific titlecheckRenderEngineStopFlag: if True, stopping renderer (pressing Q or Escape) also causes stopping the interactive dialogwindowSize: [width, height] of the dialog in pixels; None gives the size stored in ~/.exudyn/config.json, or the size of the layout
output: (type: None) updates current visualization state, renders the scene continuously (after pressing button ‘Run’)
example:
#HERE, mbs must contain same model as solution stored in solution/coordinatesSolution.txt
#adjust autoFitScence, otherwise it may lead to unwanted fit to scene
SC.visualizationSettings.general.autoFitScene = False
from exudyn.interactive import SolutionViewer #import function
sol = LoadSolutionFile('solution/coordinatesSolution.txt') #the default solution file
mbs.SolutionViewer(sol) #call via MainSystem
NOTE: this function is directly available in MainSystem (mbs); it should be directly called as mbs.SolutionViewer(…). For description of the interface, see the MainSystem Python extensions, Function: SolutionViewer
Function: ShowImage
function description: the current scene as an image, rendered by the raytracer without a window, and shown with matplotlib; made for notebooks, where no render window opens, and for documentation images
input:
systemContainer: the SystemContainer whose systems are drawn, with its visualizationSettingssize: [width, height] of the image in pixels; the render window size of the settings is set back afterwardsmodelRotation: the rotation of the view as 3x3 matrix, e.g. RotationMatrixX(0.4)@RotationMatrixY(-0.5), for this image; None takes openGL.advanced.initialModelRotation of the settingszoomAll: if True, the view is fitted to the scene first; set False to keep a view set by SC.renderer.SetModelView(…) or a stored render stateshow: if True, the image is drawn with matplotlib (inline in a notebook); False only returns itfileName: if not empty, the image is also written to this file (e.g. ‘images/scene.png’)
output: the image as numpy array of shape (height, width, 3), RGB values 0 to 255
example:
mbs.Assemble()
exudyn.interactive.ShowImage(SC) #the scene in the reference configuration
Function: ConvertImages2Video
function description: function to call ffmpeg in the background and convert images to video; requires the program ffmpeg and the package ffmpeg-python (“conda install ffmpeg”, “pip install ffmpeg-python”)
input:
workingDir: directory where images are stored and where animation is written toinputPattern: pattern of images; ‘frame’ is the name used in visualizationSettings.exportImages.saveImageFileName; if saveImageFormat=PNG, then the ending is .pngoutputFile: filename and ending (.mp4 recommended) for generated videoinputFrameRate: framerate for images relative to outputFrameRate: if inputFrameRate=50 and outputFrameRate=25, then only every second frame is usedoutputFrameRate: framerate for resulting videocompressionCRF: compression rate of ffmpeg, where 0=uncompressed, 25 is medium compression, >30 is very low qualitystartNumber: start index of first frame chosen for animationtotalFrames: total number of frames (keep field empty to select all frames after startNumber)videoCodec: the ffmpeg encoder, e.g. ‘libx264’; None: libx264, and if that ffmpeg has no libx264 (as the one of conda-forge on macOS), h264_videotoolbox on macOS and mpeg4 on the other platforms; compressionCRF applies to libx264 only
output: None; writes animation when finished
example:
#after successful simulation, call:
mbs.SolutionViewer() #click "Stop", "One Cycle" and "Record frames" => close window
#if images are in folder 'images', then call this to create animation:
ConvertImages2Video(workingDir='images', outputFile='test.mp4')
Function: InteractiveImages2Video
InteractiveImages2Video(closeAfterCreation = False, fontSize = 11)
function description: interactive dialog to convert generated images to videos using ffmpeg library; see also ConvertImages2Video() for meaning of values; requires ffmpeg-python to be installed
CLASS InteractiveDialog (in module interactive)
class description: create an interactive dialog, which allows to interact with simulations the dialog has a ‘Run’ button, which initiates the simulation and a ‘Stop’ button which stops/pauses simulation; ‘Quit’ closes the simulation model for examples, see simulateInteractively.py and massSpringFrictionInteractive.py use init method to setup this class with certain buttons, edit boxes and sliders
example:
#the following example is only demonstrating the structure of dialogItems and plots
#dialogItems structure:
#general items:
# 'type' can be out of:
# 'label' (simple text),
# 'button' (button with callback function),
# 'radio' (a radio button with several alternative options),
# 'slider' (with an adjustable range to choose a value)
# 'grid': (row, col, colspan) specifies the row, column and (optionally) the span of columns the item is placed at;
# exception in 'radio', where grid is a list of (row, col) for every choice
# 'options': text options, where 'L' means flush left, 'R' means flush right
#suboptions of 'label':
# 'text': a text to be drawn
#suboptions of 'button':
# 'text': a text to be drawn on button
# 'callFunction': function which is called on button-press
#suboptions of 'radio':
# 'textValueList': [('text1',0),('text2',1)] a list of texts with according values
# 'value': default value (choice) of radio buttons
# 'variable': according variable in mbs.variables (or mbs.sys), which is set to current radio button value
#suboptions of 'slider':
# 'range': (min, max) a tuple containing minimum and maximum value of slider
# 'value': default value of slider
# 'steps': number of steps in slider
# 'variable': according variable in mbs.variables (or mbs.sys), which is set to current slider value
#example:
dialogItems = [{'type':'label', 'text':'Nonlinear oscillation simulator', 'grid':(0,0,2), 'options':['L']},
{'type':'button', 'text':'test button','callFunction':ButtonCall, 'grid':(1,0,2)},
{'type':'radio', 'textValueList':[('linear',0),('nonlinear',1)], 'value':0, 'variable':'mode', 'grid': [(2,0),(2,1)]},
{'type':'label', 'text':'excitation frequency (Hz):', 'grid':(5,0)},
{'type':'slider', 'range':(3*f1/800, 3*f1), 'value':omegaInit/(2*pi), 'steps':800, 'variable':'frequency', 'grid':(5,1)},
{'type':'label', 'text':'damping:', 'grid':(6,0)},
{'type':'slider', 'range': (0, 40), 'value':damper, 'steps':800, 'variable':'damping', 'grid':(6,1)},
{'type':'label', 'text':'stiffness:', 'grid':(7,0)},
{'type':'slider', 'range':(0, 10000), 'value':spring, 'steps':800, 'variable':'stiffness', 'grid':(7,1)}]
#plots structure:
plots={'nPoints':500, #number of stored points in subplots (higher means slower drawing)
'subplots':(2,1), #(rows, columns) arrangement of subplots (for every sensor)
#sensors defines per subplot (sensor, coordinate), xlabel and ylabel; if coordinate=0, time is used:
'sensors':[[(sensPos,0),(sensPos,1),'time','mass position'],
[(sensFreq,0),(sensFreq,1),'time','excitation frequency']],
'limitsX':[(0,2),(-5,5)], #x-range per subplot; if not provided, autoscale is applied
'limitsY':[(-5,5),(0,10),], #y-range per subplot; if not provided, autoscale is applied
'fontSize':16, #custom font size for figure
'subplots':False, #if not specified, subplots are created; if False, all plots go into one window
'lineStyles':['r-','b-'], #if not specified, uses default '-b', otherwise define list of line styles [string for matplotlib.pyplot.plot] per sensor
'sizeInches':(12,12)} #specific x and y size of figure in inches (using 100 dpi)
Class function: init
class function description: initialize an InteractiveDialog
input:
mbs: a multibody system to be simulatedsimulationSettings: exudyn.SimulationSettings() according to user settingssimulationFunction: a user function(mbs, self) which is called before a simulation for the short period is started (e.g, assign special values, etc.); the arguments are the MainSystem mbs and the InteractiveDialog (self)dialogItems: a list of dictionaries, which describe the contents of the interactive items, where every dict has the structure {‘type’:[label, entry, button, slider, check] … according to tkinter widgets, ‘callFunction’: a function to be called, if item is changed/button pressed, ‘grid’: (row,col) of item to be placed, ‘rowSpan’: number of rows to be used, ‘columnSpan’: number of columns to be used; for special item options see notes}plots: list of dictionaries to specify a sensor to be plotted live, see example; otherwise use default Noneperiod: a simulation time span in seconds which is simulated with the simulationFunction in every iterationrealtimeFactor: if 1, the simulation is nearly performed in realtime (except for computation time); if > 1, it runs faster than realtime, if < 1, than it is sloweruserStartSimulation: a function F(flag) which is called every time after Run/Stop is pressed. The argument flag = False if button “Run” has been pressed, flag = True, if “Stop” has been pressedtitle: title text for interactive dialogshowTime: shows current time in dialogfontSize: adjust font size for all dialog itemsdoTimeIntegration: performs internal time integration with given parametersrunOnStart: immediately activate ‘Run’ button on startaddLabelStringVariables: True: adds a list labelStringVariables containing the (modifiable) list of string variables for label (text) widgetsaddSliderVariables: True: adds a list sliderVariables containing the (modifiable) list of variables for slider (=tkinter scale) widgets; this is not necessarily needed for changing slider values, as they can also be modified with dialog.widgets[..].set(…) methodcheckRenderEngineStopFlag: if True, stopping renderer (pressing Q or Escape) also causes stopping the interactive dialoguserOnChange: a user function(mbs, self) which is called after period, if widget values are different from values stored in mbs.variables; this usually occurs if buttons are pressed or sliders are moved; the arguments are the MainSystem mbs and the InteractiveDialog (self)useSysVariables: for internal visualization functions: in this case, variables are written to mbs.sys instead of mbs.variableswindowSize: [width, height] of the dialog in pixels; None gives the size stored in ~/.exudyn/config.json, or the size of the layout if nothing is stored. A size given here wins over a stored one, as a position given in a script wins for the render window
notes: detailed description of dialogItems and plots list/dictionary is given in commented the example below
Class function: OnQuit
class function description: function called when pressing escape or closing dialog
Class function: StartSimulation
StartSimulation(self, event = None)
class function description: function called on button ‘Run’
Class function: ProcessWidgetStates
class function description: assign current values of radio buttons and sliders to mbs.variables or mbs.sys
Class function: ContinuousRunFunction
ContinuousRunFunction(self, event = None)
class function description: function which is repeatedly called when button ‘Run’ is pressed
Class function: InitializePlots
class function description: initialize figure and subplots for plots structure
Class function: UpdatePlots
class function description: update all subplots with current sensor values
Class function: InitializeSolver
class function description: function to initialize solver for repeated calls
Class function: FinalizeSolver
class function description: stop solver (finalize correctly)
Class function: RunSimulationPeriod
class function description: function which performs short simulation for given period
Relevant Examples (Ex) and TestModels (TM) with weblink to github: massSpringFrictionInteractive.py (Ex), nMassOscillatorInteractive.py (Ex), serialRobotInteractiveLimits.py (Ex), simulateInteractively.py (Ex)