AbstractFlowModel.java

package nl.tudelft.simulation.dsol.formalisms.flow;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.djutils.exceptions.Throw;

import nl.tudelft.simulation.dsol.experiment.StreamInformation;
import nl.tudelft.simulation.dsol.model.AbstractDsolModel;
import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
import nl.tudelft.simulation.language.DsolRuntimeException;

/**
 * AbstractFlowModel offers a base for a block-based model that registers the blocks it is using.
 * <p>
 * Copyright (c) 2025-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/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
 * project is distributed under a three-clause BSD-style license, which can be found at
 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
 * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
 * </p>
 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
 * @param <T> the time type
 * @param <S> the simulator type to use
 */
public abstract class AbstractFlowModel<T extends Number & Comparable<T>, S extends SimulatorInterface<T>>
        extends AbstractDsolModel<T, S> implements FlowModel<T, S>
{
    /** counter of the blocks when automatic naming is used. */
    protected Map<String, AtomicInteger> countMap = new HashMap<>();

    /** the blocks in the model. */
    protected Map<String, Block<T>> blockMap = new HashMap<>();

    /** the naming regime: id-based or automatic. */
    protected final BlockNamingType blockNamingType;

    /**
     * Construct a DSOL model and set the simulator.
     * @param simulator the simulator to use for this model
     * @param blockNamingType the naming regime: BY_ID or AUTOMATIC
     * @throws NullPointerException when simulator or blockNamingType is null
     */
    public AbstractFlowModel(final S simulator, final BlockNamingType blockNamingType)
    {
        super(simulator);
        Throw.whenNull(blockNamingType, "blockNamingType cannot be null");
        this.blockNamingType = blockNamingType;
    }

    /**
     * Construct a DSOL model and set the simulator as well as the initial streams, so they can be used in the constructor of
     * the model.
     * @param simulator the simulator to use for this model
     * @param streamInformation the streams that have been prepared in a StreamInformation class
     * @param blockNamingType the naming regime: BY_ID or AUTOMATIC
     * @throws NullPointerException when simulator or streamInformation or blockNamingType is null
     */
    public AbstractFlowModel(final S simulator, final StreamInformation streamInformation,
            final BlockNamingType blockNamingType)
    {
        super(simulator, streamInformation);
        Throw.whenNull(blockNamingType, "blockNamingType cannot be null");
        this.blockNamingType = blockNamingType;
    }

    /**
     * Return the naming regime: id-based or automatic.
     * @return the naming regime: BY_ID or AUTOMATIC
     */
    public BlockNamingType getBlockNamingType()
    {
        return this.blockNamingType;
    }

    /**
     * Return the map with block ids and simulation blocks in this flow model.
     * @return the map with block ids and simulation blocks in this flow model
     */
    @Override
    public Map<String, Block<T>> getBlockMap()
    {
        return this.blockMap;
    }

    /**
     * Add a new block to the model. Adding blocks is done in the constructor of the Block object itself.
     * @param block the new block to add to the model
     */
    @Override
    public void addBlock(final Block<T> block)
    {
        String id;
        if (this.blockNamingType.equals(BlockNamingType.BY_ID))
        {
            id = block.getId();
            Throw.when(this.blockMap.containsKey(id), DsolRuntimeException.class, "block id " + id + " already registered");
        }
        else
        {
            id = block.getClass().getSimpleName();
            if (!this.countMap.containsKey(id))
            {
                this.countMap.put(id, new AtomicInteger(0));
            }
            int nr = this.countMap.get(id).incrementAndGet();
            id += "_" + nr;
        }
        this.blockMap.put(id, block);
    }

}