Demo models‎ > ‎MacroABM‎ > ‎


The observer class (

The MacroObserver builds the graphical objects that allow the monitoring and inspection of the simulation outcome in real time. It extends the AbstractSimulationObserverManager interface and, similarly to the other simulation managers (the Model and the Collector), requires the implementation of a buildObjects() method and a buildSchedule() method. 

The MacroObserver class contains 'GUI parameters' that allow the user to adjust the values of these parameters via the JAS-mine GUI, usually at run-time before the model is built and executed.  The GUI parameters of the MacroObserver class are shown in Box 1.  

@GUIparameter(description = "Set a regular time for any charts to update")

Double chartUpdatePeriod = 1.; 

@GUIparameter(description = "Toggle to decide if GUI includes data visualization") 

Boolean charts = true;

Box 1. The MacroObserver GUI parameters.

The field chartUpdatePeriod enables the user to set the time-period that must past before the charts are updated.  so the user can increase or decrease the frequency of update of the charts by decreasing or increasing the value of this field.

The charts field is a boolean that allows the user to easily switch on or off the charts.  The user might not wish to inspect the model during run-time via the charts, but only by the output data exported to .csv files or the output database, and so may increase the efficiency of the simulation by switching the charts off.  In the current situation, the charts do not significantly slow down the simulation when just one simulation run is performed, however efficiency of the simulation runs may become an issue in the case that the model is run many times to perform design of experiments.  In this case, the JAS-mine GUI can be turned off (see How to run a simulation many times (design of experiments) for more information).

The buildObjects() method creates a multitude of time-series plots to be displayed in the JAS-mine GUI within a tabbed pane, which is used to improve clarity and tidiness.  Some of the other JAS-mine demo models feature a different layout, with charts placed next to each other, to allow the user to see multiple charts simultaneously.  We occasionally do something similar within the tabbed pane in some tabs, such as the 'Debt', 'Technology (R&D)' and 'Labor Market' tabs, which feature two charts side-by-side displaying related content, but where the data in each chart is at vastly different scales.  

The first one in Box 2 (logOutputConsumptionInvestment) depicts the evolution of the logged aggregate output (GDP), Consumption and Investment in the simulation: it takes the data from the MacroCollector's Variable enums which implement the IDoubleSource interface.  

The second graphical object in Box 2 is an example of setting two charts side-by-side under one tabbed pane, because they contain related content at differing scales.  The debtPlots set holds the TimeSeriesSimulationPlotter objects 'debt' and 'badDebt', with the first object showing both debt and bad debt (which is a small fraction of the overall debt in simulations using the default parameter values), whereas the second object clarifies the bad debt values by showing this data on its own.  The MacroObserver's createScrollPaneFromPlots() method sorts out the arrangement, with the debtPlots.size() specifying how many plots need to be arranged under one graphical component (a JComponent object).  The tabbed pane holding two charts can be seen in Figure 1 below.

At the end of the buildObjects() method, all the tabbed panes each holding at least one chart are added to the overall chartsFrame object, which is displayed in the JAS-mine GUI.

public void buildObjects() {

    final MacroCollector collector = (MacroCollector) getCollectorManager();

    final MacroModel model = (MacroModel) getManager();

    final Bank bank = (Bank) model.getBank();



        updateChartSet = new LinkedHashSet<JInternalFrame>(); //Set of all charts needed to be scheduled for                                                                 //updating

        tabSet = new LinkedHashSet<JComponent>(); //Set of all JInternalFrames each having a                                         //tab.  Each tab frame will potentially contain more than one chart each.

//Create chart containing aggregate time-series' of log GDP, log consumption and log total investment

        TimeSeriesSimulationPlotter logOutputConsumptionInvestment = new TimeSeriesSimulationPlotter("Aggregate Time Series", "Log");

        logOutputConsumptionInvestment.addSeries("Log GDP", (IDoubleSource) new MultiTraceFunction.Double(collector, MacroCollector.Variables.LogGDP));

logOutputConsumptionInvestment.addSeries("Log Consumption", (IDoubleSource) new MultiTraceFunction.Double(collector, MacroCollector.Variables.ConsumptionLog));

logOutputConsumptionInvestment.addSeries("Log Investment", (IDoubleSource) new MultiTraceFunction.Double(collector, MacroCollector.Variables.LogTotalInvestment));


updateChartSet.add(logOutputConsumptionInvestment); //Add to set to be updated in                                                                                 //buildSchedule method

        tabSet.add(logOutputConsumptionInvestment);                      //Add to set of charts to feature                                                                             //in tabbed pane


//Two charts under one tab, because data in charts have different scales

        Set<JInternalFrame> debtPlots = new LinkedHashSet<JInternalFrame>();

        //Chart one: Debt and Bad Debt

TimeSeriesSimulationPlotter debt = new TimeSeriesSimulationPlotter("Debt", ""); debt.addSeries("Bad debt (cFirm sector)", (IDoubleSource) new MultiTraceFunction.Double(collector,


        debt.addSeries("Bad debt (bank)", (IDoubleSource) new MultiTraceFunction.Double(bank,


debt.addSeries("Debt", (IDoubleSource) new MultiTraceFunction.Double(bank, Bank.Variables.Debt)); updateChartSet.add(debt); //Add to set to be updated in buildSchedule method debtPlots.add(debt);
        //Chart two: Bad Debt only
     TimeSeriesSimulationPlotter badDebt = new TimeSeriesSimulationPlotter("Bad Debt", "");

badDebt.addSeries("Bad Debt (cFirm sector)", (IDoubleSource) new MultiTraceFunction.Double(collector, MacroCollector.Variables.BadDebt));

badDebt.addSeries("Bad debt (bank)", (IDoubleSource) new MultiTraceFunction.Double(bank, 


updateChartSet.add(badDebt); //Add to set to be updated in buildSchedule method


                  //The observer's createScrollPaneFromPlots() method arranges layout for multiple charts side-by-side

tabSet.add(createScrollPaneFromPlots(debtPlots, "Debt", debtPlots.size()));     //Add to set of charts                                                                                    //to feature in tabbed pane


        //Create tabbed pane to hold all the charts and add to the JAS-mine GUI window

     JInternalFrame chartsFrame = new JInternalFrame("Charts");

JTabbedPane tabbedPane = new JTabbedPane();


for(JComponent plot: tabSet) {

tabbedPane.addTab(plot.getName(), plot);





GuiUtils.addWindow(chartsFrame, 300, 0, 1560, 660);



Box 2. The MacroObserver.buildObjects() method.

Figure 1. The overall JInternalFrame object called 'chartsFrame' holds several JTabbedPane objects.  The 'Debt' JTabbedPane object itself holds two charts, one showing both Total Debt and Bad Debt, and the second showing only Bad Debt (at a smaller scale).

Other types of plots can be easily added. In particular, by building on the JFreeChart library, the HistogramSimulationPlotter class in the JAS-mine GUI libraries allows to create histograms for representing distributions of given variables in the simulated population, at any given simulation period.  The JAS-mine GUI libraries also provide the CollectionBarSimulationPlotter and IndividualBarSimulationPlotter for bar charts, and ScatterplotSimulationPlotter for scatterplots.

The schedule of the MacroObserver class simply manages the updating of these plots (Box 3). Here, the built-in JAS-mine Enum CommonEventType.Update is used, rather than a class-specific implementation of the EventListener interface as in the Collector. This requires scheduling the update of each graph separately, but allows for a better control of the update frequency and is easily achieved by adding all charts to an 'updateChartSet' set and iterating through the set, adding an update event for each chart.  The update frequency is controlled by the chartUpdatePeriod GUI parameter, as discussed above in the text relating to Box 1.

GUIparameter(description = "Set a regular time for any charts to update")

Double chartUpdatePeriod = 1.; 

@GUIparameter(description = "Toggle to decide if GUI includes data visualization") 

Boolean charts = true;


public void buildSchedule() {



EventGroup chartingEvents = new EventGroup();

for(JInternalFrame plot: updateChartSet) {

chartingEvents.addEvent(plot, CommonEventType.Update);


getEngine().getEventQueue().scheduleRepeat(chartingEvents, 0, Parameters.OBSERVER_ORDERING, chartUpdatePeriod);



Box 3. The MacroObserver.buildSchedule() method.

Previous: Data Output        Next: Running the model