void decode(XMLDecoder in) throws Exception {

		super.decode(in);

		if (nt != null)
			nt.removeParameterChangeListener(this);
		if (pl != null)
			pl.removeParameterChangeListener(this);
		if (ws != null)
			ws.removeParameterChangeListener(this);
		if (ck != null)
			ck.removeParameterChangeListener(this);
		if (mo != null)
			mo.removeParameterChangeListener(this);
		if (affinity != null)
			affinity.destroy();
		if (quantumRule != null)
			quantumRule.reset();

		/* read elements */
		nt = (Element) in.readObject();
		pl = (Element) in.readObject();
		ws = (Element) in.readObject();
		ck = (Element) in.readObject();
		affinity = (Affinity) in.readObject();
		nt.addParameterChangeListener(this);
		pl.addParameterChangeListener(this);
		ws.addParameterChangeListener(this);
		ck.addParameterChangeListener(this);
		mo.addParameterChangeListener(this);
		affinity = new Affinity(new Element[] { nt, pl, ws, ck, mo }, affinity);
		affinity.addParameterChangeListener(this);

		monitor.setProgressMessage("Reading model state...");

		final State state = (State) in.readObject();
		setLJBetweenBondPairs(state.getLJBetweenBondPairs());
		setInterCoulomb(state.getInterCoulomb());
		setUniverse(state.getUniverse() == null ? new Universe() : state.getUniverse());
		setSubatomicEnabled(state.getPhotonEnabled());
		setLightSource(state.getLightSource() == null ? new LightSource() : state.getLightSource());
		photonGun.setInterval(lightSource.getRadiationPeriod());
		quantumRule = state.getQuantumRule() != null ? state.getQuantumRule() : new QuantumRule();
		enableReminder(state.getReminderEnabled());
		if (isReminderEnabled()) {
			reminder.setInterval(state.getReminderInterval());
			reminder.setLifetime(state.getRepeatReminder() ? Loadable.ETERNAL : reminder.getInterval());
			reminderMessage = state.getReminderMessage();
		}
		setAtomFlowEnabled(state.isAtomFlowEnabled());
		if (isAtomFlowEnabled()) {
			setFlowInSide(state.getFlowInSide());
			setFlowOutSide(state.getFlowOutSide());
			Byte[] type = state.getFlowInType();
			byte[] type2 = new byte[type.length];
			for (byte i = 0; i < type.length; i++)
				type2[i] = type[i].byteValue();
			setFlowInType(type2);
			type = state.getFlowOutType();
			type2 = new byte[type.length];
			for (byte i = 0; i < type.length; i++)
				type2[i] = type[i].byteValue();
			setFlowOutType(type2);
			setFlowInterval(state.getFlowInterval());
		}
		monitor.setMaximum(2 * state.getNumberOfParticles() + 40);
		setCutOffMatrix(state.getCutOff());
		setListMatrix(state.getRList());
		setCutOffShift(state.getCutOffShift());
		setTimeStep(state.getTimeStep());
		setInitializationScript(state.getScript());
		setLightSimulationSpeed((float) getTimeStep());
		mo.setEpsilon(state.getMoEpsilon());
		mo.setMass(state.getMoMass());
		String mprop = null;
		for (Iterator it = state.getProperties().keySet().iterator(); it.hasNext();) {
			mprop = (String) it.next();
			if (!mprop.equals("url") && !mprop.equals("filename") && !mprop.equals("codebase") && !mprop.equals("date") && !mprop.equals("size"))
				putProperty(mprop, state.getProperties().get(mprop));
		}
		monitor.setProgressMessage("Retrieving obstacles...");
		setObstacles(state.getObstacles());
		kine.clear();
		pote.clear();
		tote.clear();
		for (FloatQueue q : channelTs)
			q.clear();
		for (FloatQueue q : kep)
			q.clear();
		Arrays.fill(channels, 0);
		movieUpdater.setInterval(state.getFrameInterval());
		paintView.setInterval(state.getViewRefreshInterval());
		if (heatBath != null)
			heatBath.destroy();
		heatBath = state.getHeatBath();
		if (heatBath != null)
			heatBath.setModel(this);

		/* restore view */

		monitor.setProgressMessage("Reading view...");
		final AtomisticView.State vs = (AtomisticView.State) in.readObject();
		view.setPropertyDialogEnabled(vs.isPropertyDialogEnabled());
		view.setRenderingMethod(vs.getRenderingMethod());
		view.setUseJmol(vs.getUseJmol() || !vs.getMonochromatic());
		view.setElementColors(vs.getElementColors());
		view.setFillMode(vs.getFillMode());
		view.setBackground(vs.getBackground());
		view.setMarkColor(new Color(vs.getMarkColor()));
		view.setEnergizer(vs.getEnergizer());
		view.setDrawCharge(vs.getDrawCharge());
		view.setDrawExternalForce(vs.getDrawExternalForce());
		view.setShowParticleIndex(vs.getShowParticleIndex());
		view.setShowMirrorImages(vs.getShowMirrorImages());
		view.setColorCoding(vs.getColorCode());
		view.setShowClock(vs.getShowClock());
		view.setShowHeatBath(vs.getShowHeatBath());
		view.setShowSites(false);
		view.showVDWCircles(vs.getShowVDWCircles());
		view.setVDWCircleStyle(vs.getVDWCircleStyle());
		view.showVDWLines(vs.getShowVDWLines());
		view.setVDWLinesRatio(vs.getVDWLinesRatio());
		view.setVdwLineThickness(vs.getVDWLineThickness());
		view.showChargeLines(vs.getShowChargeLines());
		view.showSSLines(vs.getShowSSLines());
		view.showBPLines(vs.getShowBPLines());
		view.showEFieldLines(vs.getShowEFieldLines());
		view.setEFCellSize(vs.getEFCellSize());
		view.setEFShadingMode(vs.getEFShadingMode());
		view.showShading(vs.getShading());
		view.showChargeShading(vs.getChargeShading());
		view.showVelocityVector(vs.getShowVVectors());
		view.showMomentumVector(vs.getShowPVectors());
		view.showAccelerationVector(vs.getShowAVectors());
		view.showForceVector(vs.getShowFVectors());
		view.setDisplayStyle(vs.getDisplayStyle());
		view.setRestraintStyle(vs.getRestraintStyle());
		view.setTrajectoryStyle(vs.getTrajectoryStyle());
		view.setVelocityFlavor(vs.getVelocityFlavor());
		view.setMomentumFlavor(vs.getMomentumFlavor());
		view.setAccelerationFlavor(vs.getAccelerationFlavor());
		view.setForceFlavor(vs.getForceFlavor());
		view.showExcitation(vs.getShowExcitation());

		/* restore atom by atom */

		Atom at = null;
		int pointer = 0;
		int nas = state.getNumberOfParticles();
		if (nas > atom.length) {
			nas = atom.length;
			EventQueue.invokeLater(new Runnable() {
				public void run() {
					JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(view), "The model contains more particles than default.", "Error", JOptionPane.ERROR_MESSAGE);
				}
			});
		}
		while (pointer < nas) {
			if (pointer % 10 == 0)
				monitor.setProgressMessage("Reading atom " + pointer + "...");
			at = (Atom) in.readObject();
			atom[pointer].destroy();
			atom[pointer] = at;
			atom[pointer].setIndex(pointer);
			atom[pointer].setModel(this);
			atom[pointer].fx = atom[pointer].ax * atom[pointer].mass;
			atom[pointer].fy = atom[pointer].ay * atom[pointer].mass;
			String savedCodon = at.getCodon();
			switch (atom[pointer].id) {
			case ID_NT:
				atom[pointer].setElement(nt);
				break;
			case ID_PL:
				atom[pointer].setElement(pl);
				break;
			case ID_WS:
				atom[pointer].setElement(ws);
				break;
			case ID_CK:
				atom[pointer].setElement(ck);
				break;
			case ID_MO:
				atom[pointer].setElement(mo);
				break;
			case ID_SP:
				atom[pointer].setElement(sp);
				break;
			case ID_ALA:
				atom[pointer].setElement(aminoAcidElement[ID_ALA - ID_ALA]);
				break;
			case ID_ARG:
				atom[pointer].setElement(aminoAcidElement[ID_ARG - ID_ALA]);
				break;
			case ID_ASN:
				atom[pointer].setElement(aminoAcidElement[ID_ASN - ID_ALA]);
				break;
			case ID_ASP:
				atom[pointer].setElement(aminoAcidElement[ID_ASP - ID_ALA]);
				break;
			case ID_CYS:
				atom[pointer].setElement(aminoAcidElement[ID_CYS - ID_ALA]);
				break;
			case ID_GLN:
				atom[pointer].setElement(aminoAcidElement[ID_GLN - ID_ALA]);
				break;
			case ID_GLU:
				atom[pointer].setElement(aminoAcidElement[ID_GLU - ID_ALA]);
				break;
			case ID_GLY:
				atom[pointer].setElement(aminoAcidElement[ID_GLY - ID_ALA]);
				break;
			case ID_HIS:
				atom[pointer].setElement(aminoAcidElement[ID_HIS - ID_ALA]);
				break;
			case ID_ILE:
				atom[pointer].setElement(aminoAcidElement[ID_ILE - ID_ALA]);
				break;
			case ID_LEU:
				atom[pointer].setElement(aminoAcidElement[ID_LEU - ID_ALA]);
				break;
			case ID_LYS:
				atom[pointer].setElement(aminoAcidElement[ID_LYS - ID_ALA]);
				break;
			case ID_MET:
				atom[pointer].setElement(aminoAcidElement[ID_MET - ID_ALA]);
				break;
			case ID_PHE:
				atom[pointer].setElement(aminoAcidElement[ID_PHE - ID_ALA]);
				break;
			case ID_PRO:
				atom[pointer].setElement(aminoAcidElement[ID_PRO - ID_ALA]);
				break;
			case ID_SER:
				atom[pointer].setElement(aminoAcidElement[ID_SER - ID_ALA]);
				break;
			case ID_THR:
				atom[pointer].setElement(aminoAcidElement[ID_THR - ID_ALA]);
				break;
			case ID_TRP:
				atom[pointer].setElement(aminoAcidElement[ID_TRP - ID_ALA]);
				break;
			case ID_TYR:
				atom[pointer].setElement(aminoAcidElement[ID_TYR - ID_ALA]);
				break;
			case ID_VAL:
				atom[pointer].setElement(aminoAcidElement[ID_VAL - ID_ALA]);
				break;
			case ID_A:
				atom[pointer].setElement(nucleotideElement[ID_A - ID_A]);
				break;
			case ID_C:
				atom[pointer].setElement(nucleotideElement[ID_C - ID_A]);
				break;
			case ID_G:
				atom[pointer].setElement(nucleotideElement[ID_G - ID_A]);
				break;
			case ID_T:
				atom[pointer].setElement(nucleotideElement[ID_T - ID_A]);
				break;
			case ID_U:
				atom[pointer].setElement(nucleotideElement[ID_U - ID_A]);
				break;
			}
			if (atom[pointer].isAminoAcid() && savedCodon != null)
				atom[pointer].setCodon(savedCodon);
			if (!atom[pointer].isMovable())
				atom[pointer].vx = atom[pointer].vy = 0;
			pointer++;
		}
		setNumberOfAtoms(pointer);
		checkCharges();

		/* finally restore boundary. This should be placed at last. */

		monitor.setProgressMessage("Retrieving boundary...");
		boundary.constructFromDelegate(state.getBoundary());
		if (state.getFields() != null && !state.getFields().isEmpty()) {
			monitor.setProgressMessage("Retrieving fields...");
			addAllNonLocalFields(state.getFields());
		}
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				view.resize(state.getBoundary().getViewSize(), true);
				EventQueue.invokeLater(new Runnable() {
					// anything that depends on the size must be called in the event queue after view is resized
					public void run() {
						if (vs.getShowContour()) {
							Atom probe = createAtomOfElement(vs.getProbeID());
							probe.setCharge(vs.getProbeCharge());
							view.showContourPlot(true, probe, view.getPreferredSize());
							view.repaint();
						}
						if (view.getUseJmol())
							view.refreshJmol();
					}
				});
			}
		});

		decodeBonds(in, vs);
		loadLayeredComponent(vs);

		ExcitedStates excitedStates = state.getExcitedStates();
		if (excitedStates != null) {
			Map<Integer, int[]> excitationMap = excitedStates.getExcitationMap();
			if (excitationMap != null && !excitationMap.isEmpty()) {
				for (Integer iat : excitationMap.keySet()) {
					int[] excite = excitationMap.get(iat);
					if (excite != null && excite.length >= 2) {
						EnergyLevel level = getElement(atom[iat].id).getElectronicStructure().getEnergyLevel(excite[1]);
						atom[iat].getElectron(excite[0]).setEnergyLevel(level);
					}
				}
			}
		}

		initializeJob();
		addCustomTasks(state.getTasks());
		if (heatBath != null && !job.contains(heatBath))
			job.add(heatBath);
		job.processPendingRequests();
		computeForce(-1); // call to build the neighbor list

	}