DsolModel.java

package nl.tudelft.simulation.dsol.model;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import org.djunits.unit.Unit;
import org.djunits.value.vdouble.scalar.base.DoubleScalar;
import org.djunits.value.vfloat.scalar.base.FloatScalar;
import org.djutils.exceptions.Throw;

import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.experiment.StreamInformation;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDoubleScalar;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterException;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterFloatScalar;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
import nl.tudelft.simulation.dsol.statistics.SimulationStatistic;
import nl.tudelft.simulation.jstats.streams.StreamInterface;
import nl.tudelft.simulation.language.DsolRuntimeException;

/**
 * The model interface defines the model object. Since version 2.1.0 of DSOL, the DsolModel now knows its simulator and can
 * return it to anyone interested. Through the Simulator, the Replication can be requested and through that the Experiment and
 * the Treatment under which the simulation is running.
 * <p>
 * Copyright (c) 2003-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
 * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
 * project is distributed under a three-clause BSD-style license, which can be found at
 * <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
 * </p>
 * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
 * @param <T> the time type
 * @param <S> the simulator to use
 */
public interface DsolModel<T extends Number & Comparable<T>, S extends SimulatorInterface<T>>
{
    /**
     * Construct a model on a simulator.
     * @throws SimRuntimeException on model construction failure
     */
    void constructModel() throws SimRuntimeException;

    /**
     * Set the executable code for resetting the application at a 'reset' command in the GUI.
     * @param resetApplicationExecutable the executable code for resetting the application
     */
    void setResetApplicationExecutable(Runnable resetApplicationExecutable);

    /**
     * Get the executable code for resetting the application at a 'reset' command in the GUI.
     * @return the executable code for resetting the application
     */
    Runnable getResetApplicationExecutable();

    /**
     * Reset the application.
     */
    void resetApplication();

    /**
     * Return the simulator for this model.
     * @return the simulator for the model
     */
    S getSimulator();

    /**
     * Get the input parameters for this model.
     * @return InputParameterMap the input parameters for this model
     */
    InputParameterMap getInputParameterMap();

    /**
     * Set the input parameters for this model.
     * @param inputParameterMap the new input parameter map for this model
     */
    void setInputParameterMap(InputParameterMap inputParameterMap);

    /**
     * Get the double value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the double value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default double getInputParameterDouble(final String key)
    {
        try
        {
            return ((Number) getInputParameterMap().get(key).getCalculatedValue()).doubleValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterDouble cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the float value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the float value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default float getInputParameterFloat(final String key)
    {
        try
        {
            return ((Number) getInputParameterMap().get(key).getCalculatedValue()).floatValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterFloat cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the long value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the long value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default long getInputParameterLong(final String key)
    {
        try
        {
            return ((Number) getInputParameterMap().get(key).getCalculatedValue()).longValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterLong cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the int value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the int value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default int getInputParameterInteger(final String key)
    {
        try
        {
            return ((Number) getInputParameterMap().get(key).getCalculatedValue()).intValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterInteger cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the String value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the String value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default String getInputParameterString(final String key)
    {
        try
        {
            return (String) getInputParameterMap().get(key).getCalculatedValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterString cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the boolean value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the boolean value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default boolean getInputParameterBoolean(final String key)
    {
        try
        {
            return (boolean) getInputParameterMap().get(key).getCalculatedValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterBoolean cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the double scalar value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @param scalarClass the scalar class to retrieve the value for
     * @param <U> the unit class
     * @param <V> the scalar class
     * @return the double scalar value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    @SuppressWarnings("unchecked")
    default <U extends Unit<U>, V extends DoubleScalar<U, V>> V getInputParameterDoubleScalar(final String key,
            final Class<V> scalarClass)
    {
        try
        {
            InputParameterDoubleScalar<U, V> ip = (InputParameterDoubleScalar<U, V>) getInputParameterMap().get(key);
            ip.setCalculatedValue();
            return ip.getCalculatedValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterDoubleScalar cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the float scalar value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @param scalarClass the scalar class to retrieve the value for
     * @param <U> the unit class
     * @param <V> the scalar class
     * @return the float scalar value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    @SuppressWarnings("unchecked")
    default <U extends Unit<U>, V extends FloatScalar<U, V>> V getInputParameterFloatScalar(final String key,
            final Class<V> scalarClass)
    {
        try
        {
            InputParameterFloatScalar<U, V> ip = (InputParameterFloatScalar<U, V>) getInputParameterMap().get(key);
            ip.setCalculatedValue();
            return ip.getCalculatedValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterFloatScalar cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the unit value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @param unitClass the unit class to retrieve the value for
     * @return the unit value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    @SuppressWarnings("unchecked")
    default <U extends Unit<U>> U getInputParameterUnit(final String key, final Class<U> unitClass)
    {
        try
        {
            return (U) getInputParameterMap().get(key).getCalculatedValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterUnit cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the LocalDateTime value for an input parameter.
     * @param key the name of the parameter to retrieve, with dot-notation to indicate sub-maps
     * @return the LocalDateTime value for an input parameter
     * @throws DsolRuntimeException when the parameter could not be retrieved or casted
     */
    default LocalDateTime getInputParameterLocalDateTime(final String key)
    {
        try
        {
            return (LocalDateTime) getInputParameterMap().get(key).getCalculatedValue();
        }
        catch (InputParameterException | ClassCastException exception)
        {
            throw new DsolRuntimeException("getInputParameterLocalDateTime cannot retrieve or cast value", exception);
        }
    }

