Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions vcell-client/src/main/java/cbit/plot/gui/ClusterDataPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import cbit.vcell.parser.Expression;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.parser.SymbolTableEntry;
import cbit.vcell.simdata.LangevinSolverResultSet;
import cbit.vcell.simdata.UiTableExporterToHDF5;
import cbit.vcell.solver.ode.ODESolverResultSet;
import cbit.vcell.solver.ode.gui.ClusterSpecificationPanel;
Expand Down Expand Up @@ -83,7 +84,12 @@ public Component getTableCellRendererComponent(
text = "<html>" + name + "<font color=\"#8B0000\"> [" + unit + "]</font></html>";
tooltip = "<html>Number of clusters made of <b>" + name + "</b> " + unit + "</html>";
break;

case MASS:
unit = "molecules";
text = "<html>" + name + "<font color=\"#8B0000\"> [" + unit + "]</font></html>";
tooltip = "<html>Cluster size <b>" + name + "</b><br>" +
"Total molecules = " + name + " × (cluster count)</html>";
break;
case MEAN:
case OVERALL:
if (stat != null) {
Expand Down Expand Up @@ -254,7 +260,8 @@ public void updateData(ClusterSpecificationPanel.ClusterSelection sel)
return;
}

ODESolverResultSet srs = sel.resultSet;
LangevinSolverResultSet lsrs = sel.resultSet;
ODESolverResultSet srs = ClusterSpecificationPanel.getResultSetForMode(lsrs, sel.mode);
java.util.List<ColumnDescription> columns = sel.columns;

int timeIndex = srs.findColumn(ReservedVariable.TIME.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public enum DisplayMode {
"Cluster Counts",
"Show the number of clusters of each size"
),
MASS( // molecule-weighted cluster count (cluster mass distribution)
"MASS",
"Cluster Mass",
"Show the number of molecules contained in clusters of each size (count × size)"
),
MEAN(
"MEAN",
"Cluster Mean",
Expand Down Expand Up @@ -116,8 +121,8 @@ public static ClusterStatistic fromString(String s) {
public static class ClusterSelection { // used to communicate y-list selection to the ClusterVisualizationPanel
public final DisplayMode mode;
public final java.util.List<ColumnDescription> columns;
public final ODESolverResultSet resultSet;
public ClusterSelection(DisplayMode mode, java.util.List<ColumnDescription> columns, ODESolverResultSet resultSet) {
public final LangevinSolverResultSet resultSet;
public ClusterSelection(DisplayMode mode, java.util.List<ColumnDescription> columns, LangevinSolverResultSet resultSet) {
this.mode = mode;
this.columns = columns;
this.resultSet = resultSet;
Expand Down Expand Up @@ -154,6 +159,9 @@ private String buildTooltip(ODESolverResultSetColumnDescription cd, DisplayMode
case COUNTS ->
"<html>Number of clusters of size <b>" + name +
"</b> <font color=\"#8B0000\">[molecules]</font></html>";
case MASS ->
"<html>Number of molecules in the clusters of size <b>" + name +
"</b> <font color=\"#8B0000\">[molecules]</font></html>";
case MEAN, OVERALL -> {
ClusterStatistic stat = ClusterStatistic.fromString(name);
if(stat == null) {
Expand Down Expand Up @@ -199,12 +207,12 @@ public void valueChanged(ListSelectionEvent e) {
// extract selected ColumnDescriptions
java.util.List<ColumnDescription> selected = getYAxisChoice().getSelectedValuesList();
DisplayMode mode = getCurrentDisplayMode();
ODESolverResultSet srs = getResultSetForMode(mode);
// ODESolverResultSet srs = getResultSetForMode(mode);
// moved this to actionPerformed() where it belongs, it was being called too late here
// // set property to inform the list about current mode (needed for renderer)
// yAxisChoiceList.putClientProperty("ClusterDisplayMode", mode);
// fire the event upward
firePropertyChange("ClusterSelection", null, new ClusterSelection(mode, selected, srs));
firePropertyChange("ClusterSelection", null, new ClusterSelection(mode, selected, langevinSolverResultSet));
}
}
};
Expand Down Expand Up @@ -324,18 +332,22 @@ protected CollapsiblePanel getDisplayOptionsPanel() {
ButtonGroup group = new ButtonGroup();

JRadioButton rbCounts = new JRadioButton(DisplayMode.COUNTS.uiLabel());
JRadioButton rbMass = new JRadioButton(DisplayMode.MASS.uiLabel());
JRadioButton rbMean = new JRadioButton(DisplayMode.MEAN.uiLabel());
JRadioButton rbOverall = new JRadioButton(DisplayMode.OVERALL.uiLabel());

rbCounts.setActionCommand(DisplayMode.COUNTS.actionCommand());
rbMass.setActionCommand(DisplayMode.MASS.actionCommand());
rbMean.setActionCommand(DisplayMode.MEAN.actionCommand());
rbOverall.setActionCommand(DisplayMode.OVERALL.actionCommand());

rbCounts.setToolTipText(DisplayMode.COUNTS.tooltip());
rbMass.setToolTipText(DisplayMode.MASS.tooltip());
rbMean.setToolTipText(DisplayMode.MEAN.tooltip());
rbOverall.setToolTipText(DisplayMode.OVERALL.tooltip());

group.add(rbCounts);
group.add(rbMass);
group.add(rbMean);
group.add(rbOverall);

Expand All @@ -348,8 +360,10 @@ protected CollapsiblePanel getDisplayOptionsPanel() {
gbc.gridy = 0;
content.add(rbCounts, gbc);
gbc.gridy = 1;
content.add(rbMean, gbc);
content.add(rbMass, gbc);
gbc.gridy = 2;
content.add(rbMean, gbc);
gbc.gridy = 3;
content.add(rbOverall, gbc);
}
return cp;
Expand Down Expand Up @@ -421,6 +435,7 @@ public void refreshData() {
yAxisCounts.clear();
if (langevinSolverResultSet != null) {
yAxisCounts.put(DisplayMode.COUNTS, countColumns(langevinSolverResultSet.getClusterCounts()));
yAxisCounts.put(DisplayMode.MASS, countColumns(langevinSolverResultSet.getClusterMass()));
yAxisCounts.put(DisplayMode.MEAN, countColumns(langevinSolverResultSet.getClusterMean()));
yAxisCounts.put(DisplayMode.OVERALL, countColumns(langevinSolverResultSet.getClusterOverall()));
}
Expand All @@ -443,19 +458,21 @@ private int countColumns(ODESolverResultSet srs) {
if (cds == null) return 0;
return cds.length > 1 ? cds.length-1 : 0; // subtract one for time column, but don't return negative if no column at all
}
private ODESolverResultSet getResultSetForMode(DisplayMode mode) {
if (langevinSolverResultSet == null) {

public static ODESolverResultSet getResultSetForMode(LangevinSolverResultSet lsrs, DisplayMode mode) {
if (lsrs == null) {
return null;
}
return switch (mode) {
case COUNTS -> langevinSolverResultSet.getClusterCounts();
case MEAN -> langevinSolverResultSet.getClusterMean();
case OVERALL-> langevinSolverResultSet.getClusterOverall();
case COUNTS -> lsrs.getClusterCounts();
case MASS -> lsrs.getClusterMass();
case MEAN -> lsrs.getClusterMean();
case OVERALL-> lsrs.getClusterOverall();
};
}

private ColumnDescription[] getColumnDescriptionsForMode(DisplayMode mode) {
ODESolverResultSet srs = getResultSetForMode(mode);
ODESolverResultSet srs = getResultSetForMode(langevinSolverResultSet, mode);
return (srs == null ? null : srs.getColumnDescriptions());
}
private DisplayMode getCurrentDisplayMode() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cbit.vcell.solver.ode.gui;

import cbit.plot.gui.ClusterDataPanel;
import cbit.vcell.simdata.LangevinSolverResultSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand Down Expand Up @@ -70,11 +71,11 @@ public void propertyChange(PropertyChangeEvent evt) {
redrawLegend(sel); // redraw legend (one plot, multiple curves)
redrawPlot(sel); // redraw plot (one plot, multiple curves)
redrawDataTable(sel); // redraw data table
if(sel.mode == ClusterSpecificationPanel.DisplayMode.COUNTS) {
getClusterPlotPanel().setShowAvgAsStep(true);
} else {
getClusterPlotPanel().setShowAvgAsStep(false);
}
// if(sel.mode == ClusterSpecificationPanel.DisplayMode.COUNTS) {
// getClusterPlotPanel().setShowAvgAsStep(true);
// } else {
// getClusterPlotPanel().setShowAvgAsStep(false);
// }
} catch (ExpressionException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -280,6 +281,12 @@ private JComponent createLegendEntry(String name, Color color, ClusterSpecificat
unitSymbol = "molecules";
tooltip = "<html>cluster size <b>" + name + "</b> molecules</html>";
break;
case MASS:
// name is the cluster size (e.g., "3")
unitSymbol = "molecules";
tooltip = "<html>cluster size <b>" + name + "</b><br>" +
"total molecules = " + name + " × (cluster count)</html>";
break;
case MEAN:
case OVERALL:
// name is ACS / SD / ACO
Expand Down Expand Up @@ -317,7 +324,8 @@ private void redrawPlot(ClusterSpecificationPanel.ClusterSelection sel) throws E
return;
}
List<ColumnDescription> columns = sel.columns;
ODESolverResultSet srs = sel.resultSet;
LangevinSolverResultSet lsrs = sel.resultSet;
ODESolverResultSet srs = ClusterSpecificationPanel.getResultSetForMode(lsrs, sel.mode);
int indexTime = srs.findColumn("t");
double[] times = srs.extractColumn(indexTime);

Expand Down Expand Up @@ -429,7 +437,7 @@ private void redrawLegend(ClusterSpecificationPanel.ClusterSelection sel) {
Color cACS = persistentColorMap.get("ACS");
c = new Color(cACS.getRed(), cACS.getGreen(), cACS.getBlue(), SD_ALPHA); // alpha 0–255
} else {
// ACS, ACO, COUNTS all use their assigned colors
// ACS, ACO, COUNTS, MASS all use their assigned colors
c = persistentColorMap.get(name);
}
getLegendContentPanel().add(createLegendEntry(name, c, sel.mode));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class LangevinClustersResultsPanel extends DocumentEditorSubPanel {
private JToolBarToggleButton ivjDataButton = null;


private enum DisplayMode { COUNTS, MEAN, OVERALL }
private enum DisplayMode { COUNTS, MASS, MEAN, OVERALL }
LangevinSolverResultSet langevinSolverResultSet = null;
SimulationModelInfo simulationModelInfo = null;

Expand Down Expand Up @@ -82,6 +82,9 @@ public void actionPerformed(ActionEvent e) {
case "COUNTS":
populateYAxisChoices(DisplayMode.COUNTS);
break;
case "MASS":
populateYAxisChoices(DisplayMode.MASS);
break;
case "MEAN":
populateYAxisChoices(DisplayMode.MEAN);
break;
Expand Down Expand Up @@ -129,6 +132,10 @@ private void populateYAxisChoices(DisplayMode mode) {
srs = langevinSolverResultSet.getClusterCounts();
cd = srs.getColumnDescriptions();
break;
case MASS:
srs = langevinSolverResultSet.getClusterMass();
cd = srs.getColumnDescriptions();
break;
case MEAN:
srs = langevinSolverResultSet.getClusterMean();
cd = srs.getColumnDescriptions();
Expand Down Expand Up @@ -226,18 +233,22 @@ private CollapsiblePanel getDisplayOptionsPanel() {

ButtonGroup group = new ButtonGroup();
JRadioButton rbCounts = new JRadioButton("Cluster Counts");
JRadioButton rbMass = new JRadioButton("Cluster Mass");
JRadioButton rbMean = new JRadioButton("Cluster Mean");
JRadioButton rbOverall = new JRadioButton("Cluster Overall");

rbCounts.setActionCommand("COUNTS");
rbMass.setActionCommand("MASS");
rbMean.setActionCommand("MEAN");
rbOverall.setActionCommand("OVERALL");

rbCounts.addActionListener(ivjEventHandler);
rbMass.addActionListener(ivjEventHandler);
rbMean.addActionListener(ivjEventHandler);
rbOverall.addActionListener(ivjEventHandler);

group.add(rbCounts);
group.add(rbMass);
group.add(rbMean);
group.add(rbOverall);

Expand All @@ -250,8 +261,10 @@ private CollapsiblePanel getDisplayOptionsPanel() {
gbc.gridy = 0;
content.add(rbCounts, gbc);
gbc.gridy = 1;
content.add(rbMean, gbc);
content.add(rbMass, gbc);
gbc.gridy = 2;
content.add(rbMean, gbc);
gbc.gridy = 3;
content.add(rbOverall, gbc);
}
return displayOptionsCollapsiblePanel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ public void gatherIssues(IssueContext issueContext, List<Issue> issueList, React
if(!(SpringStructureEnum.Membrane.columnName.equals(reactionRule.getStructure().getName()))) {
String msg = SpringSaLaDMsgAnchorReactionMustMembrane;
String tip = msg;
issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR));
issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING));
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package cbit.vcell.simdata;

import cbit.vcell.math.ODESolverResultSetColumnDescription;
import cbit.vcell.math.RowColumnResultSet;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.solver.DataSymbolMetadata;
import cbit.vcell.solver.SimulationModelInfo;
import cbit.vcell.solver.ode.ODESimData;
import cbit.vcell.solver.ode.ODESolverResultSet;
import cbit.vcell.units.VCUnitDefinition;
import cbit.vcell.util.ColumnDescription;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.vcell.model.ssld.SsldUtils;
import org.vcell.util.document.KeyValue;
import org.vcell.util.document.User;
import org.vcell.util.document.VCDataIdentifier;

import java.io.*;
import java.util.HashMap;
Expand All @@ -26,6 +31,9 @@ public LangevinSolverResultSet(LangevinBatchResultSet raw) {
}
public final Map<String, SsldUtils.LangevinResult> metadataMap = new LinkedHashMap<>();

// derived data, based on raw, populated in postProcess()
private ODESimData clusterMass = null;


// // safe getter that returns a deep copy, but I don't think we need it
// public LangevinBatchResultSet getLangevinBatchResultSetSafe() {
Expand All @@ -48,6 +56,9 @@ public ODESimData getStd() {
public ODESimData getClusterCounts() {
return raw == null ? null : raw.getOdeSimDataClusterCounts();
}
public ODESimData getClusterMass() {
return clusterMass;
}
public ODESimData getClusterMean() {
return raw == null ? null : raw.getOdeSimDataClusterMean();
}
Expand Down Expand Up @@ -103,6 +114,9 @@ public void postProcess() {
checkTrivial(co);
co = getClusterCounts();
checkTrivial(co);
computeClusterMass();
co = getClusterMass();
checkTrivial(co);
}
if(isAverageDataAvailable()) {
ODESimData co = getAvg();
Expand Down Expand Up @@ -131,6 +145,44 @@ private void populateMetadata(ODESimData co) {
metadataMap.put(columnName, lr);
}
}

private void computeClusterMass() {
ODESimData counts = getClusterCounts();
if (counts == null) {
clusterMass = null;
return;
}
try {
// 1. Deep copy counts via serialization
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(counts);
out.flush();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
clusterMass = (ODESimData) in.readObject();

// 2. Modify numeric values in-place
ColumnDescription[] cds = clusterMass.getColumnDescriptions();
int nCols = cds.length;
for (int c = 0; c < nCols; c++) {
ColumnDescription cd = cds[c];
String name = cd.getName();
if (name.equals("t")) {
continue; // time column unchanged
}
int clusterSize = Integer.parseInt(name);
double[] series = clusterMass.extractColumn(c); // reference to internal array
for (int i = 0; i < series.length; i++) {
double value = series[i] * clusterSize;
clusterMass.setValue(i, c, value);
}
}
} catch (Exception e) {
lg.error("Failed to compute cluster mass", e);
clusterMass = null;
}
}

private static void checkTrivial(ODESimData co) {
ColumnDescription[] cds = co.getColumnDescriptions();
for(ColumnDescription columnDescription : cds) {
Expand Down
Loading