<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package etomica.integrator;

import etomica.atom.AtomArrayList;
import etomica.atom.IAtom;
import etomica.atom.IAtomKinetic;
import etomica.atom.IAtomList;
import etomica.box.Box;
import etomica.box.BoxMoleculeEvent;
import etomica.molecule.IMolecule;
import etomica.nbr.list.INeighborListListener;
import etomica.nbr.list.NeighborListManagerFasterer;
import etomica.potential.*;
import etomica.potential.compute.*;
import etomica.space.Vector;
import etomica.species.SpeciesManager;
import etomica.util.collections.Int2IntHash;
import etomica.util.random.IRandom;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class IntegratorHardFasterer extends IntegratorMDFasterer implements INeighborListListener {

    protected final NeighborManagerHard neighborManager;
    protected final PotentialMasterBonding.FullBondingInfo bondingInfo;
    protected double[] collisionTimes;
    protected int[] collisionPartners;
    protected IPotentialHard[] collisionPotentials;
    protected Vector[] collisionVector;
    protected int[] eventBinsFirstAtom, eventBinsNextAtom, eventBinsPrevAtom;
    protected int[] collisionOldState;
    protected final IPotentialHard[][] pairPotentials;
    protected final IPotentialHardField[] fieldPotentials;
    protected double tBase, tMax;
    protected int collisionUpdateInterval, collisionUpdateCountdown;
    protected long collisionCount;
    protected final NeighborIterator neighborIterator;
    protected final List<CollisionListener> collisionListeners;
    private int lastBin = 0;
    private Int2IntHash[] bondState;
    private int[] fieldState;

    private long tDelete, tAdd, tNext, tSteps, tUpdate, tUp, tDown, tBump, tData, tCollect, tCollision, tAdvance, tNotStep;
    private final boolean writeTiming = false, verbose = false;
    private long lastTimingPrint = 0;
    private int handlingEvent = 0;

    private long nanoTime() {
        return writeTiming ? System.nanoTime() : 0;
    }

    public static IPotentialHard[][] extractHardPotentials(PotentialComputePair pcPair) {
        Potential2Soft[][] softPotentials = pcPair.getPairPotentials();
        int n = softPotentials.length;
        IPotentialHard[][] pairPotentials = new IPotentialHard[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pairPotentials[i][j] = (IPotentialHard) softPotentials[i][j];
            }
        }
        return pairPotentials;
    }

    public static IPotentialHard[][] extractHardPotentials(PotentialMasterFasterer pcPair) {
        Potential2Soft[][] softPotentials = pcPair.getPairPotentials();
        int n = softPotentials.length;
        IPotentialHard[][] pairPotentials = new IPotentialHard[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pairPotentials[i][j] = (IPotentialHard) softPotentials[i][j];
            }
        }
        return pairPotentials;
    }

    public static IPotentialHard[][] extractHardPotentials(PotentialComputePairGeneral pcPair) {
        IPotentialPair[][] softPotentials = pcPair.getPairPotentials();
        int n = softPotentials.length;
        IPotentialHard[][] pairPotentials = new IPotentialHard[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pairPotentials[i][j] = (IPotentialHard) softPotentials[i][j];
            }
        }
        return pairPotentials;
    }

    public static IPotentialHardField[] extractFieldPotentials(PotentialComputeField pcField) {
        IPotentialField[] softFieldPotentials = pcField.getFieldPotentials();
        int n = softFieldPotentials.length;
        IPotentialHardField[] fieldPotentials = new IPotentialHardField[n];
        for (int i = 0; i < n; i++) {
            fieldPotentials[i] = (IPotentialHardField) softFieldPotentials[i];
        }
        return fieldPotentials;
    }

    private static PotentialCompute makeTotalCompute(Box box, IPotentialHard[][] pairPotentials, NeighborManagerHard neighborManager, IPotentialHardField[] fieldPotentials, SpeciesManager sm, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        int n = pairPotentials.length;
        IPotentialPair[][] softPotentials = new IPotentialPair[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                softPotentials[i][j] = (IPotentialPair) pairPotentials[i][j];
            }
        }
        List<PotentialCompute> computes = new ArrayList<>();
        PotentialComputePairGeneral pcPair = new PotentialComputePairGeneral(softPotentials, box, neighborManager);
        computes.add(pcPair);
        if (fieldPotentials != null) {
            PotentialComputeField pcField = new PotentialComputeField(fieldPotentials, box);
            computes.add(pcField);
        }
        if (bondingInfo != null) {
            PotentialMasterBonding pmBonding = new PotentialMasterBonding(sm, box, bondingInfo);
            computes.add(pmBonding);
        }
        if (computes.size() == 1) return pcPair;
        PotentialCompute[] computesArray = computes.toArray(new PotentialCompute[0]);
        return new PotentialComputeAggregate(computesArray);
    }

    /**
     * Constructs integrator with a default for non-isothermal sampling.
     *
     * @param random           random number generator used for initial velocities and some thermostats
     * @param timeStep         time step for integration
     * @param temperature      used by thermostat and/or to initialize velocities
     * @param box
     */
    public IntegratorHardFasterer(IPotentialHard[][] pairPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box) {
        this(pairPotentials, neighborManager, random, timeStep, temperature, box, null, null);
    }

    public IntegratorHardFasterer(IPotentialHard[][] pairPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box, SpeciesManager sm, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        this(pairPotentials, null, neighborManager, random, timeStep, temperature, box, sm, bondingInfo);
    }

    public IntegratorHardFasterer(IPotentialHard[][] pairPotentials, IPotentialHardField[] fieldPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box, SpeciesManager sm, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        this(makeTotalCompute(box, pairPotentials, neighborManager, fieldPotentials, sm, bondingInfo), pairPotentials, fieldPotentials, neighborManager, random, timeStep, temperature, box, bondingInfo);
    }

    public IntegratorHardFasterer(PotentialCompute pc, IPotentialHard[][] pairPotentials, IPotentialHardField[] fieldPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        super(pc, random, timeStep, temperature, box);
        this.pairPotentials = pairPotentials;
        this.fieldPotentials = fieldPotentials;
        this.neighborManager = neighborManager;
        this.bondingInfo = bondingInfo;
        eventBinsFirstAtom = new int[10];
        collisionVector = new Vector[0];
        resizeArrays();
        int n = pairPotentials.length;
        fieldPotentials = new IPotentialHardField[n];
        // we want to store enough collisions that we don't have to recompute them
        // before the neighbor update
        collisionUpdateInterval = 5;
        tMax = collisionUpdateInterval * timeStep;
        neighborIterator = neighborManager.makeNeighborIterator();
        if (neighborManager instanceof NeighborListManagerFasterer) {
            ((NeighborListManagerFasterer) neighborManager).addListener(this);
        }
        collisionListeners = new ArrayList<>(0);
    }

    private static PotentialCompute makeTotalCompute(PotentialComputePair pcPair, PotentialComputeField pcField) {
        if (pcPair == null) return pcField;
        if (pcField == null) return pcPair;
        return new PotentialComputeAggregate(pcField, pcPair);
    }

    public void setCollisionUpdateInterval(int newInterval) {
        collisionUpdateInterval = newInterval;
        tMax = collisionUpdateInterval * timeStep;
        if (initialized) reset();
    }

    public void addCollisionListener(CollisionListener newListener) {
        collisionListeners.add(newListener);
    }

    public void setTimeStep(double dt) {
        super.setTimeStep(dt);
        tMax = collisionUpdateInterval * timeStep;
        if (initialized) reset();
    }

    public void resetStepCount() {
        super.resetStepCount();
        lastTimingPrint = 0;
    }

    protected void resizeArrays() {
        int nAtoms = box.getLeafList().size();
        if (collisionVector.length < nAtoms) {
            collisionTimes = new double[nAtoms];
            collisionPartners = new int[nAtoms];
            eventBinsNextAtom = new int[nAtoms];
            eventBinsPrevAtom = new int[nAtoms];
            collisionOldState = new int[nAtoms];
            if (bondingInfo != null) {
                bondState = new Int2IntHash[nAtoms];
                for (int i = 0; i < nAtoms; i++) {
                    bondState[i] = new Int2IntHash(2);
                }
            }<commit_msg>allow a collision listener to be removed<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package etomica.integrator;

import etomica.atom.AtomArrayList;
import etomica.atom.IAtom;
import etomica.atom.IAtomKinetic;
import etomica.atom.IAtomList;
import etomica.box.Box;
import etomica.box.BoxMoleculeEvent;
import etomica.molecule.IMolecule;
import etomica.nbr.list.INeighborListListener;
import etomica.nbr.list.NeighborListManagerFasterer;
import etomica.potential.*;
import etomica.potential.compute.*;
import etomica.space.Vector;
import etomica.species.SpeciesManager;
import etomica.util.collections.Int2IntHash;
import etomica.util.random.IRandom;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class IntegratorHardFasterer extends IntegratorMDFasterer implements INeighborListListener {

    protected final NeighborManagerHard neighborManager;
    protected final PotentialMasterBonding.FullBondingInfo bondingInfo;
    protected double[] collisionTimes;
    protected int[] collisionPartners;
    protected IPotentialHard[] collisionPotentials;
    protected Vector[] collisionVector;
    protected int[] eventBinsFirstAtom, eventBinsNextAtom, eventBinsPrevAtom;
    protected int[] collisionOldState;
    protected final IPotentialHard[][] pairPotentials;
    protected final IPotentialHardField[] fieldPotentials;
    protected double tBase, tMax;
    protected int collisionUpdateInterval, collisionUpdateCountdown;
    protected long collisionCount;
    protected final NeighborIterator neighborIterator;
    protected final List<CollisionListener> collisionListeners;
    private int lastBin = 0;
    private Int2IntHash[] bondState;
    private int[] fieldState;

    private long tDelete, tAdd, tNext, tSteps, tUpdate, tUp, tDown, tBump, tData, tCollect, tCollision, tAdvance, tNotStep;
    private final boolean writeTiming = false, verbose = false;
    private long lastTimingPrint = 0;
    private int handlingEvent = 0;

    private long nanoTime() {
        return writeTiming ? System.nanoTime() : 0;
    }

    public static IPotentialHard[][] extractHardPotentials(PotentialComputePair pcPair) {
        Potential2Soft[][] softPotentials = pcPair.getPairPotentials();
        int n = softPotentials.length;
        IPotentialHard[][] pairPotentials = new IPotentialHard[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pairPotentials[i][j] = (IPotentialHard) softPotentials[i][j];
            }
        }
        return pairPotentials;
    }

    public static IPotentialHard[][] extractHardPotentials(PotentialMasterFasterer pcPair) {
        Potential2Soft[][] softPotentials = pcPair.getPairPotentials();
        int n = softPotentials.length;
        IPotentialHard[][] pairPotentials = new IPotentialHard[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pairPotentials[i][j] = (IPotentialHard) softPotentials[i][j];
            }
        }
        return pairPotentials;
    }

    public static IPotentialHard[][] extractHardPotentials(PotentialComputePairGeneral pcPair) {
        IPotentialPair[][] softPotentials = pcPair.getPairPotentials();
        int n = softPotentials.length;
        IPotentialHard[][] pairPotentials = new IPotentialHard[n][n];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pairPotentials[i][j] = (IPotentialHard) softPotentials[i][j];
            }
        }
        return pairPotentials;
    }

    public static IPotentialHardField[] extractFieldPotentials(PotentialComputeField pcField) {
        IPotentialField[] softFieldPotentials = pcField.getFieldPotentials();
        int n = softFieldPotentials.length;
        IPotentialHardField[] fieldPotentials = new IPotentialHardField[n];
        for (int i = 0; i < n; i++) {
            fieldPotentials[i] = (IPotentialHardField) softFieldPotentials[i];
        }
        return fieldPotentials;
    }

    private static PotentialCompute makeTotalCompute(Box box, IPotentialHard[][] pairPotentials, NeighborManagerHard neighborManager, IPotentialHardField[] fieldPotentials, SpeciesManager sm, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        int n = pairPotentials.length;
        IPotentialPair[][] softPotentials = new IPotentialPair[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                softPotentials[i][j] = (IPotentialPair) pairPotentials[i][j];
            }
        }
        List<PotentialCompute> computes = new ArrayList<>();
        PotentialComputePairGeneral pcPair = new PotentialComputePairGeneral(softPotentials, box, neighborManager);
        computes.add(pcPair);
        if (fieldPotentials != null) {
            PotentialComputeField pcField = new PotentialComputeField(fieldPotentials, box);
            computes.add(pcField);
        }
        if (bondingInfo != null) {
            PotentialMasterBonding pmBonding = new PotentialMasterBonding(sm, box, bondingInfo);
            computes.add(pmBonding);
        }
        if (computes.size() == 1) return pcPair;
        PotentialCompute[] computesArray = computes.toArray(new PotentialCompute[0]);
        return new PotentialComputeAggregate(computesArray);
    }

    /**
     * Constructs integrator with a default for non-isothermal sampling.
     *
     * @param random           random number generator used for initial velocities and some thermostats
     * @param timeStep         time step for integration
     * @param temperature      used by thermostat and/or to initialize velocities
     * @param box
     */
    public IntegratorHardFasterer(IPotentialHard[][] pairPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box) {
        this(pairPotentials, neighborManager, random, timeStep, temperature, box, null, null);
    }

    public IntegratorHardFasterer(IPotentialHard[][] pairPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box, SpeciesManager sm, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        this(pairPotentials, null, neighborManager, random, timeStep, temperature, box, sm, bondingInfo);
    }

    public IntegratorHardFasterer(IPotentialHard[][] pairPotentials, IPotentialHardField[] fieldPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box, SpeciesManager sm, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        this(makeTotalCompute(box, pairPotentials, neighborManager, fieldPotentials, sm, bondingInfo), pairPotentials, fieldPotentials, neighborManager, random, timeStep, temperature, box, bondingInfo);
    }

    public IntegratorHardFasterer(PotentialCompute pc, IPotentialHard[][] pairPotentials, IPotentialHardField[] fieldPotentials, NeighborManagerHard neighborManager, IRandom random, double timeStep, double temperature, Box box, PotentialMasterBonding.FullBondingInfo bondingInfo) {
        super(pc, random, timeStep, temperature, box);
        this.pairPotentials = pairPotentials;
        this.fieldPotentials = fieldPotentials;
        this.neighborManager = neighborManager;
        this.bondingInfo = bondingInfo;
        eventBinsFirstAtom = new int[10];
        collisionVector = new Vector[0];
        resizeArrays();
        int n = pairPotentials.length;
        fieldPotentials = new IPotentialHardField[n];
        // we want to store enough collisions that we don't have to recompute them
        // before the neighbor update
        collisionUpdateInterval = 5;
        tMax = collisionUpdateInterval * timeStep;
        neighborIterator = neighborManager.makeNeighborIterator();
        if (neighborManager instanceof NeighborListManagerFasterer) {
            ((NeighborListManagerFasterer) neighborManager).addListener(this);
        }
        collisionListeners = new ArrayList<>(0);
    }

    private static PotentialCompute makeTotalCompute(PotentialComputePair pcPair, PotentialComputeField pcField) {
        if (pcPair == null) return pcField;
        if (pcField == null) return pcPair;
        return new PotentialComputeAggregate(pcField, pcPair);
    }

    public void setCollisionUpdateInterval(int newInterval) {
        collisionUpdateInterval = newInterval;
        tMax = collisionUpdateInterval * timeStep;
        if (initialized) reset();
    }

    public void addCollisionListener(CollisionListener newListener) {
        collisionListeners.add(newListener);
    }

    public void removeCollisionListener(CollisionListener oldListener) {
        collisionListeners.remove(oldListener);
    }

    public void setTimeStep(double dt) {
        super.setTimeStep(dt);
        tMax = collisionUpdateInterval * timeStep;
        if (initialized) reset();
    }

    public void resetStepCount() {
        super.resetStepCount();
        lastTimingPrint = 0;
    }

    protected void resizeArrays() {
        int nAtoms = box.getLeafList().size();
        if (collisionVector.length < nAtoms) {
            collisionTimes = new double[nAtoms];
            collisionPartners = new int[nAtoms];
            eventBinsNextAtom = new int[nAtoms];
            eventBinsPrevAtom = new int[nAtoms];
            collisionOldState = new int[nAtoms];
            if (bondingInfo != null) {
                bondState = new Int2IntHash[nAtoms];
                for (int i = 0; i < nAtoms; i++) {
                    bondState[i] = new Int2IntHash(2);
                }
            }
{'fraction_non_alphanumeric': 0.0880516129032258, 'fraction_numerical': 0.003406451612903226, 'mean_word_length': 3.3463436518618215, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6080645', 'n_tokens_mistral': 5611, 'n_tokens_neox': 5350, 'n_words': 1375}