    /**
     * Get the output statistics for this model.
     * @return List&lt;StatisticsInterface&gt; the output statistics for this model
     */
    List<SimulationStatistic<T>> getOutputStatistics();

    /**
     * Set the initial streams of the model based on a StreamInformation object. This method can be called right after the
     * construction of the model object, or just before the model is constructed. <br>
     * <u>Note 1:</u> If a model is run as part of an Experiment, the seeds of the random streams will be reset just before the
     * call to constructModel(), so <b>do not call this method from constructModel()</b>, as it will reset the seeds to their
     * initial values, and undo the seed management of the Experiment.<br>
     * <u>Note 2:</u> The original streams are copied into the model, so they are not cloned (as the streams do not implement
     * cloneable, and as they have inner state that needs to be preserved). So be careful with manipulating the streams in the
     * streamInformation object afterward.
     * @param streamInformation the streams that have been prepared in a StreamInformation class
     * @throws NullPointerException when streamInformation is null
     */
    void setStreamInformation(StreamInformation streamInformation);

    /**
     * Return the available streams of the model stored in a StreamInformation object.
     * @return the streams that have been prepared in a StreamInformation class
     */
    StreamInformation getStreamInformation();

    /**
     * Return the streams of this model, mapping stream ids to streams.
     * @return the streams of this model
     */
    default Map<String, StreamInterface> getStreams()
    {
        return getStreamInformation().getStreams();
    }

    /**
     * Return a specific stream of this model, based on a stream id, or null when the stream could not be found.
     * @param streamId the id of the stream to be retrieved
     * @return StreamInterface the stream, or null when the stream could not be found
     * @throws NullPointerException when streamId is null
     */
    default StreamInterface getStream(final String streamId)
    {
        Throw.whenNull(streamId, "streamId cannot be null");
        synchronized (getStreamInformation())
        {
            return getStreams().get(streamId);
        }
    }

    /**
     * Return the default streams of this model.
     * @return the default stream of this model
     */
    default StreamInterface getDefaultStream()
    {
        return getStreamInformation().getStream("default");
    }

    /**
     * Reset the streams to their original seed values.
     */
    default void resetStreams()
    {
        synchronized (getStreamInformation())
        {
            for (StreamInterface stream : getStreams().values())
            {
                stream.reset();
            }
        }
    }

}