From 0cf271983d3704113b38da24fb33cd7238efb9d3 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 3 Jun 2025 12:18:48 -0700 Subject: [PATCH 01/56] started to update some import issues --- .../env/component/PatchComponentGrowth.java | 938 ++++++++++++++++++ .../PatchComponentSitesGraphUtilities.java | 189 ++-- 2 files changed, 1047 insertions(+), 80 deletions(-) create mode 100644 src/arcade/patch/env/component/PatchComponentGrowth.java diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java new file mode 100644 index 000000000..796184ce1 --- /dev/null +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -0,0 +1,938 @@ +package arcade.env.comp; + +import static arcade.env.comp.GraphSites.CAP_RADIUS; +import static arcade.env.comp.GraphSites.CAP_RADIUS_MIN; +import static arcade.env.comp.GraphSites.CAP_RADIUS_MAX; + +import static arcade.env.comp.GraphSitesUtilities.calcThickness; +import static arcade.env.comp.GraphSitesUtilities.path; +import static arcade.env.comp.GraphSitesUtilities.reversePressures; +import static arcade.env.comp.GraphSitesUtilities.getPath; +import static arcade.env.comp.GraphSitesUtilities.updateGraph; +import static arcade.env.comp.GraphSitesUtilities.calcFlows; +import static arcade.env.comp.GraphSitesUtilities.calcPressure; +import static arcade.env.comp.GraphSitesUtilities.calcPressures; +import static arcade.env.comp.GraphSitesUtilities.calcStress; +import static arcade.env.comp.GraphSitesUtilities.calculateLocalFlow; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.logging.Logger; + +import arcade.env.comp.GraphSites.Root; +import arcade.env.comp.GraphSites.SiteEdge; +import arcade.env.comp.GraphSites.SiteNode; +import arcade.env.lat.Lattice; +import arcade.env.loc.Location; +import arcade.sim.Simulation; +import arcade.util.Graph; +import arcade.util.MiniBox; +import arcade.util.Solver; +import arcade.util.Solver.Function; +import ec.util.MersenneTwisterFast; +import sim.util.Bag; +import sim.engine.SimState; + + +/** + * Implementation of {@link Component} for degrading graph edges. + *

+ * This component can only be used with {@link GraphSites}. The + * component is stepped every {@code DEGRADATION_INTERVAL} ticks. wall thickness + * of edges that are adjacent to a location with cancerous cells is decreased + * ({@code DEGRADATION_RATE}). Edges that are below a minimum wall thickness and + * have a shear stress below the shear threshold ({@code SHEAR_THRESHOLD}) are + * removed from the graph. At the end of a step, if no edges have been removed + * from the graph, then only the stresses in the graph are recalculated. + * Otherwise, all hemodynamic properties are recalculated. + */ +public class GrowthComponent implements Component { + private static Logger LOGGER = Logger.getLogger(GrowthComponent.class.getName()); + + /** Calculation strategies. */ + public enum Calculation { + /** Code for upstream calculation strategy. */ + COMPENSATE, + + /** Code for downstream direction strategy. */ + DIVERT + } + + private final double migrationRate; + private final double vegfThreshold; + private final String walkType; + private final double maxLength; + + private int maxEdges; + private int interval; + private double edgeSize; + /** The associated {@link GraphSites} object. */ + private GraphSites sites; + + /** The {@link Graph} object representing the sites. */ + private Graph graph; + + private HashMap> angioMap; + private ArrayList tempEdges; + + private int[][] offsets; + private final int level = 1; + private final int default_type = 0; //Capillary type + + private final MiniBox specs; + + private ArrayList added = new ArrayList<>(); + + public GrowthComponent(MiniBox component) { + // Set loaded parameters. + migrationRate = component.getDouble("MIGRATION_RATE"); + vegfThreshold = component.getDouble("VEGF_THRESHOLD"); + walkType = component.get("WALK_TYPE"); + maxLength = component.getDouble("MAX_LENGTH"); + angioMap = new HashMap<>(); + + // Get list of specifications. + specs = new MiniBox(); + String[] specList = new String[] { "MIGRATION_RATE", "VEGF_THRESHOLD", "WALK_TYPE", "MAX_LENGTH" }; + for (String spec : specList) { specs.put(spec, component.get(spec)); } + } + + /** + * Component does not have a relevant field; returns {@code null}. + * + * @return {@code null} + */ + public double[][][] getField() { return null; } + + @Override + public void scheduleComponent(Simulation sim) { + Component comp = sim.getEnvironment("sites").getComponent("sites"); + if (!(comp instanceof GraphSites)) { + LOGGER.warning("cannot schedule GROWTH component for non-graph sites"); + return; + } + + sites = (GraphSites) comp; + offsets = sites.getOffsets(); + edgeSize = sites.getGridSize(); + maxEdges = (int) Math.floor(maxLength / edgeSize); + interval = calculateInterval(); + ((SimState)sim).schedule.scheduleRepeating(1, Simulation.ORDERING_COMPONENT - 3, this, interval); + ((SimState)sim).schedule.scheduleOnce((state) -> graph = sites.getGraph(), Simulation.ORDERING_COMPONENT - 3); + } + + private int calculateInterval() { + if (migrationRate < edgeSize) { + return 60; + } else { + + return 30; + } + } + + /** + * {@inheritDoc} + *

+ * Degradation component does not use this method. + */ + public void updateComponent(Simulation sim, Location oldLoc, Location newLoc) { } + + @Override + public void step(final SimState state) { + final Simulation sim = (Simulation) state; + final Lattice vegf_lattice = sim.getEnvironment("vegf"); + final MersenneTwisterFast random = state.random; + ArrayList nodesToRemove = new ArrayList<>(); + + final double tick = sim.getTime(); + + // if (((int) tick - 1) % (12*60) == 0) { + // LOGGER.info((int)(tick - 1) + " | number of nodes to check: " + graph.getAllNodes().size()); + // } + + LinkedHashSet set = new LinkedHashSet<>(); + + + for (Object obj : graph.getAllEdges()) { + SiteEdge edge = (SiteEdge)obj; + if (edge.isIgnored) { continue; } + SiteNode from = edge.getFrom(); + SiteNode to = edge.getTo(); + from.id = -1; + to.id = -1; + + if ((graph.getDegree(from) < 3) && !from.isRoot && !(graph.getInDegree(from) == 0 && graph.getOutDegree(from) == 1)) { set.add(from); } + if ((graph.getDegree(to) < 3) && !to.isRoot && !(graph.getInDegree(to) == 1 && graph.getOutDegree(to) == 0)) { set.add(to); } + } + + for (SiteNode node : set) { + if (checkNodeSkipStatus(node, tick)) { continue; } + + ArrayList> vegfList = getVEGFList(vegf_lattice, node); + + if (averageListArrays(vegfList) > vegfThreshold) { + angioMap.put(node, new ArrayList<>()); + ArrayList skipDirList = new ArrayList(); + + Bag in = graph.getEdgesIn(node); + Bag out = graph.getEdgesOut(node); + + if (in != null){ + for (Object edge : in){ + SiteEdge inEdge = (SiteEdge)edge; + skipDirList.add(sites.getOppositeDirection(inEdge, inEdge.level)); + } + } + if (out != null){ + for (Object edge : out){ + SiteEdge outEdge = (SiteEdge)edge; + skipDirList.add(sites.getDirection(outEdge, outEdge.level)); + } + } + + ArrayList vegfAverages = getListAverages(vegfList); + + int newDir; + switch (walkType.toUpperCase()) { + case "RANDOM": + newDir = performRandomWalk(random, node, vegfAverages, tick, skipDirList); + break; + case "BIASED": + newDir = performBiasedWalk(random, node, vegfAverages, tick, skipDirList); + break; + case "MAX": + newDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); + break; + default: + LOGGER.warning("invalid walk type: " + walkType + "; using default of MAX."); + newDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); + } + node.sproutDir = newDir; + } + } + + boolean addFlag = false; + + addTemporaryEdges(); + + for (Map.Entry> entry : angioMap.entrySet()){ + //grab final node in each list and add edge, check for perfusion + SiteNode keyNode = entry.getKey(); + + if (checkForIgnoredEdges(keyNode)) { + nodesToRemove.add(keyNode); + continue; + } + + ArrayList edgeList = entry.getValue(); + SiteNode tipNode; + SiteEdge newEdge; + if (edgeList.size() > 0) { + tipNode = edgeList.get(edgeList.size() - 1).getTo(); + } + else { tipNode = keyNode; } + + if (tick - tipNode.lastUpdate < migrationRate) { continue; } + + newEdge = createNewEdge(keyNode.sproutDir, tipNode, tick); + + if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3 ) { + // LOGGER.info("Removing " + keyNode + " from angiomap, unsuccessful perfusion."); + nodesToRemove.add(keyNode); + } + else { + edgeList.add(newEdge); + if (newEdge.isAnastomotic){ + keyNode.anastomosis = true; + addFlag = true; + } + } + } + + removeTemporaryEdges(); + + if (addFlag) { + added.clear(); + // LOGGER.info("*****Adding edges to graph.****** Time: " + tick); + // LOGGER.info("Current graph size: " + graph.getAllEdges().size()); + for (SiteNode sproutNode : angioMap.keySet()) { + if (nodesToRemove.contains(sproutNode)) { continue; } + if (sproutNode.anastomosis) { + int leadingIndex = angioMap.get(sproutNode).size() - 1; + if (leadingIndex < 0) { + nodesToRemove.add(sproutNode); + continue; + } + SiteNode finalNode = angioMap.get(sproutNode).get(leadingIndex).getTo(); + SiteNode init, fin; + + calcPressures(graph); + boolean reversed = reversePressures(graph); + if (reversed) { calcPressures(graph); } + calcFlows(graph, sites); + calcStress(graph); + + // maybe try to redo by iterating through list rather than using final node + if (!graph.containsNode(finalNode)){ + // LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); + SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); + if (targetNode == null) { + // LOGGER.info("Likely removed node - skipping"); + sproutNode.anastomosis = false; + continue; + } + // path(graph, targetNode, sproutNode); + if (sproutNode.pressure < targetNode.pressure) { + // LOGGER.info("SWAPPING SPROUT NODE"); + reverseAllEdges(sproutNode); + init = targetNode; + fin = sproutNode; + } else { + // LOGGER.info("SWAPPING TARGET NODE"); + reverseAllEdges(targetNode); + init = sproutNode; + fin = targetNode; + } + angioMap.get(sproutNode).addAll(angioMap.get(targetNode)); + + nodesToRemove.add(sproutNode); + nodesToRemove.add(targetNode); + } + else { + if (sproutNode.pressure == 0) { + if (graph.getEdgesOut(sproutNode) != null) {sproutNode = ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom();} + } + if (finalNode.pressure == 0) { + if (graph.getEdgesOut(finalNode) != null) {finalNode = ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom();} + } + // path(graph, finalNode, sproutNode); + if (sproutNode.pressure < finalNode.pressure) { + // LOGGER.info("SWAPPING NODE"); + reverseAllEdges(sproutNode); + init = finalNode; + fin = sproutNode; + } else { + // LOGGER.info("DID NOT SWAP"); + init = sproutNode; + fin = finalNode; + } + nodesToRemove.add(sproutNode); + } + if (init.pressure == 0 || fin.pressure == 0) { + // LOGGER.info("Pressure is 0, skipping"); + continue; + } + addAngioEdges(angioMap.get(sproutNode), init, fin, tick, Calculation.COMPENSATE); + } + } + } + + for (SiteNode n : nodesToRemove) { + angioMap.remove(n); + } + // If any edges are removed, update the graph edges that are ignored. + // Otherwise, recalculate calculate stresses. + if (!added.isEmpty()) { + // LOGGER.info("*****Updating graph.****** Time: " + tick); + updateGraph(graph, sites, added); + } + } + + private boolean checkForIgnoredEdges(SiteNode node){ + Bag in = graph.getEdgesIn(node); + Bag out = graph.getEdgesOut(node); + if (in != null){ + for (Object edge : in){ + SiteEdge inEdge = (SiteEdge)edge; + if (inEdge.isIgnored) { return true; } + } + } + if (out != null){ + for (Object edge : out){ + SiteEdge outEdge = (SiteEdge)edge; + if (outEdge.isIgnored) { return true; } + } + } + return false; + } + + private boolean checkNodeSkipStatus(SiteNode node, double tick) { + if (angioMap.keySet().contains(node)) { return true; } + if (node.isRoot) {return true; } + if ((tick - node.addTime ) < (72*60)) { return true; } + return false; + } + + + private void reverseAllEdges(SiteNode node) { + for (SiteEdge edge : angioMap.get(node)) { + edge.reverse(); + } + } + + + private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode){ + for (SiteNode keyNode : angioMap.keySet()) { + if (keyNode == skipNode) { continue; } + if (keyNode == targetNode ) { return keyNode; } + if (edgeListContainsNode(angioMap.get(keyNode), targetNode)) { + return keyNode; + } + } + return null; + } + + private boolean edgeListContainsNode(ArrayList edgeList, SiteNode targetNode){ + for (SiteEdge edge : edgeList) { + if (edge.getTo() == targetNode) { + return true; + } + } + return false; + + } + + private void addTemporaryEdges() { + tempEdges = new ArrayList<>(); + for (Map.Entry> entry : angioMap.entrySet()){ + ArrayList edgeList = entry.getValue(); + tempEdges.addAll(edgeList); + addEdgeList(edgeList); + } + } + + private void removeTemporaryEdges() { + if (tempEdges.isEmpty()) { return; } + removeEdgeList(tempEdges); + tempEdges.clear(); + } + + private void removeEdgeList(ArrayList edgeList) { + for (SiteEdge edge : edgeList) { + graph.removeEdge(edge); + } + } + + private int performRandomWalk(final MersenneTwisterFast random, final SiteNode node, + final ArrayList valList, final double tick, ArrayList skipList) { + int randDir; + do{ + randDir = random.nextInt(offsets.length); + } while (!skipList.contains(randDir)); + return randDir; + } + + private int performBiasedWalk(final MersenneTwisterFast random, final SiteNode node, + final ArrayList valList, final double tick, ArrayList skipList) { + for (final int dir : skipList) { + valList.set(dir, 0.0); + } + final ArrayList seqList = normalizeSequentialList(valList); + final double val = random.nextDouble(); + for (int i=0; i < offsets.length; i++) { + if (val < seqList.get(i)) { + return i; + } + } + return offsets.length - 1; + } + + private int performDeterministicWalk(final MersenneTwisterFast random, final SiteNode node, + final ArrayList valList, final double tick, ArrayList skipList) { + for (final int dir : skipList) { + valList.set(dir, 0.0); + } + final int maxDir = getMaxKey(valList); + return maxDir; + } + + private ArrayList> getVEGFList(final Lattice lattice, final SiteNode node) { + double[][][] field = lattice.getField(); + final ArrayList> vegfList = new ArrayList<>(); + for (int dir=0; dir < offsets.length; dir++) { + SiteNode proposed = sites.offsetNode(node, dir, level); + if (sites.checkNode(proposed)) { + final ArrayList span = sites.getSpan(node, proposed); + vegfList.add(dir, new ArrayList<>()); + for (final int[] coords : span) { + int i = coords[0]; + int j = coords[1]; + int k = coords[2]; + vegfList.get(dir).add(field[k][i][j]); + } + } else { + vegfList.add(dir, new ArrayList<>(0)); + } + } + return vegfList; + } + + private int getMaxKey(final ArrayList map) { + int maxDir = 0; + double maxVal = 0; + for (int i=0; i < offsets.length; i++) { + if (map.get(i) > maxVal) { + maxDir = i; + maxVal = map.get(i); + } + } + return maxDir; + } + + private ArrayList getListAverages(final ArrayList> map) { + final ArrayList averageList = new ArrayList<>(); + for (int i=0; i < offsets.length; i++) { + double sum = 0; + for (final double value : map.get(i)) { + sum += value; + } + averageList.add(i, sum / map.get(i).size()); + } + return averageList; + } + + private ArrayList normalizeSequentialList(final ArrayList map) { + final ArrayList normalizedList = new ArrayList<>(); + final double norm = sumList(map); + double prev = 0; + for (int i=0; i < offsets.length; i++) { + normalizedList.add(i, prev + map.get(i) / norm); + prev = prev + map.get(i) / norm; + } + return normalizedList; + } + + private double sumList(final ArrayList map) { + double sum = 0; + for (int i=0; i < offsets.length; i++) { + sum += map.get(i); + } + return sum; + } + + private double averageListArrays(final ArrayList> map) { + double sum = 0; + int count = 0; + for (int i=0; i < offsets.length; i++) { + for (final double value : map.get(i)) { + sum += value; + count++; + } + } + return sum / count; + } + + private void addEdgeList(final ArrayList list) { + addEdgeList(list, false); + } + + private void addEdgeList(final ArrayList list, boolean updateProperties) { + addEdgeList(list, updateProperties, default_type); + } + + private void addAngioEdges(ArrayList list, SiteNode start, SiteNode end, double tick, Calculation calc) { + + ArrayList added = new ArrayList<>(); + + //check for cycle + path(graph, end, start); + if (end.prev != null) { + // LOGGER.info("Cycle detected, not adding edge"); + return; + } + + Graph tempG = sites.newGraph(); + for (SiteEdge e : list){ + tempG.addEdge(e); + } + // LOGGER.info("" + tempG); + path(tempG, start, end); + SiteNode n = end; + while (n != start){ + added.add(new SiteEdge(n.prev, n, 0, level, false)); + n = n.prev; + if (n != start) { + if (n== null) { + return; + } + n.addTime = tick; + } + } + + + double otherRadius = 0; + Bag outEdges = graph.getEdgesOut(start); + if (outEdges != null){ + otherRadius = ((SiteEdge)outEdges.get(0)).radius; + } + else { return; } + + for (SiteEdge edge : added) { + edge.isAngiogenic = true; + edge.radius = (otherRadius > CAP_RADIUS) ? CAP_RADIUS : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); + edge.wall = calcThickness(edge.radius); + edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); + edge.length = sites.getLength(edge, 1); + + //update later (updateSpans method should take care of most of these, need to check for perfusion first) + edge.isPerfused = true; + edge.fraction = new double[sites.NUM_MOLECULES]; + edge.transport = new double[sites.NUM_MOLECULES]; + for (int[] coor: edge.span){ //i don't think i need this? + int i = coor[0]; + int j = coor[1]; + int k = coor[2]; + sites.sites[k][i][j]++; + } + } + + + if (start.pressure * end.pressure <= 0) { + return; + } + + addEdgeList(added); + + switch (calc) { + case COMPENSATE: + updateRootsAndRadii(added, start, end); + break; + case DIVERT: + SiteNode intersection = (SiteNode) graph.findDownstreamIntersection((SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); + if (intersection != null){ + recalcRadii(added, start, end, intersection); + } + else { + removeEdgeList(added); + } + break; + } + + } + + private double calculateEvenSplitRadius(SiteEdge edge){ + double radius = edge.radius; + double length = edge.length; + double deltaP = edge.getFrom().pressure - edge.getTo().pressure; + double flow = calculateLocalFlow(radius, length, deltaP); + double newRadius = Solver.bisection((double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), 1E-6, 5*CAP_RADIUS_MAX, 1E-6); + // LOGGER.info("splitting radius, for checking if it happens directly before bisection failing"); + // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * calculateLocalFlow(r, length, deltaP), 2), 0, CAP_RADIUS_MAX); + // double newRadius = Solver.boundedGradientDescent((double r) -> Math.pow(flow - 2 * calculateLocalFlow(r, length, deltaP), 2), radius, 1E-17, CAP_RADIUS_MIN, CAP_RADIUS_MAX); + return newRadius; + } + + private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteNode end) { + updateGraph(graph, sites); + ArrayList oldRadii = new ArrayList<>(); + ArrayList updatedEdges = new ArrayList<>(); + Boolean failed = false; + + Bag edges = graph.getEdgesOut(start); + + if (edges == null) { + return; + } + if (edges.size() < 2 ) { + return; + } + + Double deltaP = start.pressure - end.pressure; + Double newFlow = calculateLocalFlow(CAP_RADIUS, addedEdges, deltaP); + + ArrayList arteries = sites.arteries; + Integer num_arteries = 0; + ArrayList veins = sites.veins; + Integer num_veins = 0; + + ArrayList> pathsArteries = new ArrayList<>(); + for (Root artery : arteries) { + ArrayList path = getPath(graph, artery.node, start); + if (path.isEmpty()) { continue; } + pathsArteries.add(path); + num_arteries++; + } + + Double arteryFlow = newFlow / num_arteries; + for (ArrayList path : pathsArteries) { + if (!path.get(0).getFrom().isRoot) { + throw new ArithmeticException("Root is not the start of the path"); + } + + updatedEdges.addAll(path); + for (SiteEdge e : path) { + oldRadii.add(e.radius); + } + + SiteEdge rootEdge = path.remove(0); + + if (calculateArteryRootRadius(rootEdge, arteryFlow, false) == -1) { + failed = true; + break; + } + if (updateRadiiOfEdgeList(path, arteryFlow, false) == -1) { + failed = true; + break; + } + } + + if (failed) { + resetRadii(updatedEdges, oldRadii); + return; + } + + ArrayList> pathsVeins = new ArrayList<>(); + for (Root vein : veins) { + ArrayList path = getPath(graph, end, vein.node); + if (path.isEmpty()) { continue; } + pathsVeins.add(path); + num_veins++; + } + + Double veinFlow = newFlow / num_veins; + + for (ArrayList path : pathsVeins) { + if (!path.get(0).getFrom().isRoot) { + throw new ArithmeticException("Root is not the start of the path"); + } + SiteEdge rootEdge = path.remove(path.size() - 1); + oldRadii.add(rootEdge.radius); + updatedEdges.add(rootEdge); + if (calculateArteryRootRadius(rootEdge, veinFlow, false) == -1) { + failed = true; + break; + } + if (updateRadiiOfEdgeList(path, veinFlow, false) == -1) { + failed = true; + break; + } + } + + if (num_arteries == 0 || num_veins == 0) { + LOGGER.info("No arteries or veins found, not updating roots"); + failed = true; + } + + if (failed) { + resetRadii(updatedEdges, oldRadii); + return; + } + } + + private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection){ + + updateGraph(graph, sites); + Bag edges = graph.getEdgesOut(start); + + if (edges == null) { + return; + } + if (edges.size() < 2 ) { + return; + } + // if (((SiteEdge) edges.get(0)).isIgnored || ((SiteEdge) edges.get(1)).isIgnored) { + // return; + // } + + Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; + Integer nonAngioIndex = angioIndex ^ 1; + double deltaP = start.pressure - end.pressure; + // double deltaP = ((SiteNode) graph.lookupNode(start)).pressure - ((SiteNode) graph.lookupNode(end)).pressure; + Double divertedFlow = calculateLocalFlow(CAP_RADIUS, ignoredEdges, deltaP); + Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; + if (divertedFlow > originalFlow) {return ;} + if (intersection != null) { + if (intersection.isRoot) { + updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, ignoredEdges); + return; + // updateRadiusToRoot((SiteEdge) edges.get(angioIndex), intersection, divertedFlow, false, ignoredEdges); + // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, ignoredEdges); + } + + if (updateRadius((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, ignoredEdges) == -1){ + return; + }; + + if (updateRadius((SiteEdge) edges.get(angioIndex), intersection, divertedFlow, false, ignoredEdges) == -1){ + // LOGGER.info("Failed to update radius when increasing size, something seems up"); + }; + } + else { + // maybe also TODO: check for perfusion first + // TODO: check to add flow to radius with new flow after changes to other potential edge, need to do this math out? + // this should only work for single vein simulations + SiteNode boundary = sites.veins.get(0).node; + path(graph, start, boundary); + if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > CAP_RADIUS_MIN) { + // LOGGER.info("Calculating additional flow to vein"); + updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, ignoredEdges); + } + else{ + return; + } + // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, ignoredEdges); + } + + } + + private int updateRadius(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, ArrayList ignored){ + ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); + edgesToUpdate.add(0, edge); + + return updateRadiiOfEdgeList(edgesToUpdate, flow, decrease, ignored); + } + + private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease) { + return updateRadiiOfEdgeList(edges, flow, decrease, new ArrayList<>()); + } + + private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease, ArrayList ignored){ + ArrayList oldRadii = new ArrayList<>(); + for (SiteEdge e : edges){ + oldRadii.add(e.radius); + if (ignored.contains(e)) { continue; } + if (calculateRadius(e, flow, decrease) == -1) { + resetRadii(edges, oldRadii); + return -1; + } + } + return 0; + } + + private int calculateRadius(SiteEdge edge, double flow, boolean decrease){ + int sign = decrease ? -1 : 1; + double originalRadius = edge.radius; + double deltaP = edge.getFrom().pressure - edge.getTo().pressure; + double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); + Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); + double newRadius; + newRadius = Solver.bisection(f, 1E-6, 5*CAP_RADIUS_MAX, 1E-6); + if (newRadius == 1E-6) { + return -1; + } + edge.radius = newRadius; + return 0; + } + + private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease){ + int sign = decrease ? -1 : 1; + double originalRadius = edge.radius; + double deltaP = edge.getFrom().pressure - edge.getTo().pressure; + double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); + + Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, edge.getFrom().pressure - calcPressure(r, edge.type)); + + double newRadius = Solver.bisection(f, .5*originalRadius, 1.5*originalRadius, 1E-6); + + if (newRadius == .5*originalRadius || newRadius == Double.NaN) { + return -1; + } + + edge.radius = newRadius; + edge.getTo().pressure = calcPressure(newRadius, edge.type); + return 0; + } + + private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrease) { + int sign = decrease ? -1 : 1; + double originalRadius = edge.radius; + double deltaP = edge.getFrom().pressure - edge.getTo().pressure; + double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); + + Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, calcPressure(r, edge.type) - edge.getTo().pressure); + + double newRadius = Solver.bisection(f, .5*originalRadius, 1.5*originalRadius, 1E-6); + if (newRadius == .5*originalRadius || newRadius == Double.NaN || newRadius == 1.5*originalRadius) { + return -1; + } + + edge.radius = newRadius; + edge.getFrom().pressure = calcPressure(newRadius, edge.type); + return 0; + } + + private void updateRadiusToRoot(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, ArrayList ignored){ + ArrayList veins = sites.veins; + ArrayList oldRadii = new ArrayList<>(); + for (Root vein: veins){ + ArrayList path = getPath(graph, edge.getTo(), vein.node); + if (path.isEmpty()) {continue;} + path.add(0, edge); + for (SiteEdge e: path){ + oldRadii.add(e.radius); + if (ignored.contains(e)) { continue; } + if (e.getTo().isRoot){ + if (calculateVeinRootRadius(e, flow, decrease) == -1) { + resetRadii(path, oldRadii); + return; + } + } + else { + if (calculateRadius(e, flow, decrease) == -1) { + resetRadii(path, oldRadii); + return; + } + } + } + break; + } + } + + private void resetRadii(ArrayList edges, ArrayList oldRadii){ + for (int i = 0; i < oldRadii.size(); i++){ + edges.get(i).radius = oldRadii.get(i); + } + } + + private void addEdgeList(final ArrayList list, boolean updateProperties, int edgeType) { + for (SiteEdge edge : list) { + graph.addEdge(edge); + } + } + + private SiteEdge createNewEdge(final int dir, final SiteNode node, final double tick) { + SiteNode proposed = sites.offsetNode(node, dir, level); + proposed.lastUpdate = tick; + if (sites.checkNode(proposed) && graph.getDegree(node) < 3) { + SiteEdge edge; + if (graph.containsNode(proposed)){ + if (graph.getDegree(proposed) > 2 || graph.getEdgesOut(proposed) == null || graph.getEdgesIn(proposed) == null) { return null; } + SiteNode existing = (SiteNode) graph.lookupNode(proposed); + edge = new SiteEdge(node, existing, default_type, level, false); + edge.isAnastomotic = true; + return edge; + } + edge = new SiteEdge(node, proposed, default_type, level, false); + + return edge; + } + return null; + } + + + + /** + * {@inheritDoc} + *

+ * The JSON is formatted as: + *

+	 *     {
+	 *         "type": "GROWTH",
+	 *         "interval": interval,
+	 *         "specs" : {
+	 *             "SPEC_NAME": spec value,
+	 *             "SPEC_NAME": spec value,
+	 *             ...
+	 *         }
+	 *     }
+	 * 
+ */ + public String toJSON() { + String format = "{ " + "\"type\": \"GROWTH\", " + "\"interval\": %d, " + "\"specs\": %s " + "}"; + return String.format(format, interval, specs.toJSON()); + } +} + + diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 63c6bb050..ae60b837a 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -115,12 +115,16 @@ static EdgeType parseType(String type) { /** * Calculates node pressure for a given radius. * - *

Equation based on the paper: Welter M, Fredrich T, Rinneberg H, and Rieger H. (2016). - * Computational model for tumor oxygenation applied to clinical data on breast tumor hemoglobin - * concentrations suggests vascular dilatation and compression. PLOS ONE, 11(8), + *

+ * Equation based on the paper: Welter M, Fredrich T, Rinneberg H, and Rieger H. + * (2016). + * Computational model for tumor oxygenation applied to clinical data on breast + * tumor hemoglobin + * concentrations suggests vascular dilatation and compression. PLOS + * ONE, 11(8), * e0161267. * - * @param radius the radius of the edge + * @param radius the radius of the edge * @param category the edge category * @return the edge pressure */ @@ -131,8 +135,11 @@ static double calculatePressure(double radius, EdgeCategory category) { /** * Calculates relative viscosity for a given radius. * - *

Equation based on the paper: Pries AR, Secomb TW, Gessner T, Sperandio MB, Gross JF, and - * Gaehtgens P. (1994). Resistance to blood flow in microvessels in vivo. Circulation + *

+ * Equation based on the paper: Pries AR, Secomb TW, Gessner T, Sperandio MB, + * Gross JF, and + * Gaehtgens P. (1994). Resistance to blood flow in microvessels in vivo. + * Circulation * Research, 75(5), 904-915. * * @param radius the radius of the edge @@ -140,10 +147,9 @@ static double calculatePressure(double radius, EdgeCategory category) { */ private static double calculateViscosity(double radius) { double diameter = 2 * radius; - double mu45 = - 6 * Math.exp(-0.085 * diameter) - + 3.2 - - 2.44 * Math.exp(-0.06 * Math.pow(diameter, 0.645)); + double mu45 = 6 * Math.exp(-0.085 * diameter) + + 3.2 + - 2.44 * Math.exp(-0.06 * Math.pow(diameter, 0.645)); double fR = Math.pow(diameter / (diameter - 1.1), 2); return (1 + (mu45 - 1) * fR) * fR; } @@ -155,8 +161,12 @@ private static double calculateViscosity(double radius) { * @return the flow rate coefficient */ private static double getCoefficient(SiteEdge edge) { - double mu = PLASMA_VISCOSITY * calculateViscosity(edge.radius) / 60; - return (Math.PI * Math.pow(edge.radius, 4)) / (8 * mu * edge.length); + return getCoefficient(edge.radius, edge.length); + } + + private static double getCoefficient(double radius, double length) { + double mu = PLASMA_VISCOSITY * calculateViscosity(radius) / 60; + return (Math.PI * Math.pow(radius, 4)) / (8 * mu * length); } /** @@ -186,7 +196,7 @@ private static double getSaturation(double pressure) { /** * Gets the total amount of oxygen in blood (fmol/um3). * - * @param pressure the oxygen partial pressure + * @param pressure the oxygen partial pressure * @param solubility the oxygen solubility in blood * @return the total amount of oxygen */ @@ -198,7 +208,7 @@ static double getTotal(double pressure, double solubility) { * Gets the maximum (for arteries) or minimum (for veins) pressure across roots. * * @param roots the list of roots - * @param type the root type + * @param type the root type * @return the root pressure */ private static double getRootPressure(ArrayList roots, EdgeCategory type) { @@ -223,10 +233,12 @@ private static double getRootPressure(ArrayList roots, EdgeCategory type) /** * Sets the pressure of roots. * - *

Method assumes that the root node has already been set to the correct node object. + *

+ * Method assumes that the root node has already been set to the correct node + * object. * * @param roots the list of roots - * @param type the root type + * @param type the root type * @return the pressure assigned to the roots */ static double setRootPressures(ArrayList roots, EdgeCategory type) { @@ -241,9 +253,9 @@ static double setRootPressures(ArrayList roots, EdgeCategory type) { /** * Sets the pressure of leaves. * - * @param graph the graph object + * @param graph the graph object * @param arteryPressure the pressure at the arteries - * @param veinPressure the pressure at the veins + * @param veinPressure the pressure at the veins */ static void setLeafPressures(Graph graph, double arteryPressure, double veinPressure) { for (Object obj : graph.getAllEdges()) { @@ -284,9 +296,9 @@ static boolean reversePressures(Graph graph) { /** * Marks edges that perfused between given arteries and veins. * - * @param graph the graph object + * @param graph the graph object * @param arteries the list of arteries - * @param veins the list of veins + * @param veins the list of veins */ static void checkPerfused(Graph graph, ArrayList arteries, ArrayList veins) { // Reset all edges. @@ -379,7 +391,9 @@ static void mergeGraphs(Graph graph1, Graph graph2) { /** * Calculates pressures at nodes. * - *

Sets up a system of linear equations for the current graph structure using mass balances + *

+ * Sets up a system of linear equations for the current graph structure using + * mass balances * at each node. * * @param graph the graph object @@ -527,7 +541,25 @@ static void calculateStresses(Graph graph) { } /** - * Calculates flow rate (in um3/min) and area (in um2) for all edges. + * Solve for Radius while maintaining mass balance + */ + static double calculateLocalFlow(double radius, ArrayList edges, double deltaP) { + double length = 0; + + for (SiteEdge edge : edges) { + length += edge.length; + } + + return getCoefficient(radius, length) * (deltaP); + } + + static double calculateLocalFlow(double radius, double length, double deltaP) { + return getCoefficient(radius, length) * deltaP; + } + + /** + * Calculates flow rate (in um3/min) and area (in um2) for + * all edges. * * @param graph the graph object */ @@ -568,8 +600,8 @@ static void calculateThicknesses(Graph graph) { * Gets in degree for edge in given calculation direction. * * @param graph the graph object - * @param edge the edge object - * @param dir the calculation direction + * @param edge the edge object + * @param dir the calculation direction * @return the edge in degree */ private static int getInDegree(Graph graph, SiteEdge edge, Strategy dir) { @@ -587,8 +619,8 @@ private static int getInDegree(Graph graph, SiteEdge edge, Strategy dir) { * Gets out degree for edge in given calculation direction. * * @param graph the graph object - * @param edge the edge object - * @param dir the calculation direction + * @param edge the edge object + * @param dir the calculation direction * @return the edge out degree */ private static int getOutDegree(Graph graph, SiteEdge edge, Strategy dir) { @@ -605,11 +637,11 @@ private static int getOutDegree(Graph graph, SiteEdge edge, Strategy dir) { /** * Calculate the radii (in um) using Murray's law. * - * @param graph the graph object - * @param edge the starting edge for the calculation - * @param dir the direction of the calculation + * @param graph the graph object + * @param edge the starting edge for the calculation + * @param dir the direction of the calculation * @param fromcheck the number of edges in to the selected node - * @param tocheck the number of edges out of the selected node + * @param tocheck the number of edges out of the selected node * @return the list of children edges */ private static ArrayList calculateRadius( @@ -647,19 +679,17 @@ private static ArrayList calculateRadius( if (in == 1 && out == 1 && edge.radius != 0) { e.radius = edge.radius; } else if (in == fromcheck && out == tocheck) { - ArrayList b = - (dir == Strategy.DOWNSTREAM ? edge.getEdgesOut() : edge.getEdgesIn()); + ArrayList b = (dir == Strategy.DOWNSTREAM ? edge.getEdgesOut() : edge.getEdgesIn()); double r1 = ((SiteEdge) b.get(0)).radius; double r2 = ((SiteEdge) b.get(1)).radius; if (e.radius == 0 && edge.radius != 0) { if (r1 == 0 && r2 != 0) { if (edge.radius > r2) { - e.radius = - Math.pow( - Math.pow(edge.radius, MURRAY_EXPONENT) - - Math.pow(r2, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = Math.pow( + Math.pow(edge.radius, MURRAY_EXPONENT) + - Math.pow(r2, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - r2) < DELTA_TOLERANCE) { e.radius = r2; } @@ -667,11 +697,10 @@ private static ArrayList calculateRadius( e.radius = MINIMUM_CAPILLARY_RADIUS; } } else if (edge.radius < r2) { - e.radius = - Math.pow( - Math.pow(r2, MURRAY_EXPONENT) - - Math.pow(edge.radius, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = Math.pow( + Math.pow(r2, MURRAY_EXPONENT) + - Math.pow(edge.radius, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - edge.radius) < DELTA_TOLERANCE) { e.radius = edge.radius; } @@ -683,11 +712,10 @@ private static ArrayList calculateRadius( } } else if (r1 != 0 && r2 == 0) { if (edge.radius > r1) { - e.radius = - Math.pow( - Math.pow(edge.radius, MURRAY_EXPONENT) - - Math.pow(r1, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = Math.pow( + Math.pow(edge.radius, MURRAY_EXPONENT) + - Math.pow(r1, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - r1) < DELTA_TOLERANCE) { e.radius = r1; } @@ -695,11 +723,10 @@ private static ArrayList calculateRadius( e.radius = MINIMUM_CAPILLARY_RADIUS; } } else if (edge.radius < r1) { - e.radius = - Math.pow( - Math.pow(r1, MURRAY_EXPONENT) - - Math.pow(edge.radius, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = Math.pow( + Math.pow(r1, MURRAY_EXPONENT) + - Math.pow(edge.radius, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - edge.radius) < DELTA_TOLERANCE) { e.radius = edge.radius; } @@ -721,10 +748,9 @@ private static ArrayList calculateRadius( double r1 = ((SiteEdge) b.get(0)).radius; double r2 = ((SiteEdge) b.get(1)).radius; if (r1 != 0 && r2 != 0) { - e.radius = - Math.pow( - Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = Math.pow( + Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); } } @@ -744,11 +770,11 @@ private static ArrayList calculateRadius( /** * Assigns the radii (in um) using Murray's law without splits. * - * @param graph the graph object - * @param edge the starting edge for the calculation - * @param dir the direction of the calculation + * @param graph the graph object + * @param edge the starting edge for the calculation + * @param dir the direction of the calculation * @param fromcheck the number of edges in to the selected node - * @param tocheck the number of edges out of the selected node + * @param tocheck the number of edges out of the selected node * @return the list of children edges */ private static ArrayList assignRadius( @@ -787,10 +813,9 @@ private static ArrayList assignRadius( double r1 = ((SiteEdge) b.get(0)).radius; double r2 = ((SiteEdge) b.get(1)).radius; if (r1 != 0 && r2 != 0) { - e.radius = - Math.pow( - Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = Math.pow( + Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); } } @@ -805,9 +830,9 @@ private static ArrayList assignRadius( /** * Traverses through the graph and marks visited nodes. * - * @param graph the graph object - * @param gs the graph sites object - * @param node the starting node for the traversal + * @param graph the graph object + * @param gs the graph sites object + * @param node the starting node for the traversal * @param splitCol the column in the pattern layout * @param splitRow the row in the pattern layout */ @@ -854,7 +879,7 @@ static void visit( * * @param graph the graph object * @param start the start node - * @param end the end node + * @param end the end node */ static void path(Graph graph, SiteNode start, SiteNode end) { // Reset all distances. @@ -928,7 +953,7 @@ static void path(Graph graph, SiteNode start, SiteNode end) { * * @param graph the graph object * @param start the start node - * @param path the list of edges in the path + * @param path the list of edges in the path */ static void traverse(Graph graph, SiteNode start, ArrayList path) { Bag bag = graph.getEdgesOut(start); @@ -957,8 +982,8 @@ static void traverse(Graph graph, SiteNode start, ArrayList path) { * Updates radii for the graph using Murray's law without variation. * * @param graph the graph object - * @param list the list of edges - * @param code the update code + * @param list the list of edges + * @param code the update code */ static void updateRadii(Graph graph, ArrayList list, CalculationType code) { updateRadii(graph, list, code, null); @@ -967,12 +992,14 @@ static void updateRadii(Graph graph, ArrayList list, CalculationType c /** * Updates radii for the graph using Murray's law. * - *

The graph sites object contains an instance of a random number generator in order to + *

+ * The graph sites object contains an instance of a random number generator in + * order to * ensure simulations with the same random seed are the same. * - * @param graph the graph object - * @param list the list of edges - * @param code the update code + * @param graph the graph object + * @param list the list of edges + * @param code the update code * @param random the random number generator */ static void updateRadii( @@ -1157,12 +1184,14 @@ static void updateGraph(Graph graph) { } /** - * Iterates through nodes to eliminate low flow edges preventing graph traversal. + * Iterates through nodes to eliminate low flow edges preventing graph + * traversal. * - * @param graph the graph object - * @param nodes the set of nodes - * @param removeMin {@code true} if minimum flow edges should be removed, {@code false} - * otherwise + * @param graph the graph object + * @param nodes the set of nodes + * @param removeMin {@code true} if minimum flow edges should be removed, + * {@code false} + * otherwise */ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean removeMin) { double minFlow = Double.MAX_VALUE; From 9915650e3fc66b589690f13caa36ec803b08a4f5 Mon Sep 17 00:00:00 2001 From: cainja Date: Wed, 4 Jun 2025 18:00:05 -0700 Subject: [PATCH 02/56] fixing compiler errors --- src/arcade/core/util/Graph.java | 73 ++- .../env/component/PatchComponentGrowth.java | 595 ++++++++++-------- .../component/PatchComponentSitesGraph.java | 123 ++-- .../PatchComponentSitesGraphFactory.java | 243 +++---- .../PatchComponentSitesGraphFactoryRect.java | 245 ++++---- .../PatchComponentSitesGraphFactoryTri.java | 197 +++--- 6 files changed, 806 insertions(+), 670 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 77e37bfc8..778946092 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -14,7 +14,9 @@ /** * Container class for directed graph using nodes as hashes. * - *

{@code Edge} objects represent edges in the graph and {@code Node} objects represent nodes in + *

+ * {@code Edge} objects represent edges in the graph and {@code Node} objects + * represent nodes in * the graph. Nodes may have more than one edge in or out. */ public final class Graph { @@ -103,7 +105,7 @@ public Bag getAllEdges() { /** * Gets all edges in the graph based on the given node and category. * - * @param node the node to get edges from + * @param node the node to get edges from * @param strategy the category of edges to get * @return a bag containing the edges */ @@ -165,7 +167,7 @@ public int getDegree(Node node) { * Checks if the graph has an edge between the given nodes. * * @param from the node the edge points from - * @param to the node the edge points to + * @param to the node the edge points to * @return {@code true} if edge exists, {@code false} otherwise */ public boolean hasEdge(Node from, Node to) { @@ -197,7 +199,8 @@ public interface GraphFilter { /** * Filters this graph for edges and copies them to the given graph object. * - *

Notes that the links in the subgraph are not correct. + *

+ * Notes that the links in the subgraph are not correct. * * @param g the graph to add filtered edges to * @param f the edge filter @@ -221,13 +224,12 @@ public void getSubgraph(Graph g, GraphFilter f) { private Set retrieveNodes() { Set sOut = nodeToOutBag.keySet(); Set sIn = nodeToInBag.keySet(); - Set set = - new LinkedHashSet() { - { - addAll(sOut); - addAll(sIn); - } - }; + Set set = new LinkedHashSet() { + { + addAll(sOut); + addAll(sIn); + } + }; return set; } @@ -262,7 +264,7 @@ public void mergeNodes() { * Adds edge to graph based on nodes. * * @param from the node to add as the from node. - * @param to the node to add as the to node. + * @param to the node to add as the to node. */ public void addEdge(Node from, Node to) { addEdge(new Edge(from, to)); @@ -440,11 +442,12 @@ public Node findUpstreamIntersection(Edge edge1, Edge edge2) { } /** - * Find the first node where two edges intersect based on a calulcation strategy (upstream or + * Find the first node where two edges intersect based on a calulcation strategy + * (upstream or * downstream). * - * @param edge1 first edge to start from - * @param edge2 second edge to start from + * @param edge1 first edge to start from + * @param edge2 second edge to start from * @param strategy the direction to search * @return the intersection node or null if no intersection */ @@ -461,10 +464,11 @@ private Node findIntersection(Edge edge1, Edge edge2, Strategy strategy) { } /** - * Get all nodes connected to the given node based on a calculation strategy (e.g. upstream or + * Get all nodes connected to the given node based on a calculation strategy + * (e.g. upstream or * downstream). * - * @param node the node to start from + * @param node the node to start from * @param strategy the direction to search * @return a bag of connected nodes */ @@ -497,11 +501,12 @@ private Bag getConnectedNodes(Node node, Strategy strategy) { } /** - * Breadth first search from node according to strategy for a subset of target nodes. + * Breadth first search from node according to strategy for a subset of target + * nodes. * - * @param node the node to start from + * @param node the node to start from * @param targetNodes the bag of potential intersection nodes - * @param strategy the direction to search + * @param strategy the direction to search * @return the target node or null if not found */ private Node breadthFirstSearch(Node node, Bag targetNodes, Strategy strategy) { @@ -587,7 +592,8 @@ public String toString() { /** * Nested class representing a graph node. * - *

The node tracks its corresponding position in the lattice. + *

+ * The node tracks its corresponding position in the lattice. */ public static class Node implements Comparable { /** Coordinate in x direction. */ @@ -643,8 +649,9 @@ public int getZ() { * Compares a node to this node. * * @param node the node to compare - * @return zero if the x and y coordinates are equal, otherwise the result of integer - * comparison for x and y + * @return zero if the x and y coordinates are equal, otherwise the result of + * integer + * comparison for x and y */ public int compareTo(Node node) { int xComp = Integer.compare(x, node.getX()); @@ -667,7 +674,8 @@ public Node duplicate() { } /** - * Updates the position of this {@code Node} with coordinate from given {@code Node}. + * Updates the position of this {@code Node} with coordinate from given + * {@code Node}. * * @param node the {@code Node} with coordinates to update with */ @@ -713,7 +721,9 @@ public String toString() { /** * Nested class representing a graph edge. * - *

The edge tracks its corresponding nodes as well as the edges into the FROM node and out of + *

+ * The edge tracks its corresponding nodes as well as the edges into the FROM + * node and out of * the TO node. */ public static class Edge { @@ -733,7 +743,7 @@ public static class Edge { * Creates an {@code Edge} between two {@link Node} objects. * * @param from the node the edge is from - * @param to the node the edge is to + * @param to the node the edge is to */ public Edge(Node from, Node to) { this.from = from.duplicate(); @@ -743,7 +753,8 @@ public Edge(Node from, Node to) { } /** - * Gets the node the edge points to based on the calculation strategy (e.g. upstream or + * Gets the node the edge points to based on the calculation strategy (e.g. + * upstream or * downstream). * * @param strategy the calculation strategy @@ -790,7 +801,8 @@ public void setFrom(Node from) { } /** - * Gets list of edges based on calculation strategy (e.g. upstream or downstream). + * Gets list of edges based on calculation strategy (e.g. upstream or + * downstream). * * @param strategy the calculation strategy * @return the list of edges @@ -822,7 +834,7 @@ public ArrayList getEdgesOut() { * * @return the reversed edge */ - Edge reverse() { + public Edge reverse() { Node tempTo = to; Node tempFrom = from; to = tempFrom; @@ -849,7 +861,8 @@ public String toString() { * Checks if two nodes are equal based on to and from nodes. * * @param obj the object to check - * @return {@code true} if coordinates of both nodes match, {@code false} otherwise + * @return {@code true} if coordinates of both nodes match, {@code false} + * otherwise */ public boolean equals(Object obj) { if (obj instanceof Edge) { diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 796184ce1..ef80b31a9 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -1,40 +1,43 @@ -package arcade.env.comp; - -import static arcade.env.comp.GraphSites.CAP_RADIUS; -import static arcade.env.comp.GraphSites.CAP_RADIUS_MIN; -import static arcade.env.comp.GraphSites.CAP_RADIUS_MAX; - -import static arcade.env.comp.GraphSitesUtilities.calcThickness; -import static arcade.env.comp.GraphSitesUtilities.path; -import static arcade.env.comp.GraphSitesUtilities.reversePressures; -import static arcade.env.comp.GraphSitesUtilities.getPath; -import static arcade.env.comp.GraphSitesUtilities.updateGraph; -import static arcade.env.comp.GraphSitesUtilities.calcFlows; -import static arcade.env.comp.GraphSitesUtilities.calcPressure; -import static arcade.env.comp.GraphSitesUtilities.calcPressures; -import static arcade.env.comp.GraphSitesUtilities.calcStress; -import static arcade.env.comp.GraphSitesUtilities.calculateLocalFlow; +package arcade.patch.env.component; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; +import java.util.EnumMap; import java.util.logging.Logger; - -import arcade.env.comp.GraphSites.Root; -import arcade.env.comp.GraphSites.SiteEdge; -import arcade.env.comp.GraphSites.SiteNode; -import arcade.env.lat.Lattice; -import arcade.env.loc.Location; -import arcade.sim.Simulation; -import arcade.util.Graph; -import arcade.util.MiniBox; -import arcade.util.Solver; -import arcade.util.Solver.Function; import ec.util.MersenneTwisterFast; import sim.util.Bag; +import sim.engine.Schedule; import sim.engine.SimState; - +import arcade.core.sim.Simulation; +import arcade.core.sim.Series; +import arcade.core.util.Graph; +import arcade.core.util.MiniBox; +import arcade.core.util.Solver; +import arcade.core.util.Solver.Function; +import arcade.core.env.location.Location; +import arcade.core.env.lattice.Lattice; +import arcade.core.env.component.Component; +import arcade.patch.sim.PatchSimulation; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.CAPILLARY_RADIUS; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_CAPILLARY_RADIUS; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThicknesses; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.reversePressures; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateFlows; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressure; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressures; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateStresses; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateLocalFlow; +import static arcade.patch.util.PatchEnums.Ordering; /** * Implementation of {@link Component} for degrading graph edges. @@ -48,8 +51,8 @@ * from the graph, then only the stresses in the graph are recalculated. * Otherwise, all hemodynamic properties are recalculated. */ -public class GrowthComponent implements Component { - private static Logger LOGGER = Logger.getLogger(GrowthComponent.class.getName()); +public class PatchComponentGrowth implements Component { + private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); /** Calculation strategies. */ public enum Calculation { @@ -60,6 +63,18 @@ public enum Calculation { DIVERT } + /** Strategy for direction of migration. */ + public enum MigrationDirection { + /** Code for random direction. */ + RANDOM, + + /** Code for deterministic direction. */ + DETERMINISTIC, + + /** Code for biased random direction. */ + BIASED; + } + private final double migrationRate; private final double vegfThreshold; private final String walkType; @@ -69,126 +84,121 @@ public enum Calculation { private int interval; private double edgeSize; /** The associated {@link GraphSites} object. */ - private GraphSites sites; + private PatchComponentSitesGraph sites; /** The {@link Graph} object representing the sites. */ private Graph graph; - private HashMap> angioMap; + private HashMap> angioMap = new HashMap<>(); private ArrayList tempEdges; - private int[][] offsets; + private EnumMap offsets; private final int level = 1; - private final int default_type = 0; //Capillary type + private final int default_type = 0; // Capillary type private final MiniBox specs; - private ArrayList added = new ArrayList<>(); + private int numOffsets; - public GrowthComponent(MiniBox component) { + public PatchComponentGrowth(Series series, MiniBox parameters) { // Set loaded parameters. - migrationRate = component.getDouble("MIGRATION_RATE"); - vegfThreshold = component.getDouble("VEGF_THRESHOLD"); - walkType = component.get("WALK_TYPE"); - maxLength = component.getDouble("MAX_LENGTH"); - angioMap = new HashMap<>(); - - // Get list of specifications. - specs = new MiniBox(); - String[] specList = new String[] { "MIGRATION_RATE", "VEGF_THRESHOLD", "WALK_TYPE", "MAX_LENGTH" }; - for (String spec : specList) { specs.put(spec, component.get(spec)); } + migrationRate = parameters.getDouble("MIGRATION_RATE"); + vegfThreshold = parameters.getDouble("VEGF_THRESHOLD"); + walkType = parameters.get("WALK_TYPE"); + maxLength = parameters.getDouble("MAX_LENGTH"); } - /** - * Component does not have a relevant field; returns {@code null}. - * - * @return {@code null} - */ - public double[][][] getField() { return null; } + /** + * Component does not have a relevant field; returns {@code null}. + * + * @return {@code null} + */ + public double[][][] getField() { + return null; + } @Override - public void scheduleComponent(Simulation sim) { - Component comp = sim.getEnvironment("sites").getComponent("sites"); - if (!(comp instanceof GraphSites)) { - LOGGER.warning("cannot schedule GROWTH component for non-graph sites"); - return; - } - - sites = (GraphSites) comp; - offsets = sites.getOffsets(); - edgeSize = sites.getGridSize(); - maxEdges = (int) Math.floor(maxLength / edgeSize); - interval = calculateInterval(); - ((SimState)sim).schedule.scheduleRepeating(1, Simulation.ORDERING_COMPONENT - 3, this, interval); - ((SimState)sim).schedule.scheduleOnce((state) -> graph = sites.getGraph(), Simulation.ORDERING_COMPONENT - 3); + public void schedule(Schedule schedule) { + interval = migrationRate < edgeSize ? 60 : 30; + schedule.scheduleRepeating(this, Ordering.LAST_COMPONENT.ordinal() - 3, interval); } - private int calculateInterval() { - if (migrationRate < edgeSize) { - return 60; - } else { + @Override + public void register(Simulation sim, String layer) { + PatchSimulation patchSim = (PatchSimulation) sim; + Component component = sim.getComponent(layer); - return 30; + if (!(component instanceof PatchComponentSitesGraph)) { + return; } - } - - /** - * {@inheritDoc} - *

- * Degradation component does not use this method. - */ - public void updateComponent(Simulation sim, Location oldLoc, Location newLoc) { } - @Override - public void step(final SimState state) { - final Simulation sim = (Simulation) state; - final Lattice vegf_lattice = sim.getEnvironment("vegf"); - final MersenneTwisterFast random = state.random; - ArrayList nodesToRemove = new ArrayList<>(); + sites = (PatchComponentSitesGraph) component; + graph = sites.graph; + offsets = sites.graphFactory.getOffsets(); + numOffsets = offsets.keySet().size(); + edgeSize = sim.getSeries().ds; + maxEdges = (int) Math.floor(maxLength / edgeSize); - final double tick = sim.getTime(); + } - // if (((int) tick - 1) % (12*60) == 0) { - // LOGGER.info((int)(tick - 1) + " | number of nodes to check: " + graph.getAllNodes().size()); - // } + @Override + public void step(final SimState simstate) { + final Simulation sim = (Simulation) simstate; + final Lattice vegfLattice = sim.getLattice("VEGF"); + final MersenneTwisterFast random = simstate.random; - LinkedHashSet set = new LinkedHashSet<>(); + ArrayList nodesToRemove = new ArrayList<>(); + final double tick = (int) simstate.schedule.getTime() + 1; - for (Object obj : graph.getAllEdges()) { - SiteEdge edge = (SiteEdge)obj; - if (edge.isIgnored) { continue; } - SiteNode from = edge.getFrom(); - SiteNode to = edge.getTo(); - from.id = -1; - to.id = -1; + LinkedHashSet set = new LinkedHashSet<>(); - if ((graph.getDegree(from) < 3) && !from.isRoot && !(graph.getInDegree(from) == 0 && graph.getOutDegree(from) == 1)) { set.add(from); } - if ((graph.getDegree(to) < 3) && !to.isRoot && !(graph.getInDegree(to) == 1 && graph.getOutDegree(to) == 0)) { set.add(to); } - } + for (Object obj : graph.getAllEdges()) { + SiteEdge edge = (SiteEdge) obj; + if (edge.isIgnored) { + continue; + } + SiteNode from = edge.getFrom(); + SiteNode to = edge.getTo(); + from.id = -1; + to.id = -1; + + if ((graph.getDegree(from) < 3) && !from.isRoot + && !(graph.getInDegree(from) == 0 && graph.getOutDegree(from) == 1)) { + set.add(from); + } + if ((graph.getDegree(to) < 3) && !to.isRoot + && !(graph.getInDegree(to) == 1 && graph.getOutDegree(to) == 0)) { + set.add(to); + } + } for (SiteNode node : set) { - if (checkNodeSkipStatus(node, tick)) { continue; } + if (checkNodeSkipStatus(node, tick)) { + continue; + } - ArrayList> vegfList = getVEGFList(vegf_lattice, node); + ArrayList> vegfList = getVEGFList(vegfLattice, node); if (averageListArrays(vegfList) > vegfThreshold) { angioMap.put(node, new ArrayList<>()); - ArrayList skipDirList = new ArrayList(); + ArrayList skipDirList = new ArrayList(); Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); - if (in != null){ - for (Object edge : in){ - SiteEdge inEdge = (SiteEdge)edge; - skipDirList.add(sites.getOppositeDirection(inEdge, inEdge.level)); + if (in != null) { + for (Object edge : in) { + SiteEdge inEdge = (SiteEdge) edge; + skipDirList.add(sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); + } } - if (out != null){ - for (Object edge : out){ - SiteEdge outEdge = (SiteEdge)edge; - skipDirList.add(sites.getDirection(outEdge, outEdge.level)); + if (out != null) { + for (Object edge : out) { + SiteEdge outEdge = (SiteEdge) edge; + skipDirList.add(sites.graphFactory.getDirection(outEdge, outEdge.level)); + } } @@ -217,8 +227,8 @@ public void step(final SimState state) { addTemporaryEdges(); - for (Map.Entry> entry : angioMap.entrySet()){ - //grab final node in each list and add edge, check for perfusion + for (Map.Entry> entry : angioMap.entrySet()) { + // grab final node in each list and add edge, check for perfusion SiteNode keyNode = entry.getKey(); if (checkForIgnoredEdges(keyNode)) { @@ -231,20 +241,23 @@ public void step(final SimState state) { SiteEdge newEdge; if (edgeList.size() > 0) { tipNode = edgeList.get(edgeList.size() - 1).getTo(); + } else { + tipNode = keyNode; } - else { tipNode = keyNode; } - if (tick - tipNode.lastUpdate < migrationRate) { continue; } + if (tick - tipNode.lastUpdate < migrationRate) { + continue; + } newEdge = createNewEdge(keyNode.sproutDir, tipNode, tick); - if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3 ) { - // LOGGER.info("Removing " + keyNode + " from angiomap, unsuccessful perfusion."); + if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3) { + // LOGGER.info("Removing " + keyNode + " from angiomap, unsuccessful + // perfusion."); nodesToRemove.add(keyNode); - } - else { + } else { edgeList.add(newEdge); - if (newEdge.isAnastomotic){ + if (newEdge.isAnastomotic) { keyNode.anastomosis = true; addFlag = true; } @@ -258,7 +271,9 @@ public void step(final SimState state) { // LOGGER.info("*****Adding edges to graph.****** Time: " + tick); // LOGGER.info("Current graph size: " + graph.getAllEdges().size()); for (SiteNode sproutNode : angioMap.keySet()) { - if (nodesToRemove.contains(sproutNode)) { continue; } + if (nodesToRemove.contains(sproutNode)) { + continue; + } if (sproutNode.anastomosis) { int leadingIndex = angioMap.get(sproutNode).size() - 1; if (leadingIndex < 0) { @@ -268,14 +283,16 @@ public void step(final SimState state) { SiteNode finalNode = angioMap.get(sproutNode).get(leadingIndex).getTo(); SiteNode init, fin; - calcPressures(graph); + calculatePressures(graph); boolean reversed = reversePressures(graph); - if (reversed) { calcPressures(graph); } + if (reversed) { + calculatePressures(graph); + } calcFlows(graph, sites); calcStress(graph); // maybe try to redo by iterating through list rather than using final node - if (!graph.containsNode(finalNode)){ + if (!graph.containsNode(finalNode)) { // LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); if (targetNode == null) { @@ -299,13 +316,16 @@ public void step(final SimState state) { nodesToRemove.add(sproutNode); nodesToRemove.add(targetNode); - } - else { + } else { if (sproutNode.pressure == 0) { - if (graph.getEdgesOut(sproutNode) != null) {sproutNode = ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom();} + if (graph.getEdgesOut(sproutNode) != null) { + sproutNode = ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); + } } if (finalNode.pressure == 0) { - if (graph.getEdgesOut(finalNode) != null) {finalNode = ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom();} + if (graph.getEdgesOut(finalNode) != null) { + finalNode = ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); + } } // path(graph, finalNode, sproutNode); if (sproutNode.pressure < finalNode.pressure) { @@ -340,43 +360,55 @@ public void step(final SimState state) { } } - private boolean checkForIgnoredEdges(SiteNode node){ + private boolean checkForIgnoredEdges(SiteNode node) { Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); - if (in != null){ - for (Object edge : in){ - SiteEdge inEdge = (SiteEdge)edge; - if (inEdge.isIgnored) { return true; } + if (in != null) { + for (Object edge : in) { + SiteEdge inEdge = (SiteEdge) edge; + if (inEdge.isIgnored) { + return true; + } } } - if (out != null){ - for (Object edge : out){ - SiteEdge outEdge = (SiteEdge)edge; - if (outEdge.isIgnored) { return true; } + if (out != null) { + for (Object edge : out) { + SiteEdge outEdge = (SiteEdge) edge; + if (outEdge.isIgnored) { + return true; + } } } return false; } private boolean checkNodeSkipStatus(SiteNode node, double tick) { - if (angioMap.keySet().contains(node)) { return true; } - if (node.isRoot) {return true; } - if ((tick - node.addTime ) < (72*60)) { return true; } - return false; + if (angioMap.keySet().contains(node)) { + return true; + } + if (node.isRoot) { + return true; + } + if ((tick - node.addTime) < (72 * 60)) { + return true; + } + return false; } - private void reverseAllEdges(SiteNode node) { for (SiteEdge edge : angioMap.get(node)) { edge.reverse(); } } - - private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode){ + private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { for (SiteNode keyNode : angioMap.keySet()) { - if (keyNode == skipNode) { continue; } - if (keyNode == targetNode ) { return keyNode; } + if (keyNode == skipNode) { + continue; + } + if (keyNode == targetNode) { + return keyNode; + } if (edgeListContainsNode(angioMap.get(keyNode), targetNode)) { return keyNode; } @@ -384,7 +416,7 @@ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode){ return null; } - private boolean edgeListContainsNode(ArrayList edgeList, SiteNode targetNode){ + private boolean edgeListContainsNode(ArrayList edgeList, SiteNode targetNode) { for (SiteEdge edge : edgeList) { if (edge.getTo() == targetNode) { return true; @@ -396,7 +428,7 @@ private boolean edgeListContainsNode(ArrayList edgeList, SiteNode targ private void addTemporaryEdges() { tempEdges = new ArrayList<>(); - for (Map.Entry> entry : angioMap.entrySet()){ + for (Map.Entry> entry : angioMap.entrySet()) { ArrayList edgeList = entry.getValue(); tempEdges.addAll(edgeList); addEdgeList(edgeList); @@ -404,7 +436,9 @@ private void addTemporaryEdges() { } private void removeTemporaryEdges() { - if (tempEdges.isEmpty()) { return; } + if (tempEdges.isEmpty()) { + return; + } removeEdgeList(tempEdges); tempEdges.clear(); } @@ -416,31 +450,31 @@ private void removeEdgeList(ArrayList edgeList) { } private int performRandomWalk(final MersenneTwisterFast random, final SiteNode node, - final ArrayList valList, final double tick, ArrayList skipList) { + final ArrayList valList, final double tick, ArrayList skipList) { int randDir; - do{ - randDir = random.nextInt(offsets.length); + do { + randDir = random.nextInt(offsets.keySet().size()); } while (!skipList.contains(randDir)); return randDir; } private int performBiasedWalk(final MersenneTwisterFast random, final SiteNode node, - final ArrayList valList, final double tick, ArrayList skipList) { - for (final int dir : skipList) { + final ArrayList valList, final double tick, ArrayList skipList) { + for (final EdgeDirection dir : skipList) { valList.set(dir, 0.0); } final ArrayList seqList = normalizeSequentialList(valList); final double val = random.nextDouble(); - for (int i=0; i < offsets.length; i++) { + for (int i = 0; i < numOffsets; i++) { if (val < seqList.get(i)) { return i; } } - return offsets.length - 1; + return numOffsets - 1; } private int performDeterministicWalk(final MersenneTwisterFast random, final SiteNode node, - final ArrayList valList, final double tick, ArrayList skipList) { + final ArrayList valList, final double tick, ArrayList skipList) { for (final int dir : skipList) { valList.set(dir, 0.0); } @@ -451,7 +485,7 @@ private int performDeterministicWalk(final MersenneTwisterFast random, final Sit private ArrayList> getVEGFList(final Lattice lattice, final SiteNode node) { double[][][] field = lattice.getField(); final ArrayList> vegfList = new ArrayList<>(); - for (int dir=0; dir < offsets.length; dir++) { + for (int dir = 0; dir < numOffsets; dir++) { SiteNode proposed = sites.offsetNode(node, dir, level); if (sites.checkNode(proposed)) { final ArrayList span = sites.getSpan(node, proposed); @@ -472,7 +506,7 @@ private ArrayList> getVEGFList(final Lattice lattice, final Si private int getMaxKey(final ArrayList map) { int maxDir = 0; double maxVal = 0; - for (int i=0; i < offsets.length; i++) { + for (int i = 0; i < numOffsets; i++) { if (map.get(i) > maxVal) { maxDir = i; maxVal = map.get(i); @@ -483,7 +517,7 @@ private int getMaxKey(final ArrayList map) { private ArrayList getListAverages(final ArrayList> map) { final ArrayList averageList = new ArrayList<>(); - for (int i=0; i < offsets.length; i++) { + for (int i = 0; i < numOffsets; i++) { double sum = 0; for (final double value : map.get(i)) { sum += value; @@ -497,7 +531,7 @@ private ArrayList normalizeSequentialList(final ArrayList map) { final ArrayList normalizedList = new ArrayList<>(); final double norm = sumList(map); double prev = 0; - for (int i=0; i < offsets.length; i++) { + for (int i = 0; i < numOffsets; i++) { normalizedList.add(i, prev + map.get(i) / norm); prev = prev + map.get(i) / norm; } @@ -506,7 +540,7 @@ private ArrayList normalizeSequentialList(final ArrayList map) { private double sumList(final ArrayList map) { double sum = 0; - for (int i=0; i < offsets.length; i++) { + for (int i = 0; i < numOffsets; i++) { sum += map.get(i); } return sum; @@ -515,7 +549,7 @@ private double sumList(final ArrayList map) { private double averageListArrays(final ArrayList> map) { double sum = 0; int count = 0; - for (int i=0; i < offsets.length; i++) { + for (int i = 0; i < numOffsets; i++) { for (final double value : map.get(i)) { sum += value; count++; @@ -536,51 +570,53 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en ArrayList added = new ArrayList<>(); - //check for cycle + // check for cycle path(graph, end, start); if (end.prev != null) { // LOGGER.info("Cycle detected, not adding edge"); return; } - Graph tempG = sites.newGraph(); - for (SiteEdge e : list){ + Graph tempG = sites.graphFactory.newGraph(); + for (SiteEdge e : list) { tempG.addEdge(e); } // LOGGER.info("" + tempG); path(tempG, start, end); SiteNode n = end; - while (n != start){ - added.add(new SiteEdge(n.prev, n, 0, level, false)); + while (n != start) { + added.add(new SiteEdge(n.prev n, 0, level, false)); n = n.prev; if (n != start) { - if (n== null) { + if (n == null) { return; } - n.addTime = tick; + n.addTime = (int) tick; } } - double otherRadius = 0; Bag outEdges = graph.getEdgesOut(start); - if (outEdges != null){ - otherRadius = ((SiteEdge)outEdges.get(0)).radius; + if (outEdges != null) { + otherRadius = ((SiteEdge) outEdges.get(0)).radius; + } else { + return; } - else { return; } for (SiteEdge edge : added) { edge.isAngiogenic = true; - edge.radius = (otherRadius > CAP_RADIUS) ? CAP_RADIUS : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); + edge.radius = (otherRadius > CAP_RADIUS) ? CAP_RADIUS + : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); edge.wall = calcThickness(edge.radius); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.length = sites.getLength(edge, 1); - //update later (updateSpans method should take care of most of these, need to check for perfusion first) + // update later (updateSpans method should take care of most of these, need to + // check for perfusion first) edge.isPerfused = true; edge.fraction = new double[sites.NUM_MOLECULES]; edge.transport = new double[sites.NUM_MOLECULES]; - for (int[] coor: edge.span){ //i don't think i need this? + for (int[] coor : edge.span) { // i don't think i need this? int i = coor[0]; int j = coor[1]; int k = coor[2]; @@ -588,7 +624,6 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en } } - if (start.pressure * end.pressure <= 0) { return; } @@ -600,11 +635,11 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en updateRootsAndRadii(added, start, end); break; case DIVERT: - SiteNode intersection = (SiteNode) graph.findDownstreamIntersection((SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); - if (intersection != null){ + SiteNode intersection = (SiteNode) graph.findDownstreamIntersection((SiteEdge) outEdges.get(0), + (SiteEdge) added.get(0)); + if (intersection != null) { recalcRadii(added, start, end, intersection); - } - else { + } else { removeEdgeList(added); } break; @@ -612,15 +647,20 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en } - private double calculateEvenSplitRadius(SiteEdge edge){ + private double calculateEvenSplitRadius(SiteEdge edge) { double radius = edge.radius; double length = edge.length; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); - double newRadius = Solver.bisection((double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), 1E-6, 5*CAP_RADIUS_MAX, 1E-6); - // LOGGER.info("splitting radius, for checking if it happens directly before bisection failing"); - // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * calculateLocalFlow(r, length, deltaP), 2), 0, CAP_RADIUS_MAX); - // double newRadius = Solver.boundedGradientDescent((double r) -> Math.pow(flow - 2 * calculateLocalFlow(r, length, deltaP), 2), radius, 1E-17, CAP_RADIUS_MIN, CAP_RADIUS_MAX); + double newRadius = Solver.bisection((double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), 1E-6, + 5 * CAP_RADIUS_MAX, 1E-6); + // LOGGER.info("splitting radius, for checking if it happens directly before + // bisection failing"); + // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * + // calculateLocalFlow(r, length, deltaP), 2), 0, CAP_RADIUS_MAX); + // double newRadius = Solver.boundedGradientDescent((double r) -> Math.pow(flow + // - 2 * calculateLocalFlow(r, length, deltaP), 2), radius, 1E-17, + // CAP_RADIUS_MIN, CAP_RADIUS_MAX); return newRadius; } @@ -635,7 +675,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, if (edges == null) { return; } - if (edges.size() < 2 ) { + if (edges.size() < 2) { return; } @@ -650,7 +690,9 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, ArrayList> pathsArteries = new ArrayList<>(); for (Root artery : arteries) { ArrayList path = getPath(graph, artery.node, start); - if (path.isEmpty()) { continue; } + if (path.isEmpty()) { + continue; + } pathsArteries.add(path); num_arteries++; } @@ -686,7 +728,9 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, ArrayList> pathsVeins = new ArrayList<>(); for (Root vein : veins) { ArrayList path = getPath(graph, end, vein.node); - if (path.isEmpty()) { continue; } + if (path.isEmpty()) { + continue; + } pathsVeins.add(path); num_veins++; } @@ -721,7 +765,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } } - private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection){ + private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { updateGraph(graph, sites); Bag edges = graph.getEdgesOut(start); @@ -729,55 +773,68 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN if (edges == null) { return; } - if (edges.size() < 2 ) { + if (edges.size() < 2) { return; } - // if (((SiteEdge) edges.get(0)).isIgnored || ((SiteEdge) edges.get(1)).isIgnored) { - // return; + // if (((SiteEdge) edges.get(0)).isIgnored || ((SiteEdge) + // edges.get(1)).isIgnored) { + // return; // } Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; Integer nonAngioIndex = angioIndex ^ 1; double deltaP = start.pressure - end.pressure; - // double deltaP = ((SiteNode) graph.lookupNode(start)).pressure - ((SiteNode) graph.lookupNode(end)).pressure; + // double deltaP = ((SiteNode) graph.lookupNode(start)).pressure - ((SiteNode) + // graph.lookupNode(end)).pressure; Double divertedFlow = calculateLocalFlow(CAP_RADIUS, ignoredEdges, deltaP); Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; - if (divertedFlow > originalFlow) {return ;} + if (divertedFlow > originalFlow) { + return; + } if (intersection != null) { if (intersection.isRoot) { - updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, ignoredEdges); + updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, + ignoredEdges); return; - // updateRadiusToRoot((SiteEdge) edges.get(angioIndex), intersection, divertedFlow, false, ignoredEdges); - // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, ignoredEdges); + // updateRadiusToRoot((SiteEdge) edges.get(angioIndex), intersection, + // divertedFlow, false, ignoredEdges); + // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, + // divertedFlow, true, ignoredEdges); } - if (updateRadius((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, ignoredEdges) == -1){ + if (updateRadius((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, + ignoredEdges) == -1) { return; - }; + } + ; - if (updateRadius((SiteEdge) edges.get(angioIndex), intersection, divertedFlow, false, ignoredEdges) == -1){ - // LOGGER.info("Failed to update radius when increasing size, something seems up"); - }; - } - else { + if (updateRadius((SiteEdge) edges.get(angioIndex), intersection, divertedFlow, false, ignoredEdges) == -1) { + // LOGGER.info("Failed to update radius when increasing size, something seems + // up"); + } + ; + } else { // maybe also TODO: check for perfusion first - // TODO: check to add flow to radius with new flow after changes to other potential edge, need to do this math out? + // TODO: check to add flow to radius with new flow after changes to other + // potential edge, need to do this math out? // this should only work for single vein simulations SiteNode boundary = sites.veins.get(0).node; path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > CAP_RADIUS_MIN) { // LOGGER.info("Calculating additional flow to vein"); - updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, ignoredEdges); - } - else{ + updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, + ignoredEdges); + } else { return; } - // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, ignoredEdges); + // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, + // divertedFlow, true, ignoredEdges); } } - private int updateRadius(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, ArrayList ignored){ + private int updateRadius(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, + ArrayList ignored) { ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); edgesToUpdate.add(0, edge); @@ -788,11 +845,14 @@ private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolea return updateRadiiOfEdgeList(edges, flow, decrease, new ArrayList<>()); } - private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease, ArrayList ignored){ + private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease, + ArrayList ignored) { ArrayList oldRadii = new ArrayList<>(); - for (SiteEdge e : edges){ + for (SiteEdge e : edges) { oldRadii.add(e.radius); - if (ignored.contains(e)) { continue; } + if (ignored.contains(e)) { + continue; + } if (calculateRadius(e, flow, decrease) == -1) { resetRadii(edges, oldRadii); return -1; @@ -801,14 +861,15 @@ private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolea return 0; } - private int calculateRadius(SiteEdge edge, double flow, boolean decrease){ + private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { int sign = decrease ? -1 : 1; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; - newRadius = Solver.bisection(f, 1E-6, 5*CAP_RADIUS_MAX, 1E-6); + newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); + if (newRadius == 1E-6) { return -1; } @@ -816,22 +877,23 @@ private int calculateRadius(SiteEdge edge, double flow, boolean decrease){ return 0; } - private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease){ + private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease) { int sign = decrease ? -1 : 1; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, edge.getFrom().pressure - calcPressure(r, edge.type)); + Function f = (double r) -> originalFlow + sign * flow + - calculateLocalFlow(r, edge.length, edge.getFrom().pressure - calculatePressure(r, edge.type)); - double newRadius = Solver.bisection(f, .5*originalRadius, 1.5*originalRadius, 1E-6); + double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius, 1E-6); - if (newRadius == .5*originalRadius || newRadius == Double.NaN) { + if (newRadius == .5 * originalRadius || newRadius == Double.NaN) { return -1; } edge.radius = newRadius; - edge.getTo().pressure = calcPressure(newRadius, edge.type); + edge.getTo().pressure = calculatePressure(newRadius, edge.type); return 0; } @@ -841,35 +903,40 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, calcPressure(r, edge.type) - edge.getTo().pressure); + Function f = (double r) -> originalFlow + sign * flow + - calculateLocalFlow(r, edge.length, calculatePressure(r, edge.type) - edge.getTo().pressure); - double newRadius = Solver.bisection(f, .5*originalRadius, 1.5*originalRadius, 1E-6); - if (newRadius == .5*originalRadius || newRadius == Double.NaN || newRadius == 1.5*originalRadius) { + double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius, 1E-6); + if (newRadius == .5 * originalRadius || newRadius == Double.NaN || newRadius == 1.5 * originalRadius) { return -1; } edge.radius = newRadius; - edge.getFrom().pressure = calcPressure(newRadius, edge.type); + edge.getFrom().pressure = calculatePressure(newRadius, edge.type); return 0; } - private void updateRadiusToRoot(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, ArrayList ignored){ + private void updateRadiusToRoot(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, + ArrayList ignored) { ArrayList veins = sites.veins; ArrayList oldRadii = new ArrayList<>(); - for (Root vein: veins){ + for (Root vein : veins) { ArrayList path = getPath(graph, edge.getTo(), vein.node); - if (path.isEmpty()) {continue;} + if (path.isEmpty()) { + continue; + } path.add(0, edge); - for (SiteEdge e: path){ + for (SiteEdge e : path) { oldRadii.add(e.radius); - if (ignored.contains(e)) { continue; } - if (e.getTo().isRoot){ + if (ignored.contains(e)) { + continue; + } + if (e.getTo().isRoot) { if (calculateVeinRootRadius(e, flow, decrease) == -1) { resetRadii(path, oldRadii); return; } - } - else { + } else { if (calculateRadius(e, flow, decrease) == -1) { resetRadii(path, oldRadii); return; @@ -880,25 +947,28 @@ private void updateRadiusToRoot(SiteEdge edge, SiteNode intersection, double flo } } - private void resetRadii(ArrayList edges, ArrayList oldRadii){ - for (int i = 0; i < oldRadii.size(); i++){ + private void resetRadii(ArrayList edges, ArrayList oldRadii) { + for (int i = 0; i < oldRadii.size(); i++) { edges.get(i).radius = oldRadii.get(i); } } - private void addEdgeList(final ArrayList list, boolean updateProperties, int edgeType) { + private void addEdgeList(ArrayList list, boolean updateProperties, EdgeType edgeType) { for (SiteEdge edge : list) { graph.addEdge(edge); } } - private SiteEdge createNewEdge(final int dir, final SiteNode node, final double tick) { - SiteNode proposed = sites.offsetNode(node, dir, level); + private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node, int tick) { + SiteNode proposed = sites.graphFactory.offsetNode(node, dir, level); proposed.lastUpdate = tick; if (sites.checkNode(proposed) && graph.getDegree(node) < 3) { SiteEdge edge; - if (graph.containsNode(proposed)){ - if (graph.getDegree(proposed) > 2 || graph.getEdgesOut(proposed) == null || graph.getEdgesIn(proposed) == null) { return null; } + if (graph.containsNode(proposed)) { + if (graph.getDegree(proposed) > 2 || graph.getEdgesOut(proposed) == null + || graph.getEdgesIn(proposed) == null) { + return null; + } SiteNode existing = (SiteNode) graph.lookupNode(proposed); edge = new SiteEdge(node, existing, default_type, level, false); edge.isAnastomotic = true; @@ -911,28 +981,25 @@ private SiteEdge createNewEdge(final int dir, final SiteNode node, final double return null; } - - /** - * {@inheritDoc} - *

- * The JSON is formatted as: - *

-	 *     {
-	 *         "type": "GROWTH",
-	 *         "interval": interval,
-	 *         "specs" : {
-	 *             "SPEC_NAME": spec value,
-	 *             "SPEC_NAME": spec value,
-	 *             ...
-	 *         }
-	 *     }
-	 * 
- */ - public String toJSON() { - String format = "{ " + "\"type\": \"GROWTH\", " + "\"interval\": %d, " + "\"specs\": %s " + "}"; - return String.format(format, interval, specs.toJSON()); - } + * {@inheritDoc} + *

+ * The JSON is formatted as: + * + *

+     *     {
+     *         "type": "GROWTH",
+     *         "interval": interval,
+     *         "specs" : {
+     *             "SPEC_NAME": spec value,
+     *             "SPEC_NAME": spec value,
+     *             ...
+     *         }
+     *     }
+     * 
+ */ + public String toJSON() { + String format = "{ " + "\"type\": \"GROWTH\", " + "\"interval\": %d, " + "\"specs\": %s " + "}"; + return String.format(format, interval, specs.toJSON()); + } } - - diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 771147dd6..82ee74574 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -23,24 +23,36 @@ /** * Extension of {@link PatchComponentSites} for graph sites. * - *

Layout of the underlying graph is specified by {@code GRAPH_LAYOUT}. The pattern layout is - * specified by using {@code "*"}. The root layout is specified by specifying roots: + *

+ * Layout of the underlying graph is specified by {@code GRAPH_LAYOUT}. The + * pattern layout is + * specified by using {@code "*"}. The root layout is specified by specifying + * roots: * *

* - *

The border {@code } can be {@code LEFT} (-x direction), {@code RIGHT} (+x direction), - * {@code TOP} (-y direction), or {@code BOTTOM} (+y direction). The type {@code } can be + *

+ * The border {@code } can be {@code LEFT} (-x direction), {@code RIGHT} + * (+x direction), + * {@code TOP} (-y direction), or {@code BOTTOM} (+y direction). The type + * {@code } can be * {@code A} / {@code a} for an artery or {@code V} / {@code v} for a vein. */ public abstract class PatchComponentSitesGraph extends PatchComponentSites { @@ -77,17 +89,18 @@ public abstract class PatchComponentSitesGraph extends PatchComponentSites { /** * Creates a {@link PatchComponentSites} using graph sites. * - *

Loaded parameters include: + *

+ * Loaded parameters include: * *

* - * @param series the simulation series + * @param series the simulation series * @param parameters the component parameters dictionary - * @param random the random number generator + * @param random the random number generator */ public PatchComponentSitesGraph(Series series, MiniBox parameters, MersenneTwisterFast random) { super(series); @@ -127,7 +140,7 @@ public Graph getGraph() { * Gets the lattice coordinates spanned by an edge between two nodes. * * @param from the node the edge extends from - * @param to the node the edge extends to + * @param to the node the edge extends to * @return the list of span coordinates */ abstract ArrayList getSpan(SiteNode from, SiteNode to); @@ -157,8 +170,11 @@ void checkSite(ArrayList s, int x, int y, int z) { /** * Initializes graph for representing sites. * - *

Calls the correct method to populate the graph with edges (either pattern or root layout). - * After the graph is defined, the corresponding indices in the lattice adjacent to edges are + *

+ * Calls the correct method to populate the graph with edges (either pattern or + * root layout). + * After the graph is defined, the corresponding indices in the lattice adjacent + * to edges are * marked. * * @param random the random number generator @@ -190,10 +206,13 @@ Graph initializeGraph(MersenneTwisterFast random) { /** * Graph step that only considers differences in concentration. * - *

Method is equivalent to the step used with {@link + *

+ * Method is equivalent to the step used with {@link * arcade.patch.env.component.PatchComponentSitesSource} and {@link - * arcade.patch.env.component.PatchComponentSitesPattern} where the amount of concentration - * added is the difference between the source concentration and the current concentration for a + * arcade.patch.env.component.PatchComponentSitesPattern} where the amount of + * concentration + * added is the difference between the source concentration and the current + * concentration for a * given molecule. */ void simpleStep() { @@ -233,8 +252,11 @@ void simpleStep() { /** * Graph step that uses traversals to calculate exact hemodynamics. * - *

Traversing the graph updates the concentrations of molecules in each edge. The amount of - * concentration added is a function of flow rate and permeability to the given molecule. + *

+ * Traversing the graph updates the concentrations of molecules in each edge. + * The amount of + * concentration added is a function of flow rate and permeability to the given + * molecule. * * @param random the random number generator */ @@ -335,9 +357,8 @@ void complexStep(MersenneTwisterFast random) { // Check for stability. double max = latticePatchVolume / edge.area; if (permeability > max) { - intConcNew = - (intConcNew * flow + latticePatchVolume * extConcNew) - / (flow + latticePatchVolume); + intConcNew = (intConcNew * flow + latticePatchVolume * extConcNew) + / (flow + latticePatchVolume); extConcNew = intConcNew; } else { // Iterate for each second in the minute time step. @@ -355,14 +376,12 @@ void complexStep(MersenneTwisterFast random) { int k = coordinate.z; if (layer.name.equalsIgnoreCase("OXYGEN")) { - delta[k][i][j] += - Math.max( - (extConcNew / oxySoluTissue - - (current[k][i][j] + delta[k][i][j])), - 0); + delta[k][i][j] += Math.max( + (extConcNew / oxySoluTissue + - (current[k][i][j] + delta[k][i][j])), + 0); } else { - delta[k][i][j] += - Math.max((extConcNew - (current[k][i][j] + delta[k][i][j])), 0); + delta[k][i][j] += Math.max((extConcNew - (current[k][i][j] + delta[k][i][j])), 0); } } @@ -380,7 +399,8 @@ void complexStep(MersenneTwisterFast random) { /** * Extension of {@link arcade.core.util.Graph.Node} for site nodes. * - *

Node tracks additional hemodynamic properties including pressure and oxygen. + *

+ * Node tracks additional hemodynamic properties including pressure and oxygen. */ public static class SiteNode extends Node { /** Node ID. */ @@ -398,6 +418,12 @@ public static class SiteNode extends Node { /** Distance for Dijkstra's algorithm. */ int distance; + /** Tick for the last update during growth. */ + int lastUpdate; + + /** Tick for when the node was added to the graph. */ + int addTime; + /** Parent node. */ SiteNode prev; @@ -450,7 +476,9 @@ public double getOxygen() { /** * Extension of {@link arcade.core.util.Graph.Edge} for site edges. * - *

Node tracks additional hemodynamic properties including radius, length, wall thickness, + *

+ * Node tracks additional hemodynamic properties including radius, length, wall + * thickness, * shear stress, circumferential stress, and volumetric flow rate. */ public static class SiteEdge extends Edge { @@ -466,6 +494,12 @@ public static class SiteEdge extends Edge { /** {@code true} if edge is ignored, {@code false} otherwise. */ boolean isIgnored; + /** {@code true} if edge is angiogenic, {@code false} otherwise. */ + boolean isAngiogenic; + + /** {@code true} if edge is anastomotic, {@code false} otherwise. */ + boolean isAnastomotic; + /** Edge type. */ final EdgeType type; @@ -508,9 +542,9 @@ public static class SiteEdge extends Edge { /** * Creates a {@link Edge} for graph sites. * - * @param from the node the edge is from - * @param to the node the edge is to - * @param type the edge type + * @param from the node the edge is from + * @param to the node the edge is to + * @param type the edge type * @param level the graph resolution level */ SiteEdge(Node from, Node to, EdgeType type, EdgeLevel level) { @@ -797,9 +831,8 @@ private ArrayList traverseNode(SiteNode node, String code) { for (Object obj : in) { SiteEdge edge = (SiteEdge) obj; if (!edge.isIgnored) { - massIn += - edge.flow * getTotal(edge.getFrom().oxygen, oxySoluPlasma) - - edge.transport.get(code); + massIn += edge.flow * getTotal(edge.getFrom().oxygen, oxySoluPlasma) + - edge.transport.get(code); } } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java index e9ccfb69c..a57eb4020 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.EnumMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import sim.util.Bag; @@ -18,11 +19,13 @@ /** * Factory for building a {@link Graph} for {@link PatchComponentSitesGraph}. * - *

Graph can be initialized in two ways: + *

+ * Graph can be initialized in two ways: * *

    - *
  • pattern layout that matches the structure used by {@link PatchComponentSitesPattern} - *
  • root layout grown from a specified root system using motifs + *
  • pattern layout that matches the structure used by + * {@link PatchComponentSitesPattern} + *
  • root layout grown from a specified root system using motifs *
*/ public abstract class PatchComponentSitesGraphFactory { @@ -210,7 +213,7 @@ public PatchComponentSitesGraphFactory(Series series) { /** * Calculates the column of the pattern based on offset and index. * - * @param i the index in the x direction + * @param i the index in the x direction * @param offset the lattice offset * @return the column index */ @@ -219,8 +222,8 @@ public PatchComponentSitesGraphFactory(Series series) { /** * Calculates the row of the pattern based on offset and index. * - * @param i the index in the x direction - * @param j the index in the y direction + * @param i the index in the x direction + * @param j the index in the y direction * @param offset the lattice offset * @return the row index */ @@ -239,21 +242,32 @@ public PatchComponentSitesGraphFactory(Series series) { * * @param fromX the x coordinate of the node the edge is from * @param fromY the y coordinate of the node the edge is from - * @param toX the x coordinate of the node the edge is to - * @param toY the y coordinate of the node the edge is to + * @param toX the x coordinate of the node the edge is to + * @param toY the y coordinate of the node the edge is to * @return the code for the edge direction */ abstract EdgeDirection getDirection(int fromX, int fromY, int toX, int toY); + /** + * Gets the opposite direction of the given edge. + * + * @param edge the edge object + * @param scale the graph resolution level + * @return the code for the opposite edge direction + */ + public EdgeDirection getOppositeDirection(SiteEdge edge, EdgeLevel level) { + return getDirection(edge.getTo(), edge.getFrom(), level); + } + /** * Adds a root motif to the graph. * - * @param graph the graph instance - * @param node0 the node the motif starts at - * @param dir the direction code of the root - * @param type the root type + * @param graph the graph instance + * @param node0 the node the motif starts at + * @param dir the direction code of the root + * @param type the root type * @param offsets the list of offsets for line roots, null otherwise - * @param random the random number generator + * @param random the random number generator * @return the bag of active edges */ abstract Bag addRoot( @@ -267,12 +281,12 @@ abstract Bag addRoot( /** * Adds an edge motif to the graph. * - * @param graph the graph instance - * @param node0 the node the motif starts at - * @param edge0 the edge the motif is being added to - * @param type the edge type - * @param level the graph resolution level - * @param motif the motif type + * @param graph the graph instance + * @param node0 the node the motif starts at + * @param edge0 the edge the motif is being added to + * @param type the edge type + * @param level the graph resolution level + * @param motif the motif type * @param random the random number generator * @return the bag of active edges */ @@ -288,10 +302,10 @@ abstract Bag addMotif( /** * Adds a capillary segment joining edges of different types to the graph. * - * @param graph the graph instance - * @param node0 the node the segment starts at - * @param dir the direction code for the segment - * @param level the graph resolution level + * @param graph the graph instance + * @param node0 the node the segment starts at + * @param dir the direction code for the segment + * @param level the graph resolution level * @param random the random number generator */ abstract void addSegment( @@ -304,11 +318,11 @@ abstract void addSegment( /** * Adds a connection joining edges of the same type to the graph. * - * @param graph the graph instance - * @param node0 the node the connection starts at - * @param dir the direction code for the segment - * @param type the connection type - * @param level the graph resolution level + * @param graph the graph instance + * @param node0 the node the connection starts at + * @param dir the direction code for the segment + * @param type the connection type + * @param level the graph resolution level * @param random the random number generator */ abstract void addConnection( @@ -327,10 +341,19 @@ abstract void addConnection( */ abstract int[] getOffset(EdgeDirection offset); + /** + * Get a map of possible offset directions to their corresponding coordinate + * changes. + * + * @return the map of offset directions to their corresponding coordinate + * changes + */ + abstract EnumMap getOffsets(); + /** * Gets the length of the given edge. * - * @param edge the edge object + * @param edge the edge object * @param level the graph resolution level * @return the length of the edge */ @@ -346,10 +369,10 @@ abstract void addConnection( /** * Creates a {@link Root} for graph sites using a root layout. * - * @param border the border the root extends from + * @param border the border the root extends from * @param percent the percentage distance along the border - * @param type the root type - * @param level the graph resolution level + * @param type the root type + * @param level the graph resolution level * @return a {@link Root} object */ abstract Root createRoot(Border border, double percent, EdgeType type, EdgeLevel level); @@ -357,10 +380,10 @@ abstract void addConnection( /** * Creates offsets for a {@link Root} for graph sites using a root layout. * - * @param border the border the root extends from + * @param border the border the root extends from * @param percent the percentage distance in the perpendicular direction - * @param level the graph resolution level - * @param random the random number generator + * @param level the graph resolution level + * @param random the random number generator * @return a list of offsets */ abstract EdgeDirection[] createRootOffsets( @@ -386,10 +409,10 @@ static class Root { /** * Creates a {@code Root} object for generating root graphs. * - * @param x the x coordinate - * @param y the y coordinate + * @param x the x coordinate + * @param y the y coordinate * @param type the edge type - * @param dir the direction code of the root + * @param dir the direction code of the root */ Root(int x, int y, EdgeType type, EdgeDirection dir) { node = new SiteNode(x, y, 0); @@ -401,7 +424,7 @@ static class Root { /** * Gets direction code for an edge. * - * @param edge the edge object + * @param edge the edge object * @param level the graph resolution level * @return the code for the edge direction */ @@ -412,8 +435,8 @@ EdgeDirection getDirection(SiteEdge edge, EdgeLevel level) { /** * Gets direction code for an edge. * - * @param from the node the edge is from - * @param to the node the edge is to + * @param from the node the edge is from + * @param to the node the edge is to * @param level the graph resolution level * @return the code for the edge direction */ @@ -426,9 +449,9 @@ EdgeDirection getDirection(SiteNode from, SiteNode to, EdgeLevel level) { /** * Creates a node offset in the given direction. * - * @param node the node of the initial location + * @param node the node of the initial location * @param offset the offset direction - * @param level the graph resolution level + * @param level the graph resolution level * @return an offset node */ SiteNode offsetNode(SiteNode node, EdgeDirection offset, EdgeLevel level) { @@ -462,7 +485,7 @@ public Graph initializePatternGraph(MersenneTwisterFast random) { } // Traverse graph from capillaries to calculate radii. - ArrayList caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); + ArrayList caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); updateRadii(graph, caps, CalculationType.UPSTREAM_PATTERN, random); updateRadii(graph, caps, CalculationType.DOWNSTREAM_PATTERN, random); @@ -532,30 +555,27 @@ private void mergePatternGraph(Graph graph) { for (SiteEdge edge1 : set) { if (graph.getOutDegree(edge1.getTo()) == 1) { int scale1 = scales.get(edge1); - EdgeDirection dir1 = - getDirection( - edge1.getFrom().getX() / scale1, - edge1.getFrom().getY() / scale1, - edge1.getTo().getX() / scale1, - edge1.getTo().getY() / scale1); + EdgeDirection dir1 = getDirection( + edge1.getFrom().getX() / scale1, + edge1.getFrom().getY() / scale1, + edge1.getTo().getX() / scale1, + edge1.getTo().getY() / scale1); SiteEdge edge2 = (SiteEdge) edge1.getEdgesOut().get(0); int scale2 = scales.get(edge2); - EdgeDirection dir2 = - getDirection( - edge2.getFrom().getX() / scale2, - edge2.getFrom().getY() / scale2, - edge2.getTo().getX() / scale2, - edge2.getTo().getY() / scale2); + EdgeDirection dir2 = getDirection( + edge2.getFrom().getX() / scale2, + edge2.getFrom().getY() / scale2, + edge2.getTo().getX() / scale2, + edge2.getTo().getY() / scale2); // Join edges that are the same direction and type. if (dir1 == dir2 && edge1.type == edge2.type) { - SiteEdge join = - new SiteEdge( - edge1.getFrom(), - edge2.getTo(), - edge1.type, - EdgeLevel.VARIABLE); + SiteEdge join = new SiteEdge( + edge1.getFrom(), + edge2.getTo(), + edge1.type, + EdgeLevel.VARIABLE); scales.put(join, scale1 + scale2); // Set length to be sum and radius to be average of the @@ -588,7 +608,7 @@ private void mergePatternGraph(Graph graph) { /** * Initializes graph with edges and nodes in a root layout. * - * @param random the random number generator + * @param random the random number generator * @param graphLayout the specification for layout of roots * @return a graph instance with root layout */ @@ -675,11 +695,11 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) /** * Updates hemodynamic properties for graph sites with root layouts. * - * @param graph the graph instance + * @param graph the graph instance * @param arteries the list of artery edges - * @param veins the list of vein edges - * @param level the graph resolution level - * @param random the random number generator + * @param veins the list of vein edges + * @param level the graph resolution level + * @param random the random number generator */ private void updateRootGraph( Graph graph, @@ -692,14 +712,14 @@ private void updateRootGraph( // Store upper level capillaries. if (level != EdgeLevel.LEVEL_1) { - caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); + caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); for (SiteEdge edge : caps) { graph.removeEdge(edge); } } // Get all leaves and update radii. - list = getLeavesByType(graph, new EdgeType[] {EdgeType.ARTERY, EdgeType.VEIN}); + list = getLeavesByType(graph, new EdgeType[] { EdgeType.ARTERY, EdgeType.VEIN }); updateRadii(graph, list, CalculationType.UPSTREAM_ALL); // Replace level 1 edges capillaries. @@ -712,17 +732,16 @@ private void updateRootGraph( addSegments(graph, level, random); addConnections(graph, level, random); - caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); + caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); // Get capillaries and arterioles and update radii. switch (level) { case LEVEL_1: - list = - getEdgeByType( - graph, new EdgeType[] {EdgeType.CAPILLARY, EdgeType.ARTERIOLE}); + list = getEdgeByType( + graph, new EdgeType[] { EdgeType.CAPILLARY, EdgeType.ARTERIOLE }); break; case LEVEL_2: - list = getEdgeByType(graph, new EdgeType[] {EdgeType.ARTERIOLE}, level); + list = getEdgeByType(graph, new EdgeType[] { EdgeType.ARTERIOLE }, level); list.addAll(caps); break; default: @@ -737,10 +756,10 @@ private void updateRootGraph( // Get capillaries and venules and update radii. switch (level) { case LEVEL_1: - list = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY, EdgeType.VENULE}); + list = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY, EdgeType.VENULE }); break; case LEVEL_2: - list = getEdgeByType(graph, new EdgeType[] {EdgeType.VENULE}, level); + list = getEdgeByType(graph, new EdgeType[] { EdgeType.VENULE }, level); list.addAll(caps); break; default: @@ -810,14 +829,13 @@ private void updateRootGraph( /** * Refines the graph for graph sites with root layouts. * - * @param graph the graph instance + * @param graph the graph instance * @param arteries the list of artery edges - * @param veins the list of vein edges + * @param veins the list of vein edges */ private void refineRootGraph(Graph graph, ArrayList arteries, ArrayList veins) { // Reverse edges that are veins and venules. - ArrayList reverse = - getEdgeByType(graph, new EdgeType[] {EdgeType.VEIN, EdgeType.VENULE}); + ArrayList reverse = getEdgeByType(graph, new EdgeType[] { EdgeType.VEIN, EdgeType.VENULE }); for (SiteEdge edge : reverse) { graph.reverseEdge(edge); } @@ -826,7 +844,7 @@ private void refineRootGraph(Graph graph, ArrayList arteries, ArrayList caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); + ArrayList caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); if (caps.size() < 1) { graph.clear(); return; @@ -846,7 +864,7 @@ private void refineRootGraph(Graph graph, ArrayList arteries, ArrayList list = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); + ArrayList list = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); updateRadii(graph, list, CalculationType.UPSTREAM_ARTERIES); updateRadii(graph, list, CalculationType.DOWNSTREAM_VEINS); @@ -955,17 +973,16 @@ private Bag subdivideRootGraph(Graph graph, EdgeLevel level) { /** * Remodels sites based on shear stress. * - * @param graph the graph instance - * @param level the graph resolution level + * @param graph the graph instance + * @param level the graph resolution level * @param random the random number generator * @return the fraction of edges remodeled */ private double remodelRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFast random) { // Remove capillaries, arterioles, and venules. - ArrayList list = - getEdgeByType( - graph, - new EdgeType[] {EdgeType.CAPILLARY, EdgeType.VENULE, EdgeType.ARTERIOLE}); + ArrayList list = getEdgeByType( + graph, + new EdgeType[] { EdgeType.CAPILLARY, EdgeType.VENULE, EdgeType.ARTERIOLE }); for (SiteEdge edge : list) { graph.removeEdge(edge); } @@ -1013,26 +1030,24 @@ private double remodelRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFas for (Object obj : allEdges) { SiteEdge edge = (SiteEdge) obj; if (edge.tag == EdgeTag.ADD && graph.getDegree(edge.getTo()) < 3) { - Bag bag1 = - addMotif( - graph, - edge.getTo(), - edge, - edge.type, - level, - EdgeMotif.TRIPLE, - random); + Bag bag1 = addMotif( + graph, + edge.getTo(), + edge, + edge.type, + level, + EdgeMotif.TRIPLE, + random); SiteEdge edge1 = (SiteEdge) bag1.get(0); - Bag bag2 = - addMotif( - graph, - edge1.getTo(), - edge, - edge.type, - level, - EdgeMotif.DOUBLE, - random); + Bag bag2 = addMotif( + graph, + edge1.getTo(), + edge, + edge.type, + level, + EdgeMotif.DOUBLE, + random); SiteEdge edge2 = (SiteEdge) bag2.get(0); addMotif(graph, edge2.getTo(), edge, edge.type, level, EdgeMotif.SINGLE, random); @@ -1116,10 +1131,10 @@ private ArrayList parseRoots(String layout, MersenneTwisterFast random) { /** * Adds motifs to graph until no additional motifs can be added. * - * @param graph the graph instance - * @param bag the current bag of active edges - * @param level the graph resolution level - * @param motif the motif code + * @param graph the graph instance + * @param bag the current bag of active edges + * @param level the graph resolution level + * @param motif the motif code * @param random the random number generator * @return the updated bag of active edges */ @@ -1168,8 +1183,8 @@ private Bag addMotifs( /** * Adds segments to graph between arteries and veins. * - * @param graph the graph instance - * @param level the graph resolution level + * @param graph the graph instance + * @param level the graph resolution level * @param random the random number generator */ private void addSegments(Graph graph, EdgeLevel level, MersenneTwisterFast random) { @@ -1192,8 +1207,8 @@ private void addSegments(Graph graph, EdgeLevel level, MersenneTwisterFast rando /** * Adds connections to graphs between arteries or between veins. * - * @param graph the graph instance - * @param level the graph resolution level + * @param graph the graph instance + * @param level the graph resolution level * @param random the random number generator */ private void addConnections(Graph graph, EdgeLevel level, MersenneTwisterFast random) { diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java index 75aa944eb..bc321cd33 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java @@ -14,9 +14,11 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.*; /** - * Concrete implementation of {@link PatchComponentSitesGraphFactory} for rectangular geometry. + * Concrete implementation of {@link PatchComponentSitesGraphFactory} for + * rectangular geometry. * - *

For pattern layout, the graph is given by: + *

+ * For pattern layout, the graph is given by: * *

  *                         _ _ _ _
@@ -34,108 +36,108 @@
  *                       \ _ _ _ _ /
  * 
* - *

For root layouts, each node has eight possible orientations for the edge: left, right, up, - * down, up left, up right, down left, and down right. When initializing roots from a border, only + *

+ * For root layouts, each node has eight possible orientations for the edge: + * left, right, up, + * down, up left, up right, down left, and down right. When initializing roots + * from a border, only * certain orientations are possible: * *

    - *
  • left border = right, up right, down right - *
  • right border = left, up left, down left - *
  • top border = down, down right, down left - *
  • bottom border = up, up right, up left + *
  • left border = right, up right, down right + *
  • right border = left, up left, down left + *
  • top border = down, down right, down left + *
  • bottom border = up, up right, up left *
*/ public class PatchComponentSitesGraphFactoryRect extends PatchComponentSitesGraphFactory { /** List of all possible edge directions. */ - private static final EnumSet EDGE_DIRECTIONS = - EnumSet.of( - EdgeDirection.UP, - EdgeDirection.UP_RIGHT, - EdgeDirection.RIGHT, - EdgeDirection.DOWN_RIGHT, - EdgeDirection.DOWN, - EdgeDirection.DOWN_LEFT, - EdgeDirection.LEFT, - EdgeDirection.UP_LEFT); + private static final EnumSet EDGE_DIRECTIONS = EnumSet.of( + EdgeDirection.UP, + EdgeDirection.UP_RIGHT, + EdgeDirection.RIGHT, + EdgeDirection.DOWN_RIGHT, + EdgeDirection.DOWN, + EdgeDirection.DOWN_LEFT, + EdgeDirection.LEFT, + EdgeDirection.UP_LEFT); /** Map of edge directions to their reverse direction. */ - private static final EnumSet SINGLE_EDGE_DIRECTIONS = - EnumSet.of( - EdgeDirection.UP_RIGHT, - EdgeDirection.DOWN_RIGHT, - EdgeDirection.DOWN_LEFT, - EdgeDirection.UP_LEFT); + private static final EnumSet SINGLE_EDGE_DIRECTIONS = EnumSet.of( + EdgeDirection.UP_RIGHT, + EdgeDirection.DOWN_RIGHT, + EdgeDirection.DOWN_LEFT, + EdgeDirection.UP_LEFT); /** Map of edge directions to their reverse direction. */ - private static final EnumMap REVERSE_EDGE_DIRECTIONS = - new EnumMap(EdgeDirection.class) { - { - put(EdgeDirection.UP, EdgeDirection.DOWN); - put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); - put(EdgeDirection.RIGHT, EdgeDirection.LEFT); - put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); - put(EdgeDirection.DOWN, EdgeDirection.UP); - put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); - put(EdgeDirection.LEFT, EdgeDirection.RIGHT); - put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); - } - }; + private static final EnumMap REVERSE_EDGE_DIRECTIONS = new EnumMap( + EdgeDirection.class) { + { + put(EdgeDirection.UP, EdgeDirection.DOWN); + put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); + put(EdgeDirection.RIGHT, EdgeDirection.LEFT); + put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); + put(EdgeDirection.DOWN, EdgeDirection.UP); + put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); + put(EdgeDirection.LEFT, EdgeDirection.RIGHT); + put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); + } + }; /** List of coordinate offsets for each direction. */ - private static final EnumMap OFFSETS = - new EnumMap(EdgeDirection.class) { - { - put(EdgeDirection.UP, new int[] {0, -1, 0}); - put(EdgeDirection.UP_RIGHT, new int[] {1, -1, 0}); - put(EdgeDirection.RIGHT, new int[] {1, 0, 0}); - put(EdgeDirection.DOWN_RIGHT, new int[] {1, 1, 0}); - put(EdgeDirection.DOWN, new int[] {0, 1, 0}); - put(EdgeDirection.DOWN_LEFT, new int[] {-1, 1, 0}); - put(EdgeDirection.LEFT, new int[] {-1, 0, 0}); - put(EdgeDirection.UP_LEFT, new int[] {-1, -1, 0}); - } - }; + private static final EnumMap OFFSETS = new EnumMap( + EdgeDirection.class) { + { + put(EdgeDirection.UP, new int[] { 0, -1, 0 }); + put(EdgeDirection.UP_RIGHT, new int[] { 1, -1, 0 }); + put(EdgeDirection.RIGHT, new int[] { 1, 0, 0 }); + put(EdgeDirection.DOWN_RIGHT, new int[] { 1, 1, 0 }); + put(EdgeDirection.DOWN, new int[] { 0, 1, 0 }); + put(EdgeDirection.DOWN_LEFT, new int[] { -1, 1, 0 }); + put(EdgeDirection.LEFT, new int[] { -1, 0, 0 }); + put(EdgeDirection.UP_LEFT, new int[] { -1, -1, 0 }); + } + }; /** List of offset directions for root directions. */ - private static final EnumMap ROOT_OFFSETS = - new EnumMap(EdgeDirection.class) { - { - put( - EdgeDirection.UP, - new EdgeDirection[] {EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT}); - put( - EdgeDirection.UP_RIGHT, - new EdgeDirection[] {EdgeDirection.RIGHT, EdgeDirection.UP}); - put( - EdgeDirection.RIGHT, - new EdgeDirection[] {EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT}); - put( - EdgeDirection.DOWN_RIGHT, - new EdgeDirection[] {EdgeDirection.DOWN, EdgeDirection.RIGHT}); - put( - EdgeDirection.DOWN, - new EdgeDirection[] { - EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN_RIGHT - }); - put( - EdgeDirection.DOWN_LEFT, - new EdgeDirection[] {EdgeDirection.LEFT, EdgeDirection.DOWN}); - put( - EdgeDirection.LEFT, - new EdgeDirection[] {EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT}); - put( - EdgeDirection.UP_LEFT, - new EdgeDirection[] {EdgeDirection.UP, EdgeDirection.LEFT}); - } - }; + private static final EnumMap ROOT_OFFSETS = new EnumMap( + EdgeDirection.class) { + { + put( + EdgeDirection.UP, + new EdgeDirection[] { EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT }); + put( + EdgeDirection.UP_RIGHT, + new EdgeDirection[] { EdgeDirection.RIGHT, EdgeDirection.UP }); + put( + EdgeDirection.RIGHT, + new EdgeDirection[] { EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT }); + put( + EdgeDirection.DOWN_RIGHT, + new EdgeDirection[] { EdgeDirection.DOWN, EdgeDirection.RIGHT }); + put( + EdgeDirection.DOWN, + new EdgeDirection[] { + EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN_RIGHT + }); + put( + EdgeDirection.DOWN_LEFT, + new EdgeDirection[] { EdgeDirection.LEFT, EdgeDirection.DOWN }); + put( + EdgeDirection.LEFT, + new EdgeDirection[] { EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT }); + put( + EdgeDirection.UP_LEFT, + new EdgeDirection[] { EdgeDirection.UP, EdgeDirection.LEFT }); + } + }; /** Array positions for edge directions. */ - private static final EdgeDirection[][] DIRS = - new EdgeDirection[][] { - {EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT}, - {EdgeDirection.LEFT, EdgeDirection.UNDEFINED, EdgeDirection.RIGHT}, - {EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT}, - }; + private static final EdgeDirection[][] DIRS = new EdgeDirection[][] { + { EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT }, + { EdgeDirection.LEFT, EdgeDirection.UNDEFINED, EdgeDirection.RIGHT }, + { EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT }, + }; /** List of edge lengths. */ private final double[] edgeLengths; @@ -147,7 +149,7 @@ public class PatchComponentSitesGraphFactoryRect extends PatchComponentSitesGrap */ public PatchComponentSitesGraphFactoryRect(Series series) { super(series); - edgeLengths = new double[] {series.ds, series.ds * Math.sqrt(2)}; + edgeLengths = new double[] { series.ds, series.ds * Math.sqrt(2) }; } @Override @@ -155,12 +157,17 @@ int[] getOffset(EdgeDirection offset) { return OFFSETS.get(offset); } + @Override + EnumMap getOffsets() { + return OFFSETS; + } + /** * Checks if there is an edge in the cross diagonal. * * @param graph the graph instance - * @param from the node the edge is from - * @param to the node the edge is to + * @param from the node the edge is from + * @param to the node the edge is to * @param level the graph resolution level * @return {@code true} if no cross diagonal edge, {@code false} otherwise */ @@ -244,24 +251,24 @@ void createPattern(Graph graph) { int row = calcRow(i, j, offset); if (col == 0 && row == 5) { - edges.add(new int[] {i, j, k, i + 1, j, k}); + edges.add(new int[] { i, j, k, i + 1, j, k }); } else if (col == 1 && row == 5) { - edges.add(new int[] {i, j, k, i + 1, j, k}); + edges.add(new int[] { i, j, k, i + 1, j, k }); } else if (col == 2 && row == 5) { - edges.add(new int[] {i, j, k, i + 1, j, k}); + edges.add(new int[] { i, j, k, i + 1, j, k }); } else if (col == 3 && row == 5) { - edges.add(new int[] {i, j, k, i + 1, j, k}); + edges.add(new int[] { i, j, k, i + 1, j, k }); } else if (col == 5 && row == 4) { - edges.add(new int[] {i, j, k, i, j - 1, k}); + edges.add(new int[] { i, j, k, i, j - 1, k }); } else if (col == 5 && row == 0) { - edges.add(new int[] {i, j, k, i, j + 1, k}); + edges.add(new int[] { i, j, k, i, j + 1, k }); } else if (col == 5 && row == 1) { - edges.add(new int[] {i, j, k, i + 1, j + 1, k}); + edges.add(new int[] { i, j, k, i + 1, j + 1, k }); } else if (col == 5 && row == 3) { - edges.add(new int[] {i, j, k, i + 1, j - 1, k}); + edges.add(new int[] { i, j, k, i + 1, j - 1, k }); } else if (col == 4 && row == 5) { - edges.add(new int[] {i, j, k, i + 1, j - 1, k}); - edges.add(new int[] {i, j, k, i + 1, j + 1, k}); + edges.add(new int[] { i, j, k, i + 1, j - 1, k }); + edges.add(new int[] { i, j, k, i + 1, j + 1, k }); } } } @@ -279,10 +286,9 @@ void createPattern(Graph graph) { // Add edge to graph. SiteNode from = new SiteNode(e[0], e[1], e[2]); SiteNode to = new SiteNode(e[3], e[4], e[5]); - EdgeType type = - (e[0] == thresh - ? EdgeType.CAPILLARY - : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); + EdgeType type = (e[0] == thresh + ? EdgeType.CAPILLARY + : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); SiteEdge edge = new SiteEdge(from, to, type, EdgeLevel.VARIABLE); graph.addEdge(edge); } @@ -360,31 +366,27 @@ EdgeDirection[] createRootOffsets( // Get direction list. switch (border) { case LEFT: - directions = - new EdgeDirection[] { - EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT - }; + directions = new EdgeDirection[] { + EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT + }; index = 1; break; case RIGHT: - directions = - new EdgeDirection[] { - EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT - }; + directions = new EdgeDirection[] { + EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT + }; index = 1; break; case TOP: - directions = - new EdgeDirection[] { - EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT - }; + directions = new EdgeDirection[] { + EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT + }; index = 0; break; case BOTTOM: - directions = - new EdgeDirection[] { - EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT - }; + directions = new EdgeDirection[] { + EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT + }; index = 0; break; default: @@ -446,7 +448,8 @@ && checkCross(graph, node0, node1, level)) { return bag; } - // Add the two leaves of the tripod if line is 0, otherwise add in the root line. + // Add the two leaves of the tripod if line is 0, otherwise add in the root + // line. if (offsets == null) { for (EdgeDirection offset : ROOT_OFFSETS.get(dir)) { SiteNode node2 = offsetNode(node1, offset, level); diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java index 4935ec12a..0607a4891 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java @@ -14,9 +14,11 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.*; /** - * Concrete implementation of {@link PatchComponentSitesGraphFactory} for triangular geometry. + * Concrete implementation of {@link PatchComponentSitesGraphFactory} for + * triangular geometry. * - *

For pattern layout, the graph is given by: + *

+ * For pattern layout, the graph is given by: * *

  *                         ___ ___
@@ -34,104 +36,105 @@
  *                       \ ___ ___ /
  * 
* - *

For root layouts, each node has six possible orientations for the edge: left, right, up left, - * up right, down left, and down right. When initializing roots from a border, only certain + *

+ * For root layouts, each node has six possible orientations for the edge: left, + * right, up left, + * up right, down left, and down right. When initializing roots from a border, + * only certain * orientations are possible: * *

    - *
  • left border = right, up right, down right - *
  • right border = left, up left, down left - *
  • top border = down right, down left - *
  • bottom border = up right, up left + *
  • left border = right, up right, down right + *
  • right border = left, up left, down left + *
  • top border = down right, down left + *
  • bottom border = up right, up left *
*/ public class PatchComponentSitesGraphFactoryTri extends PatchComponentSitesGraphFactory { /** List of all possible edge directions. */ - private static final EnumSet EDGE_DIRECTIONS = - EnumSet.of( - EdgeDirection.UP_LEFT, - EdgeDirection.UP_RIGHT, - EdgeDirection.RIGHT, - EdgeDirection.DOWN_RIGHT, - EdgeDirection.DOWN_LEFT, - EdgeDirection.LEFT); + private static final EnumSet EDGE_DIRECTIONS = EnumSet.of( + EdgeDirection.UP_LEFT, + EdgeDirection.UP_RIGHT, + EdgeDirection.RIGHT, + EdgeDirection.DOWN_RIGHT, + EdgeDirection.DOWN_LEFT, + EdgeDirection.LEFT); /** Map of edge directions to their reverse direction. */ - private static final EnumMap REVERSE_EDGE_DIRECTIONS = - new EnumMap(EdgeDirection.class) { - { - put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); - put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); - put(EdgeDirection.RIGHT, EdgeDirection.LEFT); - put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); - put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); - put(EdgeDirection.LEFT, EdgeDirection.RIGHT); - } - }; + private static final EnumMap REVERSE_EDGE_DIRECTIONS = new EnumMap( + EdgeDirection.class) { + { + put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); + put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); + put(EdgeDirection.RIGHT, EdgeDirection.LEFT); + put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); + put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); + put(EdgeDirection.LEFT, EdgeDirection.RIGHT); + } + }; /** List of coordinate offsets for each direction. */ - private static final EnumMap OFFSETS = - new EnumMap(EdgeDirection.class) { - { - put(EdgeDirection.UP_LEFT, new int[] {-1, -1, 0}); - put(EdgeDirection.UP_RIGHT, new int[] {1, -1, 0}); - put(EdgeDirection.RIGHT, new int[] {2, 0, 0}); - put(EdgeDirection.DOWN_RIGHT, new int[] {1, 1, 0}); - put(EdgeDirection.DOWN_LEFT, new int[] {-1, 1, 0}); - put(EdgeDirection.LEFT, new int[] {-2, 0, 0}); - } - }; + private static final EnumMap OFFSETS = new EnumMap( + EdgeDirection.class) { + { + put(EdgeDirection.UP_LEFT, new int[] { -1, -1, 0 }); + put(EdgeDirection.UP_RIGHT, new int[] { 1, -1, 0 }); + put(EdgeDirection.RIGHT, new int[] { 2, 0, 0 }); + put(EdgeDirection.DOWN_RIGHT, new int[] { 1, 1, 0 }); + put(EdgeDirection.DOWN_LEFT, new int[] { -1, 1, 0 }); + put(EdgeDirection.LEFT, new int[] { -2, 0, 0 }); + } + }; /** List of offset directions for root directions. */ - private static final EnumMap ROOT_OFFSETS = - new EnumMap(EdgeDirection.class) { - { - put( - EdgeDirection.UP_LEFT, - new EdgeDirection[] {EdgeDirection.UP_RIGHT, EdgeDirection.LEFT}); - put( - EdgeDirection.UP_RIGHT, - new EdgeDirection[] {EdgeDirection.RIGHT, EdgeDirection.UP_LEFT}); - put( - EdgeDirection.RIGHT, - new EdgeDirection[] {EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT}); - put( - EdgeDirection.DOWN_RIGHT, - new EdgeDirection[] {EdgeDirection.DOWN_LEFT, EdgeDirection.RIGHT}); - put( - EdgeDirection.DOWN_LEFT, - new EdgeDirection[] {EdgeDirection.LEFT, EdgeDirection.DOWN_RIGHT}); - put( - EdgeDirection.LEFT, - new EdgeDirection[] {EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT}); - } - }; + private static final EnumMap ROOT_OFFSETS = new EnumMap( + EdgeDirection.class) { + { + put( + EdgeDirection.UP_LEFT, + new EdgeDirection[] { EdgeDirection.UP_RIGHT, EdgeDirection.LEFT }); + put( + EdgeDirection.UP_RIGHT, + new EdgeDirection[] { EdgeDirection.RIGHT, EdgeDirection.UP_LEFT }); + put( + EdgeDirection.RIGHT, + new EdgeDirection[] { EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT }); + put( + EdgeDirection.DOWN_RIGHT, + new EdgeDirection[] { EdgeDirection.DOWN_LEFT, EdgeDirection.RIGHT }); + put( + EdgeDirection.DOWN_LEFT, + new EdgeDirection[] { EdgeDirection.LEFT, EdgeDirection.DOWN_RIGHT }); + put( + EdgeDirection.LEFT, + new EdgeDirection[] { EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT }); + } + }; /** Array positions for edge directions. */ - private static final EdgeDirection[][] DIRS = - new EdgeDirection[][] { - { + private static final EdgeDirection[][] DIRS = new EdgeDirection[][] { + { EdgeDirection.UNDEFINED, EdgeDirection.UP_LEFT, EdgeDirection.UNDEFINED, EdgeDirection.UP_RIGHT, EdgeDirection.UNDEFINED - }, - { + }, + { EdgeDirection.LEFT, EdgeDirection.UNDEFINED, EdgeDirection.UNDEFINED, EdgeDirection.UNDEFINED, EdgeDirection.RIGHT - }, - { + }, + { EdgeDirection.UNDEFINED, EdgeDirection.DOWN_LEFT, EdgeDirection.UNDEFINED, EdgeDirection.DOWN_RIGHT, EdgeDirection.UNDEFINED, - } - }; + } + }; /** Length of edge. */ private final double edgeLength; @@ -151,6 +154,11 @@ int[] getOffset(EdgeDirection offset) { return OFFSETS.get(offset); } + @Override + EnumMap getOffsets() { + return OFFSETS; + } + @Override int calcOffset(int k) { return (latticeHeight - k / 2 - 1) % 3; @@ -202,22 +210,22 @@ void createPattern(Graph graph) { int row = calcRow(i, j, offset); if (col == 0 && row == 4) { - edges.add(new int[] {i, j, k, i + 2, j, k}); + edges.add(new int[] { i, j, k, i + 2, j, k }); } else if (col == 5 && row == 1) { - edges.add(new int[] {i, j, k, i + 2, j, k}); + edges.add(new int[] { i, j, k, i + 2, j, k }); } else if (col == 7 && row == 1) { - edges.add(new int[] {i, j, k, i + 2, j, k}); + edges.add(new int[] { i, j, k, i + 2, j, k }); } else if (col == 3 && row == 3) { - edges.add(new int[] {i, j, k, i + 1, j - 1, k}); + edges.add(new int[] { i, j, k, i + 1, j - 1, k }); } else if (col == 4 && row == 2) { - edges.add(new int[] {i, j, k, i + 1, j - 1, k}); + edges.add(new int[] { i, j, k, i + 1, j - 1, k }); } else if (col == 3 && row == 5) { - edges.add(new int[] {i, j, k, i + 1, j + 1, k}); + edges.add(new int[] { i, j, k, i + 1, j + 1, k }); } else if (col == 4 && row == 0) { - edges.add(new int[] {i, j, k, i + 1, j + 1, k}); + edges.add(new int[] { i, j, k, i + 1, j + 1, k }); } else if (col == 2 && row == 4) { - edges.add(new int[] {i, j, k, i + 1, j - 1, k}); - edges.add(new int[] {i, j, k, i + 1, j + 1, k}); + edges.add(new int[] { i, j, k, i + 1, j - 1, k }); + edges.add(new int[] { i, j, k, i + 1, j + 1, k }); } } } @@ -235,17 +243,16 @@ void createPattern(Graph graph) { // Add edge to graph. SiteNode from = new SiteNode(e[0], e[1], e[2]); SiteNode to = new SiteNode(e[3], e[4], e[5]); - EdgeType type = - (e[0] == thresh - ? EdgeType.CAPILLARY - : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); + EdgeType type = (e[0] == thresh + ? EdgeType.CAPILLARY + : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); SiteEdge edge = new SiteEdge(from, to, type, EdgeLevel.VARIABLE); graph.addEdge(edge); } } // Traverse graph from leftmost nodes to identify unnecessary edges. - int[] offsets = new int[] {0, 1, 0}; + int[] offsets = new int[] { 0, 1, 0 }; for (int k = 0; k < latticeHeight; k += 2) { int offset = calcOffset(k); int ro = offsets[offset]; @@ -334,26 +341,23 @@ EdgeDirection[] createRootOffsets( // Get direction list. switch (border) { case LEFT: - directions = - new EdgeDirection[] { - EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT - }; + directions = new EdgeDirection[] { + EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT + }; index = 1; break; case RIGHT: - directions = - new EdgeDirection[] { - EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT - }; + directions = new EdgeDirection[] { + EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT + }; index = 1; break; case TOP: - directions = - new EdgeDirection[] {EdgeDirection.DOWN_RIGHT, EdgeDirection.DOWN_LEFT}; + directions = new EdgeDirection[] { EdgeDirection.DOWN_RIGHT, EdgeDirection.DOWN_LEFT }; index = 0; break; case BOTTOM: - directions = new EdgeDirection[] {EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT}; + directions = new EdgeDirection[] { EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT }; index = 0; break; default: @@ -433,7 +437,8 @@ Bag addRoot( return bag; } - // Add the two leaves of the tripod if line is 0, otherwise add in the root line. + // Add the two leaves of the tripod if line is 0, otherwise add in the root + // line. if (offsets == null) { for (EdgeDirection offset : ROOT_OFFSETS.get(dir)) { SiteNode node2 = offsetNode(node1, offset, level); From fb498be4318e34bc2135cfdff3a65e1bd7c25bf6 Mon Sep 17 00:00:00 2001 From: cainja Date: Sat, 7 Jun 2025 17:20:23 -0700 Subject: [PATCH 03/56] fixed compiler errors for angiogenesis --- src/arcade/core/util/Graph.java | 40 ++ src/arcade/core/util/Solver.java | 41 ++- .../env/component/PatchComponentGrowth.java | 348 +++++++++--------- .../component/PatchComponentSitesGraph.java | 17 +- .../PatchComponentSitesGraphFactory.java | 30 +- .../PatchComponentSitesGraphUtilities.java | 42 ++- 6 files changed, 315 insertions(+), 203 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 778946092..bf6214b0c 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -33,6 +33,9 @@ public enum Strategy { /** Collection of all {@code Edge} objects in a graph. */ private final Bag allEdges; + /** Map of {@code Node} objects, for lookup. */ + private final Map nodes; + /** Map of {@code Node} OUT to bag of {@code Edge} objects. */ private final Map nodeToOutBag; @@ -44,6 +47,7 @@ public Graph() { allEdges = new Bag(); nodeToOutBag = new HashMap<>(); nodeToInBag = new HashMap<>(); + nodes = new HashMap<>(); } /** @@ -93,6 +97,14 @@ public boolean contains(Edge edge) { return checkEdge(edge); } + public Node lookup(int x, int y, int z){ + return nodes.get("(" + x + "," + y + "," + z + ")"); + } + + public Node lookup(Node node){ + return nodes.get(node.toString()); + } + /** * Gets all edges in the graph. * @@ -257,6 +269,8 @@ public void mergeNodes() { e.setTo(join); } } + + nodes.put(join.toString(), join); } } @@ -280,6 +294,25 @@ public void addEdge(Edge edge) { setOutMap(edge.getFrom(), edge); setInMap(edge.getTo(), edge); setLinks(edge); + addNodes(edge); + } + + + private void addNodes(Edge edge){ + Node from = edge.getFrom(); + Node to = edge.getTo(); + if (!nodes.containsKey(from.toString())){ + nodes.put(from.toString(), from); + } + else { + from = nodes.get(from.toString()); + } + if (!nodes.containsKey(to.toString())){ + nodes.put(to.toString(), to); + } + else { + to = nodes.get(to.toString()); + } } /** @@ -752,6 +785,13 @@ public Edge(Node from, Node to) { edgesOut = new ArrayList<>(); } + public Edge(Node from, Node to, boolean duplicate) { + this.from = duplicate ? from.duplicate() : from; + this.to = duplicate ? to.duplicate() : to; + edgesIn = new ArrayList<>(); + edgesOut = new ArrayList<>(); + } + /** * Gets the node the edge points to based on the calculation strategy (e.g. * upstream or diff --git a/src/arcade/core/util/Solver.java b/src/arcade/core/util/Solver.java index 019aefff8..d8deb4152 100644 --- a/src/arcade/core/util/Solver.java +++ b/src/arcade/core/util/Solver.java @@ -428,9 +428,10 @@ private static double[] sparseSOR( * @param a the lower bound on the interval * @param b the upper bound on the interval * @param maxIters the maximum number of iterations + * @param tolerance the error tolerance * @return the root of the function */ - public static double bisection(Function func, double a, double b, int maxIters) { + public static double bisection(Function func, double a, double b, int maxIters, double tolerance) { double c; double fc; int i = 0; @@ -452,7 +453,7 @@ public static double bisection(Function func, double a, double b, int maxIters) fc = func.f(c); // Check for exit conditions. - if (fc == 0 || (b - a) / 2 < DELTA) { + if (fc == 0 || (b - a) / 2 < tolerance) { return c; } else { if (Math.signum(fc) == Math.signum(func.f(a))) { @@ -469,7 +470,7 @@ public static double bisection(Function func, double a, double b, int maxIters) } /** - * Finds root using bisection method with default maximum iterations. + * Finds root using bisection method with default maximum iterations and tolerance. * *

Root is found by repeatedly bisecting the interval and selecting the interval in which the * function changes sign. If no root is found, the simulation will throw an ArithmeticException. @@ -480,6 +481,38 @@ public static double bisection(Function func, double a, double b, int maxIters) * @return the root of the function */ public static double bisection(Function func, double a, double b) { - return bisection(func, a, b, MAX_ITERS); + return bisection(func, a, b, MAX_ITERS, TOLERANCE); + } + + /** + * Finds root using bisection method with default maximum iterations. + * + *

Root is found by repeatedly bisecting the interval and selecting the interval in which the + * function changes sign. If no root is found, the simulation will throw an ArithmeticException. + * + * @param func the function + * @param a the lower bound on the interval + * @param b the upper bound on the interval + * @param tolerance the error tolerance + * @return the root of the function + */ + public static double bisection(Function func, double a, double b, double tolerance) { + return bisection(func, a, b, MAX_ITERS, tolerance); + } + + /** + * Finds root using bisection method with default tolerance. + * + *

Root is found by repeatedly bisecting the interval and selecting the interval in which the + * function changes sign. If no root is found, the simulation will throw an ArithmeticException. + * + * @param func the function + * @param a the lower bound on the interval + * @param b the upper bound on the interval + * @param maxIters the maximum number of iterations + * @return the root of the function + */ + public static double bisection(Function func, double a, double b, int maxIters) { + return bisection(func, a, b, maxIters, TOLERANCE); } } diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index ef80b31a9..c1363cfed 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -20,17 +20,21 @@ import arcade.core.env.lattice.Lattice; import arcade.core.env.component.Component; import arcade.patch.sim.PatchSimulation; +import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_CAPILLARY_RADIUS; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThickness; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThicknesses; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.reversePressures; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateFlows; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressure; @@ -54,6 +58,10 @@ public class PatchComponentGrowth implements Component { private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); + private final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_1; + + private final EdgeType DEFAULT_EDGE_TYPE = EdgeType.ANGIOGENIC; + /** Calculation strategies. */ public enum Calculation { /** Code for upstream calculation strategy. */ @@ -75,14 +83,27 @@ public enum MigrationDirection { BIASED; } - private final double migrationRate; - private final double vegfThreshold; - private final String walkType; - private final double maxLength; + /** Rate of migration. */ + private double migrationRate; + + /** + * Angiogenesis threshold for vegf concentration near SiteNode to initiate + * migration. + */ + private double vegfThreshold; + + /** Direction of migration. */ + private MigrationDirection walkType; + + /** Maximum length of migration. */ + private double maxLength; private int maxEdges; + private int interval; + private double edgeSize; + /** The associated {@link GraphSites} object. */ private PatchComponentSitesGraph sites; @@ -92,19 +113,17 @@ public enum MigrationDirection { private HashMap> angioMap = new HashMap<>(); private ArrayList tempEdges; - private EnumMap offsets; - private final int level = 1; - private final int default_type = 0; // Capillary type - - private final MiniBox specs; + private ArrayList added = new ArrayList<>(); private int numOffsets; + private ArrayList offsetDirections; + private EnumMap offsets; public PatchComponentGrowth(Series series, MiniBox parameters) { // Set loaded parameters. migrationRate = parameters.getDouble("MIGRATION_RATE"); vegfThreshold = parameters.getDouble("VEGF_THRESHOLD"); - walkType = parameters.get("WALK_TYPE"); + walkType = MigrationDirection.valueOf(parameters.get("WALK_TYPE")); maxLength = parameters.getDouble("MAX_LENGTH"); } @@ -125,7 +144,6 @@ public void schedule(Schedule schedule) { @Override public void register(Simulation sim, String layer) { - PatchSimulation patchSim = (PatchSimulation) sim; Component component = sim.getComponent(layer); if (!(component instanceof PatchComponentSitesGraph)) { @@ -135,21 +153,25 @@ public void register(Simulation sim, String layer) { sites = (PatchComponentSitesGraph) component; graph = sites.graph; offsets = sites.graphFactory.getOffsets(); - numOffsets = offsets.keySet().size(); + + offsetDirections = new ArrayList<>(offsets.keySet()); + offsetDirections.remove(EdgeDirection.UNDEFINED); + numOffsets = offsetDirections.size(); + edgeSize = sim.getSeries().ds; maxEdges = (int) Math.floor(maxLength / edgeSize); - } @Override - public void step(final SimState simstate) { - final Simulation sim = (Simulation) simstate; - final Lattice vegfLattice = sim.getLattice("VEGF"); - final MersenneTwisterFast random = simstate.random; + public void step(SimState simstate) { + Simulation sim = (Simulation) simstate; + int tick = (int) simstate.schedule.getTime(); - ArrayList nodesToRemove = new ArrayList<>(); + Lattice vegfLattice = sim.getLattice("VEGF"); - final double tick = (int) simstate.schedule.getTime() + 1; + MersenneTwisterFast random = simstate.random; + + ArrayList nodesToRemove = new ArrayList<>(); LinkedHashSet set = new LinkedHashSet<>(); @@ -178,9 +200,9 @@ public void step(final SimState simstate) { continue; } - ArrayList> vegfList = getVEGFList(vegfLattice, node); + EnumMap> vegfMap = buildDirectionalVEGFMap(vegfLattice, node); - if (averageListArrays(vegfList) > vegfThreshold) { + if (averageDirectionalMap(vegfMap) > vegfThreshold) { angioMap.put(node, new ArrayList<>()); ArrayList skipDirList = new ArrayList(); @@ -198,28 +220,24 @@ public void step(final SimState simstate) { for (Object edge : out) { SiteEdge outEdge = (SiteEdge) edge; skipDirList.add(sites.graphFactory.getDirection(outEdge, outEdge.level)); - } } - ArrayList vegfAverages = getListAverages(vegfList); + EnumMap vegfAverages = getDirectionalAverages(vegfMap); - int newDir; - switch (walkType.toUpperCase()) { - case "RANDOM": - newDir = performRandomWalk(random, node, vegfAverages, tick, skipDirList); + switch (walkType) { + case RANDOM: + node.sproutDir = performRandomWalk(random, node, vegfAverages, tick, skipDirList); break; - case "BIASED": - newDir = performBiasedWalk(random, node, vegfAverages, tick, skipDirList); + case BIASED: + node.sproutDir = performBiasedWalk(random, node, vegfAverages, tick, skipDirList); break; - case "MAX": - newDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); + case DETERMINISTIC: + node.sproutDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); break; default: - LOGGER.warning("invalid walk type: " + walkType + "; using default of MAX."); - newDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); } - node.sproutDir = newDir; } } @@ -228,7 +246,7 @@ public void step(final SimState simstate) { addTemporaryEdges(); for (Map.Entry> entry : angioMap.entrySet()) { - // grab final node in each list and add edge, check for perfusion + // grab node in each list and add edge, check for perfusion SiteNode keyNode = entry.getKey(); if (checkForIgnoredEdges(keyNode)) { @@ -288,11 +306,11 @@ public void step(final SimState simstate) { if (reversed) { calculatePressures(graph); } - calcFlows(graph, sites); - calcStress(graph); + calculateFlows(graph); + calculateStresses(graph); - // maybe try to redo by iterating through list rather than using final node - if (!graph.containsNode(finalNode)) { + // maybe try to redo by iterating through list rather than using node + if (!graph.contains(finalNode)) { // LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); if (targetNode == null) { @@ -356,7 +374,7 @@ public void step(final SimState simstate) { // Otherwise, recalculate calculate stresses. if (!added.isEmpty()) { // LOGGER.info("*****Updating graph.****** Time: " + tick); - updateGraph(graph, sites, added); + updateGraph(graph); } } @@ -382,7 +400,7 @@ private boolean checkForIgnoredEdges(SiteNode node) { return false; } - private boolean checkNodeSkipStatus(SiteNode node, double tick) { + private boolean checkNodeSkipStatus(SiteNode node, int tick) { if (angioMap.keySet().contains(node)) { return true; } @@ -409,14 +427,14 @@ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { if (keyNode == targetNode) { return keyNode; } - if (edgeListContainsNode(angioMap.get(keyNode), targetNode)) { + if (edgeListcontains(angioMap.get(keyNode), targetNode)) { return keyNode; } } return null; } - private boolean edgeListContainsNode(ArrayList edgeList, SiteNode targetNode) { + private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNode) { for (SiteEdge edge : edgeList) { if (edge.getTo() == targetNode) { return true; @@ -449,108 +467,109 @@ private void removeEdgeList(ArrayList edgeList) { } } - private int performRandomWalk(final MersenneTwisterFast random, final SiteNode node, - final ArrayList valList, final double tick, ArrayList skipList) { - int randDir; + private EdgeDirection performRandomWalk(MersenneTwisterFast random, SiteNode node, + EnumMap valList, int tick, ArrayList skipList) { + EdgeDirection randDir; do { - randDir = random.nextInt(offsets.keySet().size()); + randDir = offsetDirections.get(random.nextInt(numOffsets)); } while (!skipList.contains(randDir)); return randDir; } - private int performBiasedWalk(final MersenneTwisterFast random, final SiteNode node, - final ArrayList valList, final double tick, ArrayList skipList) { - for (final EdgeDirection dir : skipList) { - valList.set(dir, 0.0); + private EdgeDirection performBiasedWalk(MersenneTwisterFast random, SiteNode node, + EnumMap valList, int tick, ArrayList skipList) { + for (EdgeDirection dir : skipList) { + valList.put(dir, 0.0); } - final ArrayList seqList = normalizeSequentialList(valList); - final double val = random.nextDouble(); - for (int i = 0; i < numOffsets; i++) { - if (val < seqList.get(i)) { - return i; + EnumMap seqMap = normalizeDirectionalMap(valList); + double val = random.nextDouble(); + for (EdgeDirection dir : offsetDirections) { + if (val < seqMap.get(dir)) { + return dir; } } - return numOffsets - 1; + // otherwise return last direction + return offsetDirections.get(offsetDirections.size() - 1); } - private int performDeterministicWalk(final MersenneTwisterFast random, final SiteNode node, - final ArrayList valList, final double tick, ArrayList skipList) { - for (final int dir : skipList) { - valList.set(dir, 0.0); + private EdgeDirection performDeterministicWalk(MersenneTwisterFast random, SiteNode node, + EnumMap valList, int tick, ArrayList skipList) { + for (EdgeDirection dir : skipList) { + valList.put(dir, 0.0); } - final int maxDir = getMaxKey(valList); + EdgeDirection maxDir = getMaxKey(valList); return maxDir; } - private ArrayList> getVEGFList(final Lattice lattice, final SiteNode node) { + private EnumMap> buildDirectionalVEGFMap(Lattice lattice, SiteNode node) { double[][][] field = lattice.getField(); - final ArrayList> vegfList = new ArrayList<>(); - for (int dir = 0; dir < numOffsets; dir++) { - SiteNode proposed = sites.offsetNode(node, dir, level); - if (sites.checkNode(proposed)) { - final ArrayList span = sites.getSpan(node, proposed); - vegfList.add(dir, new ArrayList<>()); - for (final int[] coords : span) { - int i = coords[0]; - int j = coords[1]; - int k = coords[2]; - vegfList.get(dir).add(field[k][i][j]); + EnumMap> vegfMap = new EnumMap<>(EdgeDirection.class); + for (EdgeDirection dir : offsetDirections) { + SiteNode proposed = sites.graphFactory.offsetNode(node, dir, DEFAULT_EDGE_LEVEL); + if (sites.graphFactory.checkNode(proposed)) { + ArrayList span = sites.getSpan(node, proposed); + vegfMap.put(dir, new ArrayList<>()); + for (CoordinateXYZ coordinate : span) { + int i = coordinate.x; + int j = coordinate.y; + int k = coordinate.z; + vegfMap.get(dir).add(field[k][i][j]); } } else { - vegfList.add(dir, new ArrayList<>(0)); + vegfMap.put(dir, new ArrayList<>(0)); } } - return vegfList; + return vegfMap; } - private int getMaxKey(final ArrayList map) { - int maxDir = 0; - double maxVal = 0; - for (int i = 0; i < numOffsets; i++) { - if (map.get(i) > maxVal) { - maxDir = i; - maxVal = map.get(i); + private EnumMap getDirectionalAverages(EnumMap> map) { + EnumMap averageMap = new EnumMap<>(EdgeDirection.class); + for (EdgeDirection dir : offsetDirections) { + double sum = 0; + for (double value : map.get(dir)) { + sum += value; } + averageMap.put(dir, sum / map.get(dir).size()); } - return maxDir; + return averageMap; } - private ArrayList getListAverages(final ArrayList> map) { - final ArrayList averageList = new ArrayList<>(); - for (int i = 0; i < numOffsets; i++) { - double sum = 0; - for (final double value : map.get(i)) { - sum += value; + private EdgeDirection getMaxKey(EnumMap map) { + EdgeDirection maxDir = EdgeDirection.UNDEFINED; + double maxVal = 0; + for (EdgeDirection dir : offsetDirections) { + if (map.get(dir) > maxVal) { + maxDir = dir; + maxVal = map.get(dir); } - averageList.add(i, sum / map.get(i).size()); } - return averageList; + return maxDir; } - private ArrayList normalizeSequentialList(final ArrayList map) { - final ArrayList normalizedList = new ArrayList<>(); - final double norm = sumList(map); + private EnumMap normalizeDirectionalMap(EnumMap map) { + EnumMap normalizedMap = new EnumMap<>(EdgeDirection.class); + double norm = sumMap(map); double prev = 0; - for (int i = 0; i < numOffsets; i++) { - normalizedList.add(i, prev + map.get(i) / norm); - prev = prev + map.get(i) / norm; + for (EdgeDirection dir : offsetDirections) { + prev = prev + map.get(dir) / norm; + normalizedMap.put(dir, prev); } - return normalizedList; + return normalizedMap; } - private double sumList(final ArrayList map) { + private double sumMap(EnumMap map) { double sum = 0; - for (int i = 0; i < numOffsets; i++) { - sum += map.get(i); + for (EdgeDirection dir : offsetDirections) { + sum += map.get(dir); } return sum; } - private double averageListArrays(final ArrayList> map) { + private double averageDirectionalMap(EnumMap> map) { double sum = 0; int count = 0; - for (int i = 0; i < numOffsets; i++) { - for (final double value : map.get(i)) { + for (EdgeDirection dir : offsetDirections) { + for (double value : map.get(dir)) { sum += value; count++; } @@ -558,15 +577,15 @@ private double averageListArrays(final ArrayList> map) { return sum / count; } - private void addEdgeList(final ArrayList list) { + private void addEdgeList(ArrayList list) { addEdgeList(list, false); } - private void addEdgeList(final ArrayList list, boolean updateProperties) { - addEdgeList(list, updateProperties, default_type); + private void addEdgeList(ArrayList list, boolean updateProperties) { + addEdgeList(list, updateProperties, DEFAULT_EDGE_TYPE); } - private void addAngioEdges(ArrayList list, SiteNode start, SiteNode end, double tick, Calculation calc) { + private void addAngioEdges(ArrayList list, SiteNode start, SiteNode end, int tick, Calculation calc) { ArrayList added = new ArrayList<>(); @@ -577,7 +596,7 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en return; } - Graph tempG = sites.graphFactory.newGraph(); + Graph tempG = new Graph(); for (SiteEdge e : list) { tempG.addEdge(e); } @@ -585,7 +604,7 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en path(tempG, start, end); SiteNode n = end; while (n != start) { - added.add(new SiteEdge(n.prev n, 0, level, false)); + added.add(new SiteEdge(n.prev, n, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL)); n = n.prev; if (n != start) { if (n == null) { @@ -604,24 +623,15 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en } for (SiteEdge edge : added) { - edge.isAngiogenic = true; - edge.radius = (otherRadius > CAP_RADIUS) ? CAP_RADIUS + edge.radius = (otherRadius > CAPILLARY_RADIUS) ? CAPILLARY_RADIUS : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); - edge.wall = calcThickness(edge.radius); + edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); - edge.length = sites.getLength(edge, 1); + edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); // update later (updateSpans method should take care of most of these, need to // check for perfusion first) edge.isPerfused = true; - edge.fraction = new double[sites.NUM_MOLECULES]; - edge.transport = new double[sites.NUM_MOLECULES]; - for (int[] coor : edge.span) { // i don't think i need this? - int i = coor[0]; - int j = coor[1]; - int k = coor[2]; - sites.sites[k][i][j]++; - } } if (start.pressure * end.pressure <= 0) { @@ -653,19 +663,19 @@ private double calculateEvenSplitRadius(SiteEdge edge) { double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); double newRadius = Solver.bisection((double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), 1E-6, - 5 * CAP_RADIUS_MAX, 1E-6); + 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); // LOGGER.info("splitting radius, for checking if it happens directly before // bisection failing"); // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * - // calculateLocalFlow(r, length, deltaP), 2), 0, CAP_RADIUS_MAX); + // calculateLocalFlow(r, length, deltaP), 2), 0, MAXIMUM_CAPILLARY_RADIUS); // double newRadius = Solver.boundedGradientDescent((double r) -> Math.pow(flow // - 2 * calculateLocalFlow(r, length, deltaP), 2), radius, 1E-17, - // CAP_RADIUS_MIN, CAP_RADIUS_MAX); + // MINIMUM_CAPILLARY_RADIUS, MAXIMUM_CAPILLARY_RADIUS); return newRadius; } private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteNode end) { - updateGraph(graph, sites); + updateGraph(graph); ArrayList oldRadii = new ArrayList<>(); ArrayList updatedEdges = new ArrayList<>(); Boolean failed = false; @@ -680,12 +690,10 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } Double deltaP = start.pressure - end.pressure; - Double newFlow = calculateLocalFlow(CAP_RADIUS, addedEdges, deltaP); + Double newFlow = calculateLocalFlow(CAPILLARY_RADIUS, addedEdges, deltaP); - ArrayList arteries = sites.arteries; - Integer num_arteries = 0; - ArrayList veins = sites.veins; - Integer num_veins = 0; + ArrayList arteries = sites.graphFactory.arteries; + ArrayList veins = sites.graphFactory.veins; ArrayList> pathsArteries = new ArrayList<>(); for (Root artery : arteries) { @@ -694,10 +702,10 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, continue; } pathsArteries.add(path); - num_arteries++; } - Double arteryFlow = newFlow / num_arteries; + Double arteryFlow = newFlow / arteries.size(); + for (ArrayList path : pathsArteries) { if (!path.get(0).getFrom().isRoot) { throw new ArithmeticException("Root is not the start of the path"); @@ -732,10 +740,9 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, continue; } pathsVeins.add(path); - num_veins++; } - Double veinFlow = newFlow / num_veins; + Double veinFlow = newFlow / veins.size(); for (ArrayList path : pathsVeins) { if (!path.get(0).getFrom().isRoot) { @@ -754,7 +761,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } } - if (num_arteries == 0 || num_veins == 0) { + if (arteries.size() == 0 || veins.size() == 0) { LOGGER.info("No arteries or veins found, not updating roots"); failed = true; } @@ -767,7 +774,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { - updateGraph(graph, sites); + updateGraph(graph); Bag edges = graph.getEdgesOut(start); if (edges == null) { @@ -784,16 +791,16 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; Integer nonAngioIndex = angioIndex ^ 1; double deltaP = start.pressure - end.pressure; - // double deltaP = ((SiteNode) graph.lookupNode(start)).pressure - ((SiteNode) - // graph.lookupNode(end)).pressure; - Double divertedFlow = calculateLocalFlow(CAP_RADIUS, ignoredEdges, deltaP); + // double deltaP = ((SiteNode) graph.lookup(start)).pressure - ((SiteNode) + // graph.lookup(end)).pressure; + Double divertedFlow = calculateLocalFlow(CAPILLARY_RADIUS, ignoredEdges, deltaP); Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { return; } if (intersection != null) { if (intersection.isRoot) { - updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, + updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, false, ignoredEdges); return; // updateRadiusToRoot((SiteEdge) edges.get(angioIndex), intersection, @@ -818,11 +825,11 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN // TODO: check to add flow to radius with new flow after changes to other // potential edge, need to do this math out? // this should only work for single vein simulations - SiteNode boundary = sites.veins.get(0).node; + SiteNode boundary = sites.graphFactory.veins.get(0).node; path(graph, start, boundary); - if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > CAP_RADIUS_MIN) { + if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { // LOGGER.info("Calculating additional flow to vein"); - updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.veins.get(0).node, divertedFlow, false, + updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, false, ignoredEdges); } else { return; @@ -884,16 +891,17 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); Function f = (double r) -> originalFlow + sign * flow - - calculateLocalFlow(r, edge.length, edge.getFrom().pressure - calculatePressure(r, edge.type)); + - calculateLocalFlow(r, edge.length, + edge.getFrom().pressure - calculatePressure(r, edge.type.category)); - double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius, 1E-6); + double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); if (newRadius == .5 * originalRadius || newRadius == Double.NaN) { return -1; } edge.radius = newRadius; - edge.getTo().pressure = calculatePressure(newRadius, edge.type); + edge.getTo().pressure = calculatePressure(newRadius, edge.type.category); return 0; } @@ -904,21 +912,21 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); Function f = (double r) -> originalFlow + sign * flow - - calculateLocalFlow(r, edge.length, calculatePressure(r, edge.type) - edge.getTo().pressure); + - calculateLocalFlow(r, edge.length, calculatePressure(r, edge.type.category) - edge.getTo().pressure); - double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius, 1E-6); + double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); if (newRadius == .5 * originalRadius || newRadius == Double.NaN || newRadius == 1.5 * originalRadius) { return -1; } edge.radius = newRadius; - edge.getFrom().pressure = calculatePressure(newRadius, edge.type); + edge.getFrom().pressure = calculatePressure(newRadius, edge.type.category); return 0; } private void updateRadiusToRoot(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, ArrayList ignored) { - ArrayList veins = sites.veins; + ArrayList veins = sites.graphFactory.veins; ArrayList oldRadii = new ArrayList<>(); for (Root vein : veins) { ArrayList path = getPath(graph, edge.getTo(), vein.node); @@ -960,46 +968,24 @@ private void addEdgeList(ArrayList list, boolean updateProperties, Edg } private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node, int tick) { - SiteNode proposed = sites.graphFactory.offsetNode(node, dir, level); + SiteNode proposed = sites.graphFactory.offsetNode(node, direction, DEFAULT_EDGE_LEVEL); proposed.lastUpdate = tick; - if (sites.checkNode(proposed) && graph.getDegree(node) < 3) { + if (sites.graphFactory.checkNode(proposed) && graph.getDegree(node) < 3) { SiteEdge edge; - if (graph.containsNode(proposed)) { + if (graph.contains(proposed)) { if (graph.getDegree(proposed) > 2 || graph.getEdgesOut(proposed) == null || graph.getEdgesIn(proposed) == null) { return null; } - SiteNode existing = (SiteNode) graph.lookupNode(proposed); - edge = new SiteEdge(node, existing, default_type, level, false); + SiteNode existing = (SiteNode) graph.lookup(proposed); + edge = new SiteEdge(node, existing, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); edge.isAnastomotic = true; return edge; } - edge = new SiteEdge(node, proposed, default_type, level, false); + edge = new SiteEdge(node, proposed, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); return edge; } return null; } - - /** - * {@inheritDoc} - *

- * The JSON is formatted as: - * - *

-     *     {
-     *         "type": "GROWTH",
-     *         "interval": interval,
-     *         "specs" : {
-     *             "SPEC_NAME": spec value,
-     *             "SPEC_NAME": spec value,
-     *             ...
-     *         }
-     *     }
-     * 
- */ - public String toJSON() { - String format = "{ " + "\"type\": \"GROWTH\", " + "\"interval\": %d, " + "\"specs\": %s " + "}"; - return String.format(format, interval, specs.toJSON()); - } } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 82ee74574..91e5dda3e 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -15,6 +15,7 @@ import arcade.core.util.Solver.Function; import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.sim.PatchSeries; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeTag; import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; @@ -424,6 +425,15 @@ public static class SiteNode extends Node { /** Tick for when the node was added to the graph. */ int addTime; + /** Direction of the angiogenic sprout. */ + EdgeDirection sproutDir; + + /** + * {@ True} if the angiogenic sprrout is anastomotic/perfused, {@code false} + * otherwise. + */ + boolean anastomosis; + /** Parent node. */ SiteNode prev; @@ -494,9 +504,6 @@ public static class SiteEdge extends Edge { /** {@code true} if edge is ignored, {@code false} otherwise. */ boolean isIgnored; - /** {@code true} if edge is angiogenic, {@code false} otherwise. */ - boolean isAngiogenic; - /** {@code true} if edge is anastomotic, {@code false} otherwise. */ boolean isAnastomotic; @@ -547,8 +554,8 @@ public static class SiteEdge extends Edge { * @param type the edge type * @param level the graph resolution level */ - SiteEdge(Node from, Node to, EdgeType type, EdgeLevel level) { - super(from, to); + SiteEdge(SiteNode from, SiteNode to, EdgeType type, EdgeLevel level) { + super(from, to, type == EdgeType.ANGIOGENIC); this.type = type; this.level = level; isVisited = false; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java index a57eb4020..fd2608bfa 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java @@ -59,8 +59,12 @@ public enum EdgeType { VEIN(EdgeCategory.VEIN), /** Code for venule edge type. */ - VENULE(EdgeCategory.VEIN); + VENULE(EdgeCategory.VEIN), + /** Code for angiogenic edge type. */ + ANGIOGENIC(EdgeCategory.CAPILLARY); + + /** Code for capillary edge type. */ /** Edge category corresponding to the edge type. */ final EdgeCategory category; @@ -191,6 +195,12 @@ enum EdgeMotif { /** Width of the array (y direction). */ final int latticeWidth; + /** List of pointers to artery node and edge objects. */ + ArrayList arteries; + + /** List of pointers to vein node and edge objects. */ + ArrayList veins; + /** * Creates a factory for making {@link Graph} sites. * @@ -624,8 +634,8 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) leaves.addAll(bag); } - ArrayList arteries = new ArrayList<>(); - ArrayList veins = new ArrayList<>(); + arteries = new ArrayList<>(); + veins = new ArrayList<>(); boolean hasArtery = false; boolean hasVein = false; @@ -663,19 +673,19 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) addMotifs(graph, leaves2, EdgeLevel.LEVEL_1, EdgeMotif.SINGLE, random); // Calculate radii, pressure, and shears. - updateRootGraph(graph, arteries, veins, EdgeLevel.LEVEL_1, random); + updateRootGraph(graph,EdgeLevel.LEVEL_1, random); // Iterative remodeling. int iter = 0; double frac = 1.0; while (frac > REMODELING_FRACTION && iter < MAX_ITERATIONS) { frac = remodelRootGraph(graph, EdgeLevel.LEVEL_1, random); - updateRootGraph(graph, arteries, veins, EdgeLevel.LEVEL_1, random); + updateRootGraph(graph, EdgeLevel.LEVEL_1, random); iter++; } // Prune network for perfused segments and recalculate properties. - refineRootGraph(graph, arteries, veins); + refineRootGraph(graph); // Subdivide growth sites and add new motifs. Bag midpoints = subdivideRootGraph(graph, EdgeLevel.LEVEL_1); @@ -684,10 +694,10 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) addMotifs(graph, midpoints2, EdgeLevel.LEVEL_2, EdgeMotif.SINGLE, random); // Calculate radii, pressure, and shears. - updateRootGraph(graph, arteries, veins, EdgeLevel.LEVEL_2, random); + updateRootGraph(graph, EdgeLevel.LEVEL_2, random); // Prune network for perfused segments and recalculate properties. - refineRootGraph(graph, arteries, veins); + refineRootGraph(graph); return graph; } @@ -703,8 +713,6 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) */ private void updateRootGraph( Graph graph, - ArrayList arteries, - ArrayList veins, EdgeLevel level, MersenneTwisterFast random) { ArrayList list; @@ -833,7 +841,7 @@ private void updateRootGraph( * @param arteries the list of artery edges * @param veins the list of vein edges */ - private void refineRootGraph(Graph graph, ArrayList arteries, ArrayList veins) { + private void refineRootGraph(Graph graph) { // Reverse edges that are veins and venules. ArrayList reverse = getEdgeByType(graph, new EdgeType[] { EdgeType.VEIN, EdgeType.VENULE }); for (SiteEdge edge : reverse) { diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index ae60b837a..8146fd790 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -1,14 +1,23 @@ package arcade.patch.env.component; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import sim.util.Bag; import ec.util.MersenneTwisterFast; import arcade.core.util.Graph; +import arcade.core.util.Graph.Edge; import arcade.core.util.Graph.Strategy; import arcade.core.util.Matrix; import arcade.core.util.Solver; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeCategory; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; + import static arcade.core.util.Graph.Edge; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; @@ -591,11 +600,21 @@ static void calculateFlows(Graph graph) { static void calculateThicknesses(Graph graph) { for (Object obj : graph.getAllEdges()) { SiteEdge edge = (SiteEdge) obj; - double d = 2 * edge.radius; - edge.wall = d * (0.267 - 0.084 * Math.log10(d)); + edge.wall = calculateThickness(edge); } } + /** + * Calculates the thickness of an edge. + * + * @param edge the edge object + * @return the thickness of the edge + */ + static double calculateThickness(SiteEdge edge) { + double d = 2 * edge.radius; + return d * (0.267 - 0.084 * Math.log10(d)); + } + /** * Gets in degree for edge in given calculation direction. * @@ -948,6 +967,25 @@ static void path(Graph graph, SiteNode start, SiteNode end) { } } + static ArrayList getPath(Graph graph, SiteNode start, SiteNode end){ + path(graph, start, end); + ArrayList path = new ArrayList<>(); + SiteNode node = end; + while (node != null && node != start) { + Bag b = graph.getEdgesIn(node); + if (b.numObjs == 1) { path.add((SiteEdge)b.objs[0]); } + else if (b.numObjs == 2) { + SiteEdge edgeA = ((SiteEdge)b.objs[0]); + SiteEdge edgeB = ((SiteEdge)b.objs[1]); + if (edgeA.getFrom() == node.prev) { path.add(edgeA); } + else { path.add(edgeB); } + } + node = node.prev; + } + Collections.reverse(path); + return path; + } + /** * Traverses through graph to find perfused paths. * From 1748fcac104f14a7ad434ca544e32e8144609f46 Mon Sep 17 00:00:00 2001 From: cainja Date: Sat, 7 Jun 2025 17:21:11 -0700 Subject: [PATCH 04/56] spotless --- src/arcade/core/util/Graph.java | 88 +++---- src/arcade/core/util/Solver.java | 3 +- .../env/component/PatchComponentGrowth.java | 237 +++++++++++------ .../component/PatchComponentSitesGraph.java | 118 ++++----- .../PatchComponentSitesGraphFactory.java | 241 +++++++++--------- .../PatchComponentSitesGraphFactoryRect.java | 237 ++++++++--------- .../PatchComponentSitesGraphFactoryTri.java | 189 +++++++------- .../PatchComponentSitesGraphUtilities.java | 187 +++++++------- 8 files changed, 666 insertions(+), 634 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index bf6214b0c..ca950cd7b 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -14,9 +14,7 @@ /** * Container class for directed graph using nodes as hashes. * - *

- * {@code Edge} objects represent edges in the graph and {@code Node} objects - * represent nodes in + *

{@code Edge} objects represent edges in the graph and {@code Node} objects represent nodes in * the graph. Nodes may have more than one edge in or out. */ public final class Graph { @@ -97,11 +95,11 @@ public boolean contains(Edge edge) { return checkEdge(edge); } - public Node lookup(int x, int y, int z){ + public Node lookup(int x, int y, int z) { return nodes.get("(" + x + "," + y + "," + z + ")"); } - public Node lookup(Node node){ + public Node lookup(Node node) { return nodes.get(node.toString()); } @@ -117,7 +115,7 @@ public Bag getAllEdges() { /** * Gets all edges in the graph based on the given node and category. * - * @param node the node to get edges from + * @param node the node to get edges from * @param strategy the category of edges to get * @return a bag containing the edges */ @@ -179,7 +177,7 @@ public int getDegree(Node node) { * Checks if the graph has an edge between the given nodes. * * @param from the node the edge points from - * @param to the node the edge points to + * @param to the node the edge points to * @return {@code true} if edge exists, {@code false} otherwise */ public boolean hasEdge(Node from, Node to) { @@ -211,8 +209,7 @@ public interface GraphFilter { /** * Filters this graph for edges and copies them to the given graph object. * - *

- * Notes that the links in the subgraph are not correct. + *

Notes that the links in the subgraph are not correct. * * @param g the graph to add filtered edges to * @param f the edge filter @@ -236,12 +233,13 @@ public void getSubgraph(Graph g, GraphFilter f) { private Set retrieveNodes() { Set sOut = nodeToOutBag.keySet(); Set sIn = nodeToInBag.keySet(); - Set set = new LinkedHashSet() { - { - addAll(sOut); - addAll(sIn); - } - }; + Set set = + new LinkedHashSet() { + { + addAll(sOut); + addAll(sIn); + } + }; return set; } @@ -278,7 +276,7 @@ public void mergeNodes() { * Adds edge to graph based on nodes. * * @param from the node to add as the from node. - * @param to the node to add as the to node. + * @param to the node to add as the to node. */ public void addEdge(Node from, Node to) { addEdge(new Edge(from, to)); @@ -297,20 +295,17 @@ public void addEdge(Edge edge) { addNodes(edge); } - - private void addNodes(Edge edge){ + private void addNodes(Edge edge) { Node from = edge.getFrom(); Node to = edge.getTo(); - if (!nodes.containsKey(from.toString())){ + if (!nodes.containsKey(from.toString())) { nodes.put(from.toString(), from); - } - else { + } else { from = nodes.get(from.toString()); } - if (!nodes.containsKey(to.toString())){ + if (!nodes.containsKey(to.toString())) { nodes.put(to.toString(), to); - } - else { + } else { to = nodes.get(to.toString()); } } @@ -475,12 +470,11 @@ public Node findUpstreamIntersection(Edge edge1, Edge edge2) { } /** - * Find the first node where two edges intersect based on a calulcation strategy - * (upstream or + * Find the first node where two edges intersect based on a calulcation strategy (upstream or * downstream). * - * @param edge1 first edge to start from - * @param edge2 second edge to start from + * @param edge1 first edge to start from + * @param edge2 second edge to start from * @param strategy the direction to search * @return the intersection node or null if no intersection */ @@ -497,11 +491,10 @@ private Node findIntersection(Edge edge1, Edge edge2, Strategy strategy) { } /** - * Get all nodes connected to the given node based on a calculation strategy - * (e.g. upstream or + * Get all nodes connected to the given node based on a calculation strategy (e.g. upstream or * downstream). * - * @param node the node to start from + * @param node the node to start from * @param strategy the direction to search * @return a bag of connected nodes */ @@ -534,12 +527,11 @@ private Bag getConnectedNodes(Node node, Strategy strategy) { } /** - * Breadth first search from node according to strategy for a subset of target - * nodes. + * Breadth first search from node according to strategy for a subset of target nodes. * - * @param node the node to start from + * @param node the node to start from * @param targetNodes the bag of potential intersection nodes - * @param strategy the direction to search + * @param strategy the direction to search * @return the target node or null if not found */ private Node breadthFirstSearch(Node node, Bag targetNodes, Strategy strategy) { @@ -625,8 +617,7 @@ public String toString() { /** * Nested class representing a graph node. * - *

- * The node tracks its corresponding position in the lattice. + *

The node tracks its corresponding position in the lattice. */ public static class Node implements Comparable { /** Coordinate in x direction. */ @@ -682,9 +673,8 @@ public int getZ() { * Compares a node to this node. * * @param node the node to compare - * @return zero if the x and y coordinates are equal, otherwise the result of - * integer - * comparison for x and y + * @return zero if the x and y coordinates are equal, otherwise the result of integer + * comparison for x and y */ public int compareTo(Node node) { int xComp = Integer.compare(x, node.getX()); @@ -707,8 +697,7 @@ public Node duplicate() { } /** - * Updates the position of this {@code Node} with coordinate from given - * {@code Node}. + * Updates the position of this {@code Node} with coordinate from given {@code Node}. * * @param node the {@code Node} with coordinates to update with */ @@ -754,9 +743,7 @@ public String toString() { /** * Nested class representing a graph edge. * - *

- * The edge tracks its corresponding nodes as well as the edges into the FROM - * node and out of + *

The edge tracks its corresponding nodes as well as the edges into the FROM node and out of * the TO node. */ public static class Edge { @@ -776,7 +763,7 @@ public static class Edge { * Creates an {@code Edge} between two {@link Node} objects. * * @param from the node the edge is from - * @param to the node the edge is to + * @param to the node the edge is to */ public Edge(Node from, Node to) { this.from = from.duplicate(); @@ -793,8 +780,7 @@ public Edge(Node from, Node to, boolean duplicate) { } /** - * Gets the node the edge points to based on the calculation strategy (e.g. - * upstream or + * Gets the node the edge points to based on the calculation strategy (e.g. upstream or * downstream). * * @param strategy the calculation strategy @@ -841,8 +827,7 @@ public void setFrom(Node from) { } /** - * Gets list of edges based on calculation strategy (e.g. upstream or - * downstream). + * Gets list of edges based on calculation strategy (e.g. upstream or downstream). * * @param strategy the calculation strategy * @return the list of edges @@ -901,8 +886,7 @@ public String toString() { * Checks if two nodes are equal based on to and from nodes. * * @param obj the object to check - * @return {@code true} if coordinates of both nodes match, {@code false} - * otherwise + * @return {@code true} if coordinates of both nodes match, {@code false} otherwise */ public boolean equals(Object obj) { if (obj instanceof Edge) { diff --git a/src/arcade/core/util/Solver.java b/src/arcade/core/util/Solver.java index d8deb4152..a0bd6b525 100644 --- a/src/arcade/core/util/Solver.java +++ b/src/arcade/core/util/Solver.java @@ -431,7 +431,8 @@ private static double[] sparseSOR( * @param tolerance the error tolerance * @return the root of the function */ - public static double bisection(Function func, double a, double b, int maxIters, double tolerance) { + public static double bisection( + Function func, double a, double b, int maxIters, double tolerance) { double c; double fc; int i = 0; diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index c1363cfed..801b86c6f 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -1,59 +1,54 @@ package arcade.patch.env.component; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; -import java.util.EnumMap; import java.util.logging.Logger; -import ec.util.MersenneTwisterFast; -import sim.util.Bag; import sim.engine.Schedule; import sim.engine.SimState; -import arcade.core.sim.Simulation; +import sim.util.Bag; +import ec.util.MersenneTwisterFast; +import arcade.core.env.component.Component; +import arcade.core.env.lattice.Lattice; import arcade.core.sim.Series; +import arcade.core.sim.Simulation; import arcade.core.util.Graph; import arcade.core.util.MiniBox; import arcade.core.util.Solver; import arcade.core.util.Solver.Function; -import arcade.core.env.location.Location; -import arcade.core.env.lattice.Lattice; -import arcade.core.env.component.Component; -import arcade.patch.sim.PatchSimulation; -import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; -import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; +import arcade.patch.env.location.CoordinateXYZ; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.CAPILLARY_RADIUS; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_CAPILLARY_RADIUS; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThickness; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThicknesses; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.reversePressures; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateFlows; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateLocalFlow; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressure; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressures; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateStresses; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateLocalFlow; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThickness; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.reversePressures; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.util.PatchEnums.Ordering; /** * Implementation of {@link Component} for degrading graph edges. - *

- * This component can only be used with {@link GraphSites}. The - * component is stepped every {@code DEGRADATION_INTERVAL} ticks. wall thickness - * of edges that are adjacent to a location with cancerous cells is decreased - * ({@code DEGRADATION_RATE}). Edges that are below a minimum wall thickness and - * have a shear stress below the shear threshold ({@code SHEAR_THRESHOLD}) are - * removed from the graph. At the end of a step, if no edges have been removed - * from the graph, then only the stresses in the graph are recalculated. - * Otherwise, all hemodynamic properties are recalculated. + * + *

This component can only be used with {@link GraphSites}. The component is stepped every {@code + * DEGRADATION_INTERVAL} ticks. wall thickness of edges that are adjacent to a location with + * cancerous cells is decreased ({@code DEGRADATION_RATE}). Edges that are below a minimum wall + * thickness and have a shear stress below the shear threshold ({@code SHEAR_THRESHOLD}) are removed + * from the graph. At the end of a step, if no edges have been removed from the graph, then only the + * stresses in the graph are recalculated. Otherwise, all hemodynamic properties are recalculated. */ public class PatchComponentGrowth implements Component { private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); @@ -86,10 +81,7 @@ public enum MigrationDirection { /** Rate of migration. */ private double migrationRate; - /** - * Angiogenesis threshold for vegf concentration near SiteNode to initiate - * migration. - */ + /** Angiogenesis threshold for vegf concentration near SiteNode to initiate migration. */ private double vegfThreshold; /** Direction of migration. */ @@ -185,11 +177,13 @@ public void step(SimState simstate) { from.id = -1; to.id = -1; - if ((graph.getDegree(from) < 3) && !from.isRoot + if ((graph.getDegree(from) < 3) + && !from.isRoot && !(graph.getInDegree(from) == 0 && graph.getOutDegree(from) == 1)) { set.add(from); } - if ((graph.getDegree(to) < 3) && !to.isRoot + if ((graph.getDegree(to) < 3) + && !to.isRoot && !(graph.getInDegree(to) == 1 && graph.getOutDegree(to) == 0)) { set.add(to); } @@ -200,7 +194,8 @@ public void step(SimState simstate) { continue; } - EnumMap> vegfMap = buildDirectionalVEGFMap(vegfLattice, node); + EnumMap> vegfMap = + buildDirectionalVEGFMap(vegfLattice, node); if (averageDirectionalMap(vegfMap) > vegfThreshold) { angioMap.put(node, new ArrayList<>()); @@ -212,8 +207,8 @@ public void step(SimState simstate) { if (in != null) { for (Object edge : in) { SiteEdge inEdge = (SiteEdge) edge; - skipDirList.add(sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); - + skipDirList.add( + sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); } } if (out != null) { @@ -227,16 +222,22 @@ public void step(SimState simstate) { switch (walkType) { case RANDOM: - node.sproutDir = performRandomWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performRandomWalk(random, node, vegfAverages, tick, skipDirList); break; case BIASED: - node.sproutDir = performBiasedWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performBiasedWalk(random, node, vegfAverages, tick, skipDirList); break; case DETERMINISTIC: - node.sproutDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performDeterministicWalk( + random, node, vegfAverages, tick, skipDirList); break; default: - node.sproutDir = performDeterministicWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performDeterministicWalk( + random, node, vegfAverages, tick, skipDirList); } } } @@ -337,12 +338,14 @@ public void step(SimState simstate) { } else { if (sproutNode.pressure == 0) { if (graph.getEdgesOut(sproutNode) != null) { - sproutNode = ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); + sproutNode = + ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); } } if (finalNode.pressure == 0) { if (graph.getEdgesOut(finalNode) != null) { - finalNode = ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); + finalNode = + ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); } } // path(graph, finalNode, sproutNode); @@ -362,7 +365,8 @@ public void step(SimState simstate) { // LOGGER.info("Pressure is 0, skipping"); continue; } - addAngioEdges(angioMap.get(sproutNode), init, fin, tick, Calculation.COMPENSATE); + addAngioEdges( + angioMap.get(sproutNode), init, fin, tick, Calculation.COMPENSATE); } } } @@ -441,7 +445,6 @@ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNo } } return false; - } private void addTemporaryEdges() { @@ -467,8 +470,12 @@ private void removeEdgeList(ArrayList edgeList) { } } - private EdgeDirection performRandomWalk(MersenneTwisterFast random, SiteNode node, - EnumMap valList, int tick, ArrayList skipList) { + private EdgeDirection performRandomWalk( + MersenneTwisterFast random, + SiteNode node, + EnumMap valList, + int tick, + ArrayList skipList) { EdgeDirection randDir; do { randDir = offsetDirections.get(random.nextInt(numOffsets)); @@ -476,8 +483,12 @@ private EdgeDirection performRandomWalk(MersenneTwisterFast random, SiteNode nod return randDir; } - private EdgeDirection performBiasedWalk(MersenneTwisterFast random, SiteNode node, - EnumMap valList, int tick, ArrayList skipList) { + private EdgeDirection performBiasedWalk( + MersenneTwisterFast random, + SiteNode node, + EnumMap valList, + int tick, + ArrayList skipList) { for (EdgeDirection dir : skipList) { valList.put(dir, 0.0); } @@ -492,8 +503,12 @@ private EdgeDirection performBiasedWalk(MersenneTwisterFast random, SiteNode nod return offsetDirections.get(offsetDirections.size() - 1); } - private EdgeDirection performDeterministicWalk(MersenneTwisterFast random, SiteNode node, - EnumMap valList, int tick, ArrayList skipList) { + private EdgeDirection performDeterministicWalk( + MersenneTwisterFast random, + SiteNode node, + EnumMap valList, + int tick, + ArrayList skipList) { for (EdgeDirection dir : skipList) { valList.put(dir, 0.0); } @@ -501,7 +516,8 @@ private EdgeDirection performDeterministicWalk(MersenneTwisterFast random, SiteN return maxDir; } - private EnumMap> buildDirectionalVEGFMap(Lattice lattice, SiteNode node) { + private EnumMap> buildDirectionalVEGFMap( + Lattice lattice, SiteNode node) { double[][][] field = lattice.getField(); EnumMap> vegfMap = new EnumMap<>(EdgeDirection.class); for (EdgeDirection dir : offsetDirections) { @@ -522,7 +538,8 @@ private EnumMap> buildDirectionalVEGFMap(Lattic return vegfMap; } - private EnumMap getDirectionalAverages(EnumMap> map) { + private EnumMap getDirectionalAverages( + EnumMap> map) { EnumMap averageMap = new EnumMap<>(EdgeDirection.class); for (EdgeDirection dir : offsetDirections) { double sum = 0; @@ -546,7 +563,8 @@ private EdgeDirection getMaxKey(EnumMap map) { return maxDir; } - private EnumMap normalizeDirectionalMap(EnumMap map) { + private EnumMap normalizeDirectionalMap( + EnumMap map) { EnumMap normalizedMap = new EnumMap<>(EdgeDirection.class); double norm = sumMap(map); double prev = 0; @@ -585,7 +603,8 @@ private void addEdgeList(ArrayList list, boolean updateProperties) { addEdgeList(list, updateProperties, DEFAULT_EDGE_TYPE); } - private void addAngioEdges(ArrayList list, SiteNode start, SiteNode end, int tick, Calculation calc) { + private void addAngioEdges( + ArrayList list, SiteNode start, SiteNode end, int tick, Calculation calc) { ArrayList added = new ArrayList<>(); @@ -623,8 +642,10 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en } for (SiteEdge edge : added) { - edge.radius = (otherRadius > CAPILLARY_RADIUS) ? CAPILLARY_RADIUS - : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); + edge.radius = + (otherRadius > CAPILLARY_RADIUS) + ? CAPILLARY_RADIUS + : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); @@ -645,8 +666,10 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en updateRootsAndRadii(added, start, end); break; case DIVERT: - SiteNode intersection = (SiteNode) graph.findDownstreamIntersection((SiteEdge) outEdges.get(0), - (SiteEdge) added.get(0)); + SiteNode intersection = + (SiteNode) + graph.findDownstreamIntersection( + (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); if (intersection != null) { recalcRadii(added, start, end, intersection); } else { @@ -654,7 +677,6 @@ private void addAngioEdges(ArrayList list, SiteNode start, SiteNode en } break; } - } private double calculateEvenSplitRadius(SiteEdge edge) { @@ -662,8 +684,12 @@ private double calculateEvenSplitRadius(SiteEdge edge) { double length = edge.length; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); - double newRadius = Solver.bisection((double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), 1E-6, - 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); + double newRadius = + Solver.bisection( + (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), + 1E-6, + 5 * MAXIMUM_CAPILLARY_RADIUS, + 1E-6); // LOGGER.info("splitting radius, for checking if it happens directly before // bisection failing"); // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * @@ -772,7 +798,8 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } } - private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { + private void recalcRadii( + ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { updateGraph(graph); Bag edges = graph.getEdgesOut(start); @@ -800,7 +827,11 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN } if (intersection != null) { if (intersection.isRoot) { - updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, false, + updateRadiusToRoot( + (SiteEdge) edges.get(angioIndex), + sites.graphFactory.veins.get(0).node, + divertedFlow, + false, ignoredEdges); return; // updateRadiusToRoot((SiteEdge) edges.get(angioIndex), intersection, @@ -809,13 +840,24 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN // divertedFlow, true, ignoredEdges); } - if (updateRadius((SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, true, - ignoredEdges) == -1) { + if (updateRadius( + (SiteEdge) edges.get(nonAngioIndex), + intersection, + divertedFlow, + true, + ignoredEdges) + == -1) { return; } ; - if (updateRadius((SiteEdge) edges.get(angioIndex), intersection, divertedFlow, false, ignoredEdges) == -1) { + if (updateRadius( + (SiteEdge) edges.get(angioIndex), + intersection, + divertedFlow, + false, + ignoredEdges) + == -1) { // LOGGER.info("Failed to update radius when increasing size, something seems // up"); } @@ -827,9 +869,14 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN // this should only work for single vein simulations SiteNode boundary = sites.graphFactory.veins.get(0).node; path(graph, start, boundary); - if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { + if (boundary.prev != null + && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { // LOGGER.info("Calculating additional flow to vein"); - updateRadiusToRoot((SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, false, + updateRadiusToRoot( + (SiteEdge) edges.get(angioIndex), + sites.graphFactory.veins.get(0).node, + divertedFlow, + false, ignoredEdges); } else { return; @@ -837,10 +884,13 @@ private void recalcRadii(ArrayList ignoredEdges, SiteNode start, SiteN // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, // divertedFlow, true, ignoredEdges); } - } - private int updateRadius(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, + private int updateRadius( + SiteEdge edge, + SiteNode intersection, + double flow, + boolean decrease, ArrayList ignored) { ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); edgesToUpdate.add(0, edge); @@ -852,8 +902,8 @@ private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolea return updateRadiiOfEdgeList(edges, flow, decrease, new ArrayList<>()); } - private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease, - ArrayList ignored) { + private int updateRadiiOfEdgeList( + ArrayList edges, double flow, boolean decrease, ArrayList ignored) { ArrayList oldRadii = new ArrayList<>(); for (SiteEdge e : edges) { oldRadii.add(e.radius); @@ -873,7 +923,9 @@ private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); + Function f = + (double r) -> + originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); @@ -890,9 +942,15 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow + sign * flow - - calculateLocalFlow(r, edge.length, - edge.getFrom().pressure - calculatePressure(r, edge.type.category)); + Function f = + (double r) -> + originalFlow + + sign * flow + - calculateLocalFlow( + r, + edge.length, + edge.getFrom().pressure + - calculatePressure(r, edge.type.category)); double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); @@ -911,11 +969,20 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow + sign * flow - - calculateLocalFlow(r, edge.length, calculatePressure(r, edge.type.category) - edge.getTo().pressure); + Function f = + (double r) -> + originalFlow + + sign * flow + - calculateLocalFlow( + r, + edge.length, + calculatePressure(r, edge.type.category) + - edge.getTo().pressure); double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); - if (newRadius == .5 * originalRadius || newRadius == Double.NaN || newRadius == 1.5 * originalRadius) { + if (newRadius == .5 * originalRadius + || newRadius == Double.NaN + || newRadius == 1.5 * originalRadius) { return -1; } @@ -924,7 +991,11 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea return 0; } - private void updateRadiusToRoot(SiteEdge edge, SiteNode intersection, double flow, boolean decrease, + private void updateRadiusToRoot( + SiteEdge edge, + SiteNode intersection, + double flow, + boolean decrease, ArrayList ignored) { ArrayList veins = sites.graphFactory.veins; ArrayList oldRadii = new ArrayList<>(); @@ -961,7 +1032,8 @@ private void resetRadii(ArrayList edges, ArrayList oldRadii) { } } - private void addEdgeList(ArrayList list, boolean updateProperties, EdgeType edgeType) { + private void addEdgeList( + ArrayList list, boolean updateProperties, EdgeType edgeType) { for (SiteEdge edge : list) { graph.addEdge(edge); } @@ -973,7 +1045,8 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node, int tick) if (sites.graphFactory.checkNode(proposed) && graph.getDegree(node) < 3) { SiteEdge edge; if (graph.contains(proposed)) { - if (graph.getDegree(proposed) > 2 || graph.getEdgesOut(proposed) == null + if (graph.getDegree(proposed) > 2 + || graph.getEdgesOut(proposed) == null || graph.getEdgesIn(proposed) == null) { return null; } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 91e5dda3e..1033694c8 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -13,9 +13,9 @@ import arcade.core.util.MiniBox; import arcade.core.util.Solver; import arcade.core.util.Solver.Function; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.sim.PatchSeries; -import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeTag; import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; @@ -24,36 +24,24 @@ /** * Extension of {@link PatchComponentSites} for graph sites. * - *

- * Layout of the underlying graph is specified by {@code GRAPH_LAYOUT}. The - * pattern layout is - * specified by using {@code "*"}. The root layout is specified by specifying - * roots: + *

Layout of the underlying graph is specified by {@code GRAPH_LAYOUT}. The pattern layout is + * specified by using {@code "*"}. The root layout is specified by specifying roots: * *

    - *
  • {@code [ random <#>]} = random roots with {@code <#>} randomly - * spaced roots, - * randomly assigned as artery or vein - *
  • {@code [ alternate <#>]} = alternating roots with {@code <#>} - * evenly spaced roots, - * alternating between arteries and veins - *
  • {@code [ single <#>]} = single {@code } root placed a - * distance {@code + *
  • {@code [ random <#>]} = random roots with {@code <#>} randomly spaced roots, + * randomly assigned as artery or vein + *
  • {@code [ alternate <#>]} = alternating roots with {@code <#>} evenly spaced roots, + * alternating between arteries and veins + *
  • {@code [ single <#>]} = single {@code } root placed a distance {@code * <#>} percent across the specified border - *
  • {@code [ line <#><#>]} = line {@code } root placed a - * distance {@code - * <#>} (first number) percent across the specified border that spans a - * distance {@code <#>} - * (second number) percent across the environment in the direction normal to the - * specified - * border + *
  • {@code [ line <#><#>]} = line {@code } root placed a distance {@code + * <#>} (first number) percent across the specified border that spans a distance {@code <#>} + * (second number) percent across the environment in the direction normal to the specified + * border *
* - *

- * The border {@code } can be {@code LEFT} (-x direction), {@code RIGHT} - * (+x direction), - * {@code TOP} (-y direction), or {@code BOTTOM} (+y direction). The type - * {@code } can be + *

The border {@code } can be {@code LEFT} (-x direction), {@code RIGHT} (+x direction), + * {@code TOP} (-y direction), or {@code BOTTOM} (+y direction). The type {@code } can be * {@code A} / {@code a} for an artery or {@code V} / {@code v} for a vein. */ public abstract class PatchComponentSitesGraph extends PatchComponentSites { @@ -90,18 +78,17 @@ public abstract class PatchComponentSitesGraph extends PatchComponentSites { /** * Creates a {@link PatchComponentSites} using graph sites. * - *

- * Loaded parameters include: + *

Loaded parameters include: * *

    - *
  • {@code GRAPH_LAYOUT} = graph layout type - *
  • {@code OXYGEN_SOLUBILITY_PLASMA} = solubility of oxygen in plasma - *
  • {@code OXYGEN_SOLUBILITY_TISSUE} = solubility of oxygen in tissue + *
  • {@code GRAPH_LAYOUT} = graph layout type + *
  • {@code OXYGEN_SOLUBILITY_PLASMA} = solubility of oxygen in plasma + *
  • {@code OXYGEN_SOLUBILITY_TISSUE} = solubility of oxygen in tissue *
* - * @param series the simulation series + * @param series the simulation series * @param parameters the component parameters dictionary - * @param random the random number generator + * @param random the random number generator */ public PatchComponentSitesGraph(Series series, MiniBox parameters, MersenneTwisterFast random) { super(series); @@ -141,7 +128,7 @@ public Graph getGraph() { * Gets the lattice coordinates spanned by an edge between two nodes. * * @param from the node the edge extends from - * @param to the node the edge extends to + * @param to the node the edge extends to * @return the list of span coordinates */ abstract ArrayList getSpan(SiteNode from, SiteNode to); @@ -171,11 +158,8 @@ void checkSite(ArrayList s, int x, int y, int z) { /** * Initializes graph for representing sites. * - *

- * Calls the correct method to populate the graph with edges (either pattern or - * root layout). - * After the graph is defined, the corresponding indices in the lattice adjacent - * to edges are + *

Calls the correct method to populate the graph with edges (either pattern or root layout). + * After the graph is defined, the corresponding indices in the lattice adjacent to edges are * marked. * * @param random the random number generator @@ -207,13 +191,10 @@ Graph initializeGraph(MersenneTwisterFast random) { /** * Graph step that only considers differences in concentration. * - *

- * Method is equivalent to the step used with {@link + *

Method is equivalent to the step used with {@link * arcade.patch.env.component.PatchComponentSitesSource} and {@link - * arcade.patch.env.component.PatchComponentSitesPattern} where the amount of - * concentration - * added is the difference between the source concentration and the current - * concentration for a + * arcade.patch.env.component.PatchComponentSitesPattern} where the amount of concentration + * added is the difference between the source concentration and the current concentration for a * given molecule. */ void simpleStep() { @@ -253,11 +234,8 @@ void simpleStep() { /** * Graph step that uses traversals to calculate exact hemodynamics. * - *

- * Traversing the graph updates the concentrations of molecules in each edge. - * The amount of - * concentration added is a function of flow rate and permeability to the given - * molecule. + *

Traversing the graph updates the concentrations of molecules in each edge. The amount of + * concentration added is a function of flow rate and permeability to the given molecule. * * @param random the random number generator */ @@ -358,8 +336,9 @@ void complexStep(MersenneTwisterFast random) { // Check for stability. double max = latticePatchVolume / edge.area; if (permeability > max) { - intConcNew = (intConcNew * flow + latticePatchVolume * extConcNew) - / (flow + latticePatchVolume); + intConcNew = + (intConcNew * flow + latticePatchVolume * extConcNew) + / (flow + latticePatchVolume); extConcNew = intConcNew; } else { // Iterate for each second in the minute time step. @@ -377,12 +356,14 @@ void complexStep(MersenneTwisterFast random) { int k = coordinate.z; if (layer.name.equalsIgnoreCase("OXYGEN")) { - delta[k][i][j] += Math.max( - (extConcNew / oxySoluTissue - - (current[k][i][j] + delta[k][i][j])), - 0); + delta[k][i][j] += + Math.max( + (extConcNew / oxySoluTissue + - (current[k][i][j] + delta[k][i][j])), + 0); } else { - delta[k][i][j] += Math.max((extConcNew - (current[k][i][j] + delta[k][i][j])), 0); + delta[k][i][j] += + Math.max((extConcNew - (current[k][i][j] + delta[k][i][j])), 0); } } @@ -400,8 +381,7 @@ void complexStep(MersenneTwisterFast random) { /** * Extension of {@link arcade.core.util.Graph.Node} for site nodes. * - *

- * Node tracks additional hemodynamic properties including pressure and oxygen. + *

Node tracks additional hemodynamic properties including pressure and oxygen. */ public static class SiteNode extends Node { /** Node ID. */ @@ -428,10 +408,7 @@ public static class SiteNode extends Node { /** Direction of the angiogenic sprout. */ EdgeDirection sproutDir; - /** - * {@ True} if the angiogenic sprrout is anastomotic/perfused, {@code false} - * otherwise. - */ + /** {@ True} if the angiogenic sprrout is anastomotic/perfused, {@code false} otherwise. */ boolean anastomosis; /** Parent node. */ @@ -486,9 +463,7 @@ public double getOxygen() { /** * Extension of {@link arcade.core.util.Graph.Edge} for site edges. * - *

- * Node tracks additional hemodynamic properties including radius, length, wall - * thickness, + *

Node tracks additional hemodynamic properties including radius, length, wall thickness, * shear stress, circumferential stress, and volumetric flow rate. */ public static class SiteEdge extends Edge { @@ -549,9 +524,9 @@ public static class SiteEdge extends Edge { /** * Creates a {@link Edge} for graph sites. * - * @param from the node the edge is from - * @param to the node the edge is to - * @param type the edge type + * @param from the node the edge is from + * @param to the node the edge is to + * @param type the edge type * @param level the graph resolution level */ SiteEdge(SiteNode from, SiteNode to, EdgeType type, EdgeLevel level) { @@ -838,8 +813,9 @@ private ArrayList traverseNode(SiteNode node, String code) { for (Object obj : in) { SiteEdge edge = (SiteEdge) obj; if (!edge.isIgnored) { - massIn += edge.flow * getTotal(edge.getFrom().oxygen, oxySoluPlasma) - - edge.transport.get(code); + massIn += + edge.flow * getTotal(edge.getFrom().oxygen, oxySoluPlasma) + - edge.transport.get(code); } } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java index fd2608bfa..f2ec3a483 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java @@ -1,9 +1,9 @@ package arcade.patch.env.component; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.EnumMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import sim.util.Bag; @@ -19,13 +19,11 @@ /** * Factory for building a {@link Graph} for {@link PatchComponentSitesGraph}. * - *

- * Graph can be initialized in two ways: + *

Graph can be initialized in two ways: * *

    - *
  • pattern layout that matches the structure used by - * {@link PatchComponentSitesPattern} - *
  • root layout grown from a specified root system using motifs + *
  • pattern layout that matches the structure used by {@link PatchComponentSitesPattern} + *
  • root layout grown from a specified root system using motifs *
*/ public abstract class PatchComponentSitesGraphFactory { @@ -223,7 +221,7 @@ public PatchComponentSitesGraphFactory(Series series) { /** * Calculates the column of the pattern based on offset and index. * - * @param i the index in the x direction + * @param i the index in the x direction * @param offset the lattice offset * @return the column index */ @@ -232,8 +230,8 @@ public PatchComponentSitesGraphFactory(Series series) { /** * Calculates the row of the pattern based on offset and index. * - * @param i the index in the x direction - * @param j the index in the y direction + * @param i the index in the x direction + * @param j the index in the y direction * @param offset the lattice offset * @return the row index */ @@ -252,16 +250,16 @@ public PatchComponentSitesGraphFactory(Series series) { * * @param fromX the x coordinate of the node the edge is from * @param fromY the y coordinate of the node the edge is from - * @param toX the x coordinate of the node the edge is to - * @param toY the y coordinate of the node the edge is to + * @param toX the x coordinate of the node the edge is to + * @param toY the y coordinate of the node the edge is to * @return the code for the edge direction */ abstract EdgeDirection getDirection(int fromX, int fromY, int toX, int toY); /** * Gets the opposite direction of the given edge. - * - * @param edge the edge object + * + * @param edge the edge object * @param scale the graph resolution level * @return the code for the opposite edge direction */ @@ -272,12 +270,12 @@ public EdgeDirection getOppositeDirection(SiteEdge edge, EdgeLevel level) { /** * Adds a root motif to the graph. * - * @param graph the graph instance - * @param node0 the node the motif starts at - * @param dir the direction code of the root - * @param type the root type + * @param graph the graph instance + * @param node0 the node the motif starts at + * @param dir the direction code of the root + * @param type the root type * @param offsets the list of offsets for line roots, null otherwise - * @param random the random number generator + * @param random the random number generator * @return the bag of active edges */ abstract Bag addRoot( @@ -291,12 +289,12 @@ abstract Bag addRoot( /** * Adds an edge motif to the graph. * - * @param graph the graph instance - * @param node0 the node the motif starts at - * @param edge0 the edge the motif is being added to - * @param type the edge type - * @param level the graph resolution level - * @param motif the motif type + * @param graph the graph instance + * @param node0 the node the motif starts at + * @param edge0 the edge the motif is being added to + * @param type the edge type + * @param level the graph resolution level + * @param motif the motif type * @param random the random number generator * @return the bag of active edges */ @@ -312,10 +310,10 @@ abstract Bag addMotif( /** * Adds a capillary segment joining edges of different types to the graph. * - * @param graph the graph instance - * @param node0 the node the segment starts at - * @param dir the direction code for the segment - * @param level the graph resolution level + * @param graph the graph instance + * @param node0 the node the segment starts at + * @param dir the direction code for the segment + * @param level the graph resolution level * @param random the random number generator */ abstract void addSegment( @@ -328,11 +326,11 @@ abstract void addSegment( /** * Adds a connection joining edges of the same type to the graph. * - * @param graph the graph instance - * @param node0 the node the connection starts at - * @param dir the direction code for the segment - * @param type the connection type - * @param level the graph resolution level + * @param graph the graph instance + * @param node0 the node the connection starts at + * @param dir the direction code for the segment + * @param type the connection type + * @param level the graph resolution level * @param random the random number generator */ abstract void addConnection( @@ -352,18 +350,16 @@ abstract void addConnection( abstract int[] getOffset(EdgeDirection offset); /** - * Get a map of possible offset directions to their corresponding coordinate - * changes. + * Get a map of possible offset directions to their corresponding coordinate changes. * - * @return the map of offset directions to their corresponding coordinate - * changes + * @return the map of offset directions to their corresponding coordinate changes */ abstract EnumMap getOffsets(); /** * Gets the length of the given edge. * - * @param edge the edge object + * @param edge the edge object * @param level the graph resolution level * @return the length of the edge */ @@ -379,10 +375,10 @@ abstract void addConnection( /** * Creates a {@link Root} for graph sites using a root layout. * - * @param border the border the root extends from + * @param border the border the root extends from * @param percent the percentage distance along the border - * @param type the root type - * @param level the graph resolution level + * @param type the root type + * @param level the graph resolution level * @return a {@link Root} object */ abstract Root createRoot(Border border, double percent, EdgeType type, EdgeLevel level); @@ -390,10 +386,10 @@ abstract void addConnection( /** * Creates offsets for a {@link Root} for graph sites using a root layout. * - * @param border the border the root extends from + * @param border the border the root extends from * @param percent the percentage distance in the perpendicular direction - * @param level the graph resolution level - * @param random the random number generator + * @param level the graph resolution level + * @param random the random number generator * @return a list of offsets */ abstract EdgeDirection[] createRootOffsets( @@ -419,10 +415,10 @@ static class Root { /** * Creates a {@code Root} object for generating root graphs. * - * @param x the x coordinate - * @param y the y coordinate + * @param x the x coordinate + * @param y the y coordinate * @param type the edge type - * @param dir the direction code of the root + * @param dir the direction code of the root */ Root(int x, int y, EdgeType type, EdgeDirection dir) { node = new SiteNode(x, y, 0); @@ -434,7 +430,7 @@ static class Root { /** * Gets direction code for an edge. * - * @param edge the edge object + * @param edge the edge object * @param level the graph resolution level * @return the code for the edge direction */ @@ -445,8 +441,8 @@ EdgeDirection getDirection(SiteEdge edge, EdgeLevel level) { /** * Gets direction code for an edge. * - * @param from the node the edge is from - * @param to the node the edge is to + * @param from the node the edge is from + * @param to the node the edge is to * @param level the graph resolution level * @return the code for the edge direction */ @@ -459,9 +455,9 @@ EdgeDirection getDirection(SiteNode from, SiteNode to, EdgeLevel level) { /** * Creates a node offset in the given direction. * - * @param node the node of the initial location + * @param node the node of the initial location * @param offset the offset direction - * @param level the graph resolution level + * @param level the graph resolution level * @return an offset node */ SiteNode offsetNode(SiteNode node, EdgeDirection offset, EdgeLevel level) { @@ -495,7 +491,7 @@ public Graph initializePatternGraph(MersenneTwisterFast random) { } // Traverse graph from capillaries to calculate radii. - ArrayList caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); + ArrayList caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); updateRadii(graph, caps, CalculationType.UPSTREAM_PATTERN, random); updateRadii(graph, caps, CalculationType.DOWNSTREAM_PATTERN, random); @@ -565,27 +561,30 @@ private void mergePatternGraph(Graph graph) { for (SiteEdge edge1 : set) { if (graph.getOutDegree(edge1.getTo()) == 1) { int scale1 = scales.get(edge1); - EdgeDirection dir1 = getDirection( - edge1.getFrom().getX() / scale1, - edge1.getFrom().getY() / scale1, - edge1.getTo().getX() / scale1, - edge1.getTo().getY() / scale1); + EdgeDirection dir1 = + getDirection( + edge1.getFrom().getX() / scale1, + edge1.getFrom().getY() / scale1, + edge1.getTo().getX() / scale1, + edge1.getTo().getY() / scale1); SiteEdge edge2 = (SiteEdge) edge1.getEdgesOut().get(0); int scale2 = scales.get(edge2); - EdgeDirection dir2 = getDirection( - edge2.getFrom().getX() / scale2, - edge2.getFrom().getY() / scale2, - edge2.getTo().getX() / scale2, - edge2.getTo().getY() / scale2); + EdgeDirection dir2 = + getDirection( + edge2.getFrom().getX() / scale2, + edge2.getFrom().getY() / scale2, + edge2.getTo().getX() / scale2, + edge2.getTo().getY() / scale2); // Join edges that are the same direction and type. if (dir1 == dir2 && edge1.type == edge2.type) { - SiteEdge join = new SiteEdge( - edge1.getFrom(), - edge2.getTo(), - edge1.type, - EdgeLevel.VARIABLE); + SiteEdge join = + new SiteEdge( + edge1.getFrom(), + edge2.getTo(), + edge1.type, + EdgeLevel.VARIABLE); scales.put(join, scale1 + scale2); // Set length to be sum and radius to be average of the @@ -618,7 +617,7 @@ private void mergePatternGraph(Graph graph) { /** * Initializes graph with edges and nodes in a root layout. * - * @param random the random number generator + * @param random the random number generator * @param graphLayout the specification for layout of roots * @return a graph instance with root layout */ @@ -673,7 +672,7 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) addMotifs(graph, leaves2, EdgeLevel.LEVEL_1, EdgeMotif.SINGLE, random); // Calculate radii, pressure, and shears. - updateRootGraph(graph,EdgeLevel.LEVEL_1, random); + updateRootGraph(graph, EdgeLevel.LEVEL_1, random); // Iterative remodeling. int iter = 0; @@ -705,29 +704,26 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) /** * Updates hemodynamic properties for graph sites with root layouts. * - * @param graph the graph instance + * @param graph the graph instance * @param arteries the list of artery edges - * @param veins the list of vein edges - * @param level the graph resolution level - * @param random the random number generator + * @param veins the list of vein edges + * @param level the graph resolution level + * @param random the random number generator */ - private void updateRootGraph( - Graph graph, - EdgeLevel level, - MersenneTwisterFast random) { + private void updateRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFast random) { ArrayList list; ArrayList caps = new ArrayList<>(); // Store upper level capillaries. if (level != EdgeLevel.LEVEL_1) { - caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); + caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); for (SiteEdge edge : caps) { graph.removeEdge(edge); } } // Get all leaves and update radii. - list = getLeavesByType(graph, new EdgeType[] { EdgeType.ARTERY, EdgeType.VEIN }); + list = getLeavesByType(graph, new EdgeType[] {EdgeType.ARTERY, EdgeType.VEIN}); updateRadii(graph, list, CalculationType.UPSTREAM_ALL); // Replace level 1 edges capillaries. @@ -740,16 +736,17 @@ private void updateRootGraph( addSegments(graph, level, random); addConnections(graph, level, random); - caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); + caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); // Get capillaries and arterioles and update radii. switch (level) { case LEVEL_1: - list = getEdgeByType( - graph, new EdgeType[] { EdgeType.CAPILLARY, EdgeType.ARTERIOLE }); + list = + getEdgeByType( + graph, new EdgeType[] {EdgeType.CAPILLARY, EdgeType.ARTERIOLE}); break; case LEVEL_2: - list = getEdgeByType(graph, new EdgeType[] { EdgeType.ARTERIOLE }, level); + list = getEdgeByType(graph, new EdgeType[] {EdgeType.ARTERIOLE}, level); list.addAll(caps); break; default: @@ -764,10 +761,10 @@ private void updateRootGraph( // Get capillaries and venules and update radii. switch (level) { case LEVEL_1: - list = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY, EdgeType.VENULE }); + list = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY, EdgeType.VENULE}); break; case LEVEL_2: - list = getEdgeByType(graph, new EdgeType[] { EdgeType.VENULE }, level); + list = getEdgeByType(graph, new EdgeType[] {EdgeType.VENULE}, level); list.addAll(caps); break; default: @@ -837,13 +834,14 @@ private void updateRootGraph( /** * Refines the graph for graph sites with root layouts. * - * @param graph the graph instance + * @param graph the graph instance * @param arteries the list of artery edges - * @param veins the list of vein edges + * @param veins the list of vein edges */ private void refineRootGraph(Graph graph) { // Reverse edges that are veins and venules. - ArrayList reverse = getEdgeByType(graph, new EdgeType[] { EdgeType.VEIN, EdgeType.VENULE }); + ArrayList reverse = + getEdgeByType(graph, new EdgeType[] {EdgeType.VEIN, EdgeType.VENULE}); for (SiteEdge edge : reverse) { graph.reverseEdge(edge); } @@ -852,7 +850,7 @@ private void refineRootGraph(Graph graph) { reversePressures(graph); // Check for non-connected graph. - ArrayList caps = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); + ArrayList caps = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); if (caps.size() < 1) { graph.clear(); return; @@ -872,7 +870,7 @@ private void refineRootGraph(Graph graph) { } // Get all capillaries and update radii. - ArrayList list = getEdgeByType(graph, new EdgeType[] { EdgeType.CAPILLARY }); + ArrayList list = getEdgeByType(graph, new EdgeType[] {EdgeType.CAPILLARY}); updateRadii(graph, list, CalculationType.UPSTREAM_ARTERIES); updateRadii(graph, list, CalculationType.DOWNSTREAM_VEINS); @@ -981,16 +979,17 @@ private Bag subdivideRootGraph(Graph graph, EdgeLevel level) { /** * Remodels sites based on shear stress. * - * @param graph the graph instance - * @param level the graph resolution level + * @param graph the graph instance + * @param level the graph resolution level * @param random the random number generator * @return the fraction of edges remodeled */ private double remodelRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFast random) { // Remove capillaries, arterioles, and venules. - ArrayList list = getEdgeByType( - graph, - new EdgeType[] { EdgeType.CAPILLARY, EdgeType.VENULE, EdgeType.ARTERIOLE }); + ArrayList list = + getEdgeByType( + graph, + new EdgeType[] {EdgeType.CAPILLARY, EdgeType.VENULE, EdgeType.ARTERIOLE}); for (SiteEdge edge : list) { graph.removeEdge(edge); } @@ -1038,24 +1037,26 @@ private double remodelRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFas for (Object obj : allEdges) { SiteEdge edge = (SiteEdge) obj; if (edge.tag == EdgeTag.ADD && graph.getDegree(edge.getTo()) < 3) { - Bag bag1 = addMotif( - graph, - edge.getTo(), - edge, - edge.type, - level, - EdgeMotif.TRIPLE, - random); + Bag bag1 = + addMotif( + graph, + edge.getTo(), + edge, + edge.type, + level, + EdgeMotif.TRIPLE, + random); SiteEdge edge1 = (SiteEdge) bag1.get(0); - Bag bag2 = addMotif( - graph, - edge1.getTo(), - edge, - edge.type, - level, - EdgeMotif.DOUBLE, - random); + Bag bag2 = + addMotif( + graph, + edge1.getTo(), + edge, + edge.type, + level, + EdgeMotif.DOUBLE, + random); SiteEdge edge2 = (SiteEdge) bag2.get(0); addMotif(graph, edge2.getTo(), edge, edge.type, level, EdgeMotif.SINGLE, random); @@ -1139,10 +1140,10 @@ private ArrayList parseRoots(String layout, MersenneTwisterFast random) { /** * Adds motifs to graph until no additional motifs can be added. * - * @param graph the graph instance - * @param bag the current bag of active edges - * @param level the graph resolution level - * @param motif the motif code + * @param graph the graph instance + * @param bag the current bag of active edges + * @param level the graph resolution level + * @param motif the motif code * @param random the random number generator * @return the updated bag of active edges */ @@ -1191,8 +1192,8 @@ private Bag addMotifs( /** * Adds segments to graph between arteries and veins. * - * @param graph the graph instance - * @param level the graph resolution level + * @param graph the graph instance + * @param level the graph resolution level * @param random the random number generator */ private void addSegments(Graph graph, EdgeLevel level, MersenneTwisterFast random) { @@ -1215,8 +1216,8 @@ private void addSegments(Graph graph, EdgeLevel level, MersenneTwisterFast rando /** * Adds connections to graphs between arteries or between veins. * - * @param graph the graph instance - * @param level the graph resolution level + * @param graph the graph instance + * @param level the graph resolution level * @param random the random number generator */ private void addConnections(Graph graph, EdgeLevel level, MersenneTwisterFast random) { diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java index bc321cd33..98d3722c2 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryRect.java @@ -14,11 +14,9 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.*; /** - * Concrete implementation of {@link PatchComponentSitesGraphFactory} for - * rectangular geometry. + * Concrete implementation of {@link PatchComponentSitesGraphFactory} for rectangular geometry. * - *

- * For pattern layout, the graph is given by: + *

For pattern layout, the graph is given by: * *

  *                         _ _ _ _
@@ -36,108 +34,108 @@
  *                       \ _ _ _ _ /
  * 
* - *

- * For root layouts, each node has eight possible orientations for the edge: - * left, right, up, - * down, up left, up right, down left, and down right. When initializing roots - * from a border, only + *

For root layouts, each node has eight possible orientations for the edge: left, right, up, + * down, up left, up right, down left, and down right. When initializing roots from a border, only * certain orientations are possible: * *

    - *
  • left border = right, up right, down right - *
  • right border = left, up left, down left - *
  • top border = down, down right, down left - *
  • bottom border = up, up right, up left + *
  • left border = right, up right, down right + *
  • right border = left, up left, down left + *
  • top border = down, down right, down left + *
  • bottom border = up, up right, up left *
*/ public class PatchComponentSitesGraphFactoryRect extends PatchComponentSitesGraphFactory { /** List of all possible edge directions. */ - private static final EnumSet EDGE_DIRECTIONS = EnumSet.of( - EdgeDirection.UP, - EdgeDirection.UP_RIGHT, - EdgeDirection.RIGHT, - EdgeDirection.DOWN_RIGHT, - EdgeDirection.DOWN, - EdgeDirection.DOWN_LEFT, - EdgeDirection.LEFT, - EdgeDirection.UP_LEFT); + private static final EnumSet EDGE_DIRECTIONS = + EnumSet.of( + EdgeDirection.UP, + EdgeDirection.UP_RIGHT, + EdgeDirection.RIGHT, + EdgeDirection.DOWN_RIGHT, + EdgeDirection.DOWN, + EdgeDirection.DOWN_LEFT, + EdgeDirection.LEFT, + EdgeDirection.UP_LEFT); /** Map of edge directions to their reverse direction. */ - private static final EnumSet SINGLE_EDGE_DIRECTIONS = EnumSet.of( - EdgeDirection.UP_RIGHT, - EdgeDirection.DOWN_RIGHT, - EdgeDirection.DOWN_LEFT, - EdgeDirection.UP_LEFT); + private static final EnumSet SINGLE_EDGE_DIRECTIONS = + EnumSet.of( + EdgeDirection.UP_RIGHT, + EdgeDirection.DOWN_RIGHT, + EdgeDirection.DOWN_LEFT, + EdgeDirection.UP_LEFT); /** Map of edge directions to their reverse direction. */ - private static final EnumMap REVERSE_EDGE_DIRECTIONS = new EnumMap( - EdgeDirection.class) { - { - put(EdgeDirection.UP, EdgeDirection.DOWN); - put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); - put(EdgeDirection.RIGHT, EdgeDirection.LEFT); - put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); - put(EdgeDirection.DOWN, EdgeDirection.UP); - put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); - put(EdgeDirection.LEFT, EdgeDirection.RIGHT); - put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); - } - }; + private static final EnumMap REVERSE_EDGE_DIRECTIONS = + new EnumMap(EdgeDirection.class) { + { + put(EdgeDirection.UP, EdgeDirection.DOWN); + put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); + put(EdgeDirection.RIGHT, EdgeDirection.LEFT); + put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); + put(EdgeDirection.DOWN, EdgeDirection.UP); + put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); + put(EdgeDirection.LEFT, EdgeDirection.RIGHT); + put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); + } + }; /** List of coordinate offsets for each direction. */ - private static final EnumMap OFFSETS = new EnumMap( - EdgeDirection.class) { - { - put(EdgeDirection.UP, new int[] { 0, -1, 0 }); - put(EdgeDirection.UP_RIGHT, new int[] { 1, -1, 0 }); - put(EdgeDirection.RIGHT, new int[] { 1, 0, 0 }); - put(EdgeDirection.DOWN_RIGHT, new int[] { 1, 1, 0 }); - put(EdgeDirection.DOWN, new int[] { 0, 1, 0 }); - put(EdgeDirection.DOWN_LEFT, new int[] { -1, 1, 0 }); - put(EdgeDirection.LEFT, new int[] { -1, 0, 0 }); - put(EdgeDirection.UP_LEFT, new int[] { -1, -1, 0 }); - } - }; + private static final EnumMap OFFSETS = + new EnumMap(EdgeDirection.class) { + { + put(EdgeDirection.UP, new int[] {0, -1, 0}); + put(EdgeDirection.UP_RIGHT, new int[] {1, -1, 0}); + put(EdgeDirection.RIGHT, new int[] {1, 0, 0}); + put(EdgeDirection.DOWN_RIGHT, new int[] {1, 1, 0}); + put(EdgeDirection.DOWN, new int[] {0, 1, 0}); + put(EdgeDirection.DOWN_LEFT, new int[] {-1, 1, 0}); + put(EdgeDirection.LEFT, new int[] {-1, 0, 0}); + put(EdgeDirection.UP_LEFT, new int[] {-1, -1, 0}); + } + }; /** List of offset directions for root directions. */ - private static final EnumMap ROOT_OFFSETS = new EnumMap( - EdgeDirection.class) { - { - put( - EdgeDirection.UP, - new EdgeDirection[] { EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT }); - put( - EdgeDirection.UP_RIGHT, - new EdgeDirection[] { EdgeDirection.RIGHT, EdgeDirection.UP }); - put( - EdgeDirection.RIGHT, - new EdgeDirection[] { EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT }); - put( - EdgeDirection.DOWN_RIGHT, - new EdgeDirection[] { EdgeDirection.DOWN, EdgeDirection.RIGHT }); - put( - EdgeDirection.DOWN, - new EdgeDirection[] { - EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN_RIGHT - }); - put( - EdgeDirection.DOWN_LEFT, - new EdgeDirection[] { EdgeDirection.LEFT, EdgeDirection.DOWN }); - put( - EdgeDirection.LEFT, - new EdgeDirection[] { EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT }); - put( - EdgeDirection.UP_LEFT, - new EdgeDirection[] { EdgeDirection.UP, EdgeDirection.LEFT }); - } - }; + private static final EnumMap ROOT_OFFSETS = + new EnumMap(EdgeDirection.class) { + { + put( + EdgeDirection.UP, + new EdgeDirection[] {EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT}); + put( + EdgeDirection.UP_RIGHT, + new EdgeDirection[] {EdgeDirection.RIGHT, EdgeDirection.UP}); + put( + EdgeDirection.RIGHT, + new EdgeDirection[] {EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT}); + put( + EdgeDirection.DOWN_RIGHT, + new EdgeDirection[] {EdgeDirection.DOWN, EdgeDirection.RIGHT}); + put( + EdgeDirection.DOWN, + new EdgeDirection[] { + EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN_RIGHT + }); + put( + EdgeDirection.DOWN_LEFT, + new EdgeDirection[] {EdgeDirection.LEFT, EdgeDirection.DOWN}); + put( + EdgeDirection.LEFT, + new EdgeDirection[] {EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT}); + put( + EdgeDirection.UP_LEFT, + new EdgeDirection[] {EdgeDirection.UP, EdgeDirection.LEFT}); + } + }; /** Array positions for edge directions. */ - private static final EdgeDirection[][] DIRS = new EdgeDirection[][] { - { EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT }, - { EdgeDirection.LEFT, EdgeDirection.UNDEFINED, EdgeDirection.RIGHT }, - { EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT }, - }; + private static final EdgeDirection[][] DIRS = + new EdgeDirection[][] { + {EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT}, + {EdgeDirection.LEFT, EdgeDirection.UNDEFINED, EdgeDirection.RIGHT}, + {EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT}, + }; /** List of edge lengths. */ private final double[] edgeLengths; @@ -149,7 +147,7 @@ public class PatchComponentSitesGraphFactoryRect extends PatchComponentSitesGrap */ public PatchComponentSitesGraphFactoryRect(Series series) { super(series); - edgeLengths = new double[] { series.ds, series.ds * Math.sqrt(2) }; + edgeLengths = new double[] {series.ds, series.ds * Math.sqrt(2)}; } @Override @@ -166,8 +164,8 @@ EnumMap getOffsets() { * Checks if there is an edge in the cross diagonal. * * @param graph the graph instance - * @param from the node the edge is from - * @param to the node the edge is to + * @param from the node the edge is from + * @param to the node the edge is to * @param level the graph resolution level * @return {@code true} if no cross diagonal edge, {@code false} otherwise */ @@ -251,24 +249,24 @@ void createPattern(Graph graph) { int row = calcRow(i, j, offset); if (col == 0 && row == 5) { - edges.add(new int[] { i, j, k, i + 1, j, k }); + edges.add(new int[] {i, j, k, i + 1, j, k}); } else if (col == 1 && row == 5) { - edges.add(new int[] { i, j, k, i + 1, j, k }); + edges.add(new int[] {i, j, k, i + 1, j, k}); } else if (col == 2 && row == 5) { - edges.add(new int[] { i, j, k, i + 1, j, k }); + edges.add(new int[] {i, j, k, i + 1, j, k}); } else if (col == 3 && row == 5) { - edges.add(new int[] { i, j, k, i + 1, j, k }); + edges.add(new int[] {i, j, k, i + 1, j, k}); } else if (col == 5 && row == 4) { - edges.add(new int[] { i, j, k, i, j - 1, k }); + edges.add(new int[] {i, j, k, i, j - 1, k}); } else if (col == 5 && row == 0) { - edges.add(new int[] { i, j, k, i, j + 1, k }); + edges.add(new int[] {i, j, k, i, j + 1, k}); } else if (col == 5 && row == 1) { - edges.add(new int[] { i, j, k, i + 1, j + 1, k }); + edges.add(new int[] {i, j, k, i + 1, j + 1, k}); } else if (col == 5 && row == 3) { - edges.add(new int[] { i, j, k, i + 1, j - 1, k }); + edges.add(new int[] {i, j, k, i + 1, j - 1, k}); } else if (col == 4 && row == 5) { - edges.add(new int[] { i, j, k, i + 1, j - 1, k }); - edges.add(new int[] { i, j, k, i + 1, j + 1, k }); + edges.add(new int[] {i, j, k, i + 1, j - 1, k}); + edges.add(new int[] {i, j, k, i + 1, j + 1, k}); } } } @@ -286,9 +284,10 @@ void createPattern(Graph graph) { // Add edge to graph. SiteNode from = new SiteNode(e[0], e[1], e[2]); SiteNode to = new SiteNode(e[3], e[4], e[5]); - EdgeType type = (e[0] == thresh - ? EdgeType.CAPILLARY - : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); + EdgeType type = + (e[0] == thresh + ? EdgeType.CAPILLARY + : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); SiteEdge edge = new SiteEdge(from, to, type, EdgeLevel.VARIABLE); graph.addEdge(edge); } @@ -366,27 +365,31 @@ EdgeDirection[] createRootOffsets( // Get direction list. switch (border) { case LEFT: - directions = new EdgeDirection[] { - EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT - }; + directions = + new EdgeDirection[] { + EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT + }; index = 1; break; case RIGHT: - directions = new EdgeDirection[] { - EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT - }; + directions = + new EdgeDirection[] { + EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT + }; index = 1; break; case TOP: - directions = new EdgeDirection[] { - EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT - }; + directions = + new EdgeDirection[] { + EdgeDirection.DOWN_LEFT, EdgeDirection.DOWN, EdgeDirection.DOWN_RIGHT + }; index = 0; break; case BOTTOM: - directions = new EdgeDirection[] { - EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT - }; + directions = + new EdgeDirection[] { + EdgeDirection.UP_LEFT, EdgeDirection.UP, EdgeDirection.UP_RIGHT + }; index = 0; break; default: diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java index 0607a4891..6b6d0a750 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactoryTri.java @@ -14,11 +14,9 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.*; /** - * Concrete implementation of {@link PatchComponentSitesGraphFactory} for - * triangular geometry. + * Concrete implementation of {@link PatchComponentSitesGraphFactory} for triangular geometry. * - *

- * For pattern layout, the graph is given by: + *

For pattern layout, the graph is given by: * *

  *                         ___ ___
@@ -36,105 +34,104 @@
  *                       \ ___ ___ /
  * 
* - *

- * For root layouts, each node has six possible orientations for the edge: left, - * right, up left, - * up right, down left, and down right. When initializing roots from a border, - * only certain + *

For root layouts, each node has six possible orientations for the edge: left, right, up left, + * up right, down left, and down right. When initializing roots from a border, only certain * orientations are possible: * *

    - *
  • left border = right, up right, down right - *
  • right border = left, up left, down left - *
  • top border = down right, down left - *
  • bottom border = up right, up left + *
  • left border = right, up right, down right + *
  • right border = left, up left, down left + *
  • top border = down right, down left + *
  • bottom border = up right, up left *
*/ public class PatchComponentSitesGraphFactoryTri extends PatchComponentSitesGraphFactory { /** List of all possible edge directions. */ - private static final EnumSet EDGE_DIRECTIONS = EnumSet.of( - EdgeDirection.UP_LEFT, - EdgeDirection.UP_RIGHT, - EdgeDirection.RIGHT, - EdgeDirection.DOWN_RIGHT, - EdgeDirection.DOWN_LEFT, - EdgeDirection.LEFT); + private static final EnumSet EDGE_DIRECTIONS = + EnumSet.of( + EdgeDirection.UP_LEFT, + EdgeDirection.UP_RIGHT, + EdgeDirection.RIGHT, + EdgeDirection.DOWN_RIGHT, + EdgeDirection.DOWN_LEFT, + EdgeDirection.LEFT); /** Map of edge directions to their reverse direction. */ - private static final EnumMap REVERSE_EDGE_DIRECTIONS = new EnumMap( - EdgeDirection.class) { - { - put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); - put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); - put(EdgeDirection.RIGHT, EdgeDirection.LEFT); - put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); - put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); - put(EdgeDirection.LEFT, EdgeDirection.RIGHT); - } - }; + private static final EnumMap REVERSE_EDGE_DIRECTIONS = + new EnumMap(EdgeDirection.class) { + { + put(EdgeDirection.UP_LEFT, EdgeDirection.DOWN_RIGHT); + put(EdgeDirection.UP_RIGHT, EdgeDirection.DOWN_LEFT); + put(EdgeDirection.RIGHT, EdgeDirection.LEFT); + put(EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_LEFT); + put(EdgeDirection.DOWN_LEFT, EdgeDirection.UP_RIGHT); + put(EdgeDirection.LEFT, EdgeDirection.RIGHT); + } + }; /** List of coordinate offsets for each direction. */ - private static final EnumMap OFFSETS = new EnumMap( - EdgeDirection.class) { - { - put(EdgeDirection.UP_LEFT, new int[] { -1, -1, 0 }); - put(EdgeDirection.UP_RIGHT, new int[] { 1, -1, 0 }); - put(EdgeDirection.RIGHT, new int[] { 2, 0, 0 }); - put(EdgeDirection.DOWN_RIGHT, new int[] { 1, 1, 0 }); - put(EdgeDirection.DOWN_LEFT, new int[] { -1, 1, 0 }); - put(EdgeDirection.LEFT, new int[] { -2, 0, 0 }); - } - }; + private static final EnumMap OFFSETS = + new EnumMap(EdgeDirection.class) { + { + put(EdgeDirection.UP_LEFT, new int[] {-1, -1, 0}); + put(EdgeDirection.UP_RIGHT, new int[] {1, -1, 0}); + put(EdgeDirection.RIGHT, new int[] {2, 0, 0}); + put(EdgeDirection.DOWN_RIGHT, new int[] {1, 1, 0}); + put(EdgeDirection.DOWN_LEFT, new int[] {-1, 1, 0}); + put(EdgeDirection.LEFT, new int[] {-2, 0, 0}); + } + }; /** List of offset directions for root directions. */ - private static final EnumMap ROOT_OFFSETS = new EnumMap( - EdgeDirection.class) { - { - put( - EdgeDirection.UP_LEFT, - new EdgeDirection[] { EdgeDirection.UP_RIGHT, EdgeDirection.LEFT }); - put( - EdgeDirection.UP_RIGHT, - new EdgeDirection[] { EdgeDirection.RIGHT, EdgeDirection.UP_LEFT }); - put( - EdgeDirection.RIGHT, - new EdgeDirection[] { EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT }); - put( - EdgeDirection.DOWN_RIGHT, - new EdgeDirection[] { EdgeDirection.DOWN_LEFT, EdgeDirection.RIGHT }); - put( - EdgeDirection.DOWN_LEFT, - new EdgeDirection[] { EdgeDirection.LEFT, EdgeDirection.DOWN_RIGHT }); - put( - EdgeDirection.LEFT, - new EdgeDirection[] { EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT }); - } - }; + private static final EnumMap ROOT_OFFSETS = + new EnumMap(EdgeDirection.class) { + { + put( + EdgeDirection.UP_LEFT, + new EdgeDirection[] {EdgeDirection.UP_RIGHT, EdgeDirection.LEFT}); + put( + EdgeDirection.UP_RIGHT, + new EdgeDirection[] {EdgeDirection.RIGHT, EdgeDirection.UP_LEFT}); + put( + EdgeDirection.RIGHT, + new EdgeDirection[] {EdgeDirection.DOWN_RIGHT, EdgeDirection.UP_RIGHT}); + put( + EdgeDirection.DOWN_RIGHT, + new EdgeDirection[] {EdgeDirection.DOWN_LEFT, EdgeDirection.RIGHT}); + put( + EdgeDirection.DOWN_LEFT, + new EdgeDirection[] {EdgeDirection.LEFT, EdgeDirection.DOWN_RIGHT}); + put( + EdgeDirection.LEFT, + new EdgeDirection[] {EdgeDirection.UP_LEFT, EdgeDirection.DOWN_LEFT}); + } + }; /** Array positions for edge directions. */ - private static final EdgeDirection[][] DIRS = new EdgeDirection[][] { - { + private static final EdgeDirection[][] DIRS = + new EdgeDirection[][] { + { EdgeDirection.UNDEFINED, EdgeDirection.UP_LEFT, EdgeDirection.UNDEFINED, EdgeDirection.UP_RIGHT, EdgeDirection.UNDEFINED - }, - { + }, + { EdgeDirection.LEFT, EdgeDirection.UNDEFINED, EdgeDirection.UNDEFINED, EdgeDirection.UNDEFINED, EdgeDirection.RIGHT - }, - { + }, + { EdgeDirection.UNDEFINED, EdgeDirection.DOWN_LEFT, EdgeDirection.UNDEFINED, EdgeDirection.DOWN_RIGHT, EdgeDirection.UNDEFINED, - } - }; + } + }; /** Length of edge. */ private final double edgeLength; @@ -210,22 +207,22 @@ void createPattern(Graph graph) { int row = calcRow(i, j, offset); if (col == 0 && row == 4) { - edges.add(new int[] { i, j, k, i + 2, j, k }); + edges.add(new int[] {i, j, k, i + 2, j, k}); } else if (col == 5 && row == 1) { - edges.add(new int[] { i, j, k, i + 2, j, k }); + edges.add(new int[] {i, j, k, i + 2, j, k}); } else if (col == 7 && row == 1) { - edges.add(new int[] { i, j, k, i + 2, j, k }); + edges.add(new int[] {i, j, k, i + 2, j, k}); } else if (col == 3 && row == 3) { - edges.add(new int[] { i, j, k, i + 1, j - 1, k }); + edges.add(new int[] {i, j, k, i + 1, j - 1, k}); } else if (col == 4 && row == 2) { - edges.add(new int[] { i, j, k, i + 1, j - 1, k }); + edges.add(new int[] {i, j, k, i + 1, j - 1, k}); } else if (col == 3 && row == 5) { - edges.add(new int[] { i, j, k, i + 1, j + 1, k }); + edges.add(new int[] {i, j, k, i + 1, j + 1, k}); } else if (col == 4 && row == 0) { - edges.add(new int[] { i, j, k, i + 1, j + 1, k }); + edges.add(new int[] {i, j, k, i + 1, j + 1, k}); } else if (col == 2 && row == 4) { - edges.add(new int[] { i, j, k, i + 1, j - 1, k }); - edges.add(new int[] { i, j, k, i + 1, j + 1, k }); + edges.add(new int[] {i, j, k, i + 1, j - 1, k}); + edges.add(new int[] {i, j, k, i + 1, j + 1, k}); } } } @@ -243,16 +240,17 @@ void createPattern(Graph graph) { // Add edge to graph. SiteNode from = new SiteNode(e[0], e[1], e[2]); SiteNode to = new SiteNode(e[3], e[4], e[5]); - EdgeType type = (e[0] == thresh - ? EdgeType.CAPILLARY - : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); + EdgeType type = + (e[0] == thresh + ? EdgeType.CAPILLARY + : (e[0] < thresh ? EdgeType.ARTERY : EdgeType.VEIN)); SiteEdge edge = new SiteEdge(from, to, type, EdgeLevel.VARIABLE); graph.addEdge(edge); } } // Traverse graph from leftmost nodes to identify unnecessary edges. - int[] offsets = new int[] { 0, 1, 0 }; + int[] offsets = new int[] {0, 1, 0}; for (int k = 0; k < latticeHeight; k += 2) { int offset = calcOffset(k); int ro = offsets[offset]; @@ -341,23 +339,26 @@ EdgeDirection[] createRootOffsets( // Get direction list. switch (border) { case LEFT: - directions = new EdgeDirection[] { - EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT - }; + directions = + new EdgeDirection[] { + EdgeDirection.UP_RIGHT, EdgeDirection.RIGHT, EdgeDirection.DOWN_RIGHT + }; index = 1; break; case RIGHT: - directions = new EdgeDirection[] { - EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT - }; + directions = + new EdgeDirection[] { + EdgeDirection.UP_LEFT, EdgeDirection.LEFT, EdgeDirection.DOWN_LEFT + }; index = 1; break; case TOP: - directions = new EdgeDirection[] { EdgeDirection.DOWN_RIGHT, EdgeDirection.DOWN_LEFT }; + directions = + new EdgeDirection[] {EdgeDirection.DOWN_RIGHT, EdgeDirection.DOWN_LEFT}; index = 0; break; case BOTTOM: - directions = new EdgeDirection[] { EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT }; + directions = new EdgeDirection[] {EdgeDirection.UP_RIGHT, EdgeDirection.UP_LEFT}; index = 0; break; default: diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 8146fd790..10aff184f 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -17,7 +17,6 @@ import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; - import static arcade.core.util.Graph.Edge; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; @@ -124,16 +123,12 @@ static EdgeType parseType(String type) { /** * Calculates node pressure for a given radius. * - *

- * Equation based on the paper: Welter M, Fredrich T, Rinneberg H, and Rieger H. - * (2016). - * Computational model for tumor oxygenation applied to clinical data on breast - * tumor hemoglobin - * concentrations suggests vascular dilatation and compression. PLOS - * ONE, 11(8), + *

Equation based on the paper: Welter M, Fredrich T, Rinneberg H, and Rieger H. (2016). + * Computational model for tumor oxygenation applied to clinical data on breast tumor hemoglobin + * concentrations suggests vascular dilatation and compression. PLOS ONE, 11(8), * e0161267. * - * @param radius the radius of the edge + * @param radius the radius of the edge * @param category the edge category * @return the edge pressure */ @@ -144,11 +139,8 @@ static double calculatePressure(double radius, EdgeCategory category) { /** * Calculates relative viscosity for a given radius. * - *

- * Equation based on the paper: Pries AR, Secomb TW, Gessner T, Sperandio MB, - * Gross JF, and - * Gaehtgens P. (1994). Resistance to blood flow in microvessels in vivo. - * Circulation + *

Equation based on the paper: Pries AR, Secomb TW, Gessner T, Sperandio MB, Gross JF, and + * Gaehtgens P. (1994). Resistance to blood flow in microvessels in vivo. Circulation * Research, 75(5), 904-915. * * @param radius the radius of the edge @@ -156,9 +148,10 @@ static double calculatePressure(double radius, EdgeCategory category) { */ private static double calculateViscosity(double radius) { double diameter = 2 * radius; - double mu45 = 6 * Math.exp(-0.085 * diameter) - + 3.2 - - 2.44 * Math.exp(-0.06 * Math.pow(diameter, 0.645)); + double mu45 = + 6 * Math.exp(-0.085 * diameter) + + 3.2 + - 2.44 * Math.exp(-0.06 * Math.pow(diameter, 0.645)); double fR = Math.pow(diameter / (diameter - 1.1), 2); return (1 + (mu45 - 1) * fR) * fR; } @@ -205,7 +198,7 @@ private static double getSaturation(double pressure) { /** * Gets the total amount of oxygen in blood (fmol/um3). * - * @param pressure the oxygen partial pressure + * @param pressure the oxygen partial pressure * @param solubility the oxygen solubility in blood * @return the total amount of oxygen */ @@ -217,7 +210,7 @@ static double getTotal(double pressure, double solubility) { * Gets the maximum (for arteries) or minimum (for veins) pressure across roots. * * @param roots the list of roots - * @param type the root type + * @param type the root type * @return the root pressure */ private static double getRootPressure(ArrayList roots, EdgeCategory type) { @@ -242,12 +235,10 @@ private static double getRootPressure(ArrayList roots, EdgeCategory type) /** * Sets the pressure of roots. * - *

- * Method assumes that the root node has already been set to the correct node - * object. + *

Method assumes that the root node has already been set to the correct node object. * * @param roots the list of roots - * @param type the root type + * @param type the root type * @return the pressure assigned to the roots */ static double setRootPressures(ArrayList roots, EdgeCategory type) { @@ -262,9 +253,9 @@ static double setRootPressures(ArrayList roots, EdgeCategory type) { /** * Sets the pressure of leaves. * - * @param graph the graph object + * @param graph the graph object * @param arteryPressure the pressure at the arteries - * @param veinPressure the pressure at the veins + * @param veinPressure the pressure at the veins */ static void setLeafPressures(Graph graph, double arteryPressure, double veinPressure) { for (Object obj : graph.getAllEdges()) { @@ -305,9 +296,9 @@ static boolean reversePressures(Graph graph) { /** * Marks edges that perfused between given arteries and veins. * - * @param graph the graph object + * @param graph the graph object * @param arteries the list of arteries - * @param veins the list of veins + * @param veins the list of veins */ static void checkPerfused(Graph graph, ArrayList arteries, ArrayList veins) { // Reset all edges. @@ -400,9 +391,7 @@ static void mergeGraphs(Graph graph1, Graph graph2) { /** * Calculates pressures at nodes. * - *

- * Sets up a system of linear equations for the current graph structure using - * mass balances + *

Sets up a system of linear equations for the current graph structure using mass balances * at each node. * * @param graph the graph object @@ -549,9 +538,7 @@ static void calculateStresses(Graph graph) { } } - /** - * Solve for Radius while maintaining mass balance - */ + /** Solve for Radius while maintaining mass balance */ static double calculateLocalFlow(double radius, ArrayList edges, double deltaP) { double length = 0; @@ -567,8 +554,7 @@ static double calculateLocalFlow(double radius, double length, double deltaP) { } /** - * Calculates flow rate (in um3/min) and area (in um2) for - * all edges. + * Calculates flow rate (in um3/min) and area (in um2) for all edges. * * @param graph the graph object */ @@ -619,8 +605,8 @@ static double calculateThickness(SiteEdge edge) { * Gets in degree for edge in given calculation direction. * * @param graph the graph object - * @param edge the edge object - * @param dir the calculation direction + * @param edge the edge object + * @param dir the calculation direction * @return the edge in degree */ private static int getInDegree(Graph graph, SiteEdge edge, Strategy dir) { @@ -638,8 +624,8 @@ private static int getInDegree(Graph graph, SiteEdge edge, Strategy dir) { * Gets out degree for edge in given calculation direction. * * @param graph the graph object - * @param edge the edge object - * @param dir the calculation direction + * @param edge the edge object + * @param dir the calculation direction * @return the edge out degree */ private static int getOutDegree(Graph graph, SiteEdge edge, Strategy dir) { @@ -656,11 +642,11 @@ private static int getOutDegree(Graph graph, SiteEdge edge, Strategy dir) { /** * Calculate the radii (in um) using Murray's law. * - * @param graph the graph object - * @param edge the starting edge for the calculation - * @param dir the direction of the calculation + * @param graph the graph object + * @param edge the starting edge for the calculation + * @param dir the direction of the calculation * @param fromcheck the number of edges in to the selected node - * @param tocheck the number of edges out of the selected node + * @param tocheck the number of edges out of the selected node * @return the list of children edges */ private static ArrayList calculateRadius( @@ -698,17 +684,19 @@ private static ArrayList calculateRadius( if (in == 1 && out == 1 && edge.radius != 0) { e.radius = edge.radius; } else if (in == fromcheck && out == tocheck) { - ArrayList b = (dir == Strategy.DOWNSTREAM ? edge.getEdgesOut() : edge.getEdgesIn()); + ArrayList b = + (dir == Strategy.DOWNSTREAM ? edge.getEdgesOut() : edge.getEdgesIn()); double r1 = ((SiteEdge) b.get(0)).radius; double r2 = ((SiteEdge) b.get(1)).radius; if (e.radius == 0 && edge.radius != 0) { if (r1 == 0 && r2 != 0) { if (edge.radius > r2) { - e.radius = Math.pow( - Math.pow(edge.radius, MURRAY_EXPONENT) - - Math.pow(r2, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = + Math.pow( + Math.pow(edge.radius, MURRAY_EXPONENT) + - Math.pow(r2, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - r2) < DELTA_TOLERANCE) { e.radius = r2; } @@ -716,10 +704,11 @@ private static ArrayList calculateRadius( e.radius = MINIMUM_CAPILLARY_RADIUS; } } else if (edge.radius < r2) { - e.radius = Math.pow( - Math.pow(r2, MURRAY_EXPONENT) - - Math.pow(edge.radius, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = + Math.pow( + Math.pow(r2, MURRAY_EXPONENT) + - Math.pow(edge.radius, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - edge.radius) < DELTA_TOLERANCE) { e.radius = edge.radius; } @@ -731,10 +720,11 @@ private static ArrayList calculateRadius( } } else if (r1 != 0 && r2 == 0) { if (edge.radius > r1) { - e.radius = Math.pow( - Math.pow(edge.radius, MURRAY_EXPONENT) - - Math.pow(r1, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = + Math.pow( + Math.pow(edge.radius, MURRAY_EXPONENT) + - Math.pow(r1, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - r1) < DELTA_TOLERANCE) { e.radius = r1; } @@ -742,10 +732,11 @@ private static ArrayList calculateRadius( e.radius = MINIMUM_CAPILLARY_RADIUS; } } else if (edge.radius < r1) { - e.radius = Math.pow( - Math.pow(r1, MURRAY_EXPONENT) - - Math.pow(edge.radius, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = + Math.pow( + Math.pow(r1, MURRAY_EXPONENT) + - Math.pow(edge.radius, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); if (Math.abs(e.radius - edge.radius) < DELTA_TOLERANCE) { e.radius = edge.radius; } @@ -767,9 +758,10 @@ private static ArrayList calculateRadius( double r1 = ((SiteEdge) b.get(0)).radius; double r2 = ((SiteEdge) b.get(1)).radius; if (r1 != 0 && r2 != 0) { - e.radius = Math.pow( - Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = + Math.pow( + Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); } } @@ -789,11 +781,11 @@ private static ArrayList calculateRadius( /** * Assigns the radii (in um) using Murray's law without splits. * - * @param graph the graph object - * @param edge the starting edge for the calculation - * @param dir the direction of the calculation + * @param graph the graph object + * @param edge the starting edge for the calculation + * @param dir the direction of the calculation * @param fromcheck the number of edges in to the selected node - * @param tocheck the number of edges out of the selected node + * @param tocheck the number of edges out of the selected node * @return the list of children edges */ private static ArrayList assignRadius( @@ -832,9 +824,10 @@ private static ArrayList assignRadius( double r1 = ((SiteEdge) b.get(0)).radius; double r2 = ((SiteEdge) b.get(1)).radius; if (r1 != 0 && r2 != 0) { - e.radius = Math.pow( - Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), - 1 / MURRAY_EXPONENT); + e.radius = + Math.pow( + Math.pow(r1, MURRAY_EXPONENT) + Math.pow(r2, MURRAY_EXPONENT), + 1 / MURRAY_EXPONENT); } } @@ -849,9 +842,9 @@ private static ArrayList assignRadius( /** * Traverses through the graph and marks visited nodes. * - * @param graph the graph object - * @param gs the graph sites object - * @param node the starting node for the traversal + * @param graph the graph object + * @param gs the graph sites object + * @param node the starting node for the traversal * @param splitCol the column in the pattern layout * @param splitRow the row in the pattern layout */ @@ -898,7 +891,7 @@ static void visit( * * @param graph the graph object * @param start the start node - * @param end the end node + * @param end the end node */ static void path(Graph graph, SiteNode start, SiteNode end) { // Reset all distances. @@ -967,18 +960,22 @@ static void path(Graph graph, SiteNode start, SiteNode end) { } } - static ArrayList getPath(Graph graph, SiteNode start, SiteNode end){ + static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { path(graph, start, end); ArrayList path = new ArrayList<>(); SiteNode node = end; while (node != null && node != start) { Bag b = graph.getEdgesIn(node); - if (b.numObjs == 1) { path.add((SiteEdge)b.objs[0]); } - else if (b.numObjs == 2) { - SiteEdge edgeA = ((SiteEdge)b.objs[0]); - SiteEdge edgeB = ((SiteEdge)b.objs[1]); - if (edgeA.getFrom() == node.prev) { path.add(edgeA); } - else { path.add(edgeB); } + if (b.numObjs == 1) { + path.add((SiteEdge) b.objs[0]); + } else if (b.numObjs == 2) { + SiteEdge edgeA = ((SiteEdge) b.objs[0]); + SiteEdge edgeB = ((SiteEdge) b.objs[1]); + if (edgeA.getFrom() == node.prev) { + path.add(edgeA); + } else { + path.add(edgeB); + } } node = node.prev; } @@ -991,7 +988,7 @@ else if (b.numObjs == 2) { * * @param graph the graph object * @param start the start node - * @param path the list of edges in the path + * @param path the list of edges in the path */ static void traverse(Graph graph, SiteNode start, ArrayList path) { Bag bag = graph.getEdgesOut(start); @@ -1020,8 +1017,8 @@ static void traverse(Graph graph, SiteNode start, ArrayList path) { * Updates radii for the graph using Murray's law without variation. * * @param graph the graph object - * @param list the list of edges - * @param code the update code + * @param list the list of edges + * @param code the update code */ static void updateRadii(Graph graph, ArrayList list, CalculationType code) { updateRadii(graph, list, code, null); @@ -1030,14 +1027,12 @@ static void updateRadii(Graph graph, ArrayList list, CalculationType c /** * Updates radii for the graph using Murray's law. * - *

- * The graph sites object contains an instance of a random number generator in - * order to + *

The graph sites object contains an instance of a random number generator in order to * ensure simulations with the same random seed are the same. * - * @param graph the graph object - * @param list the list of edges - * @param code the update code + * @param graph the graph object + * @param list the list of edges + * @param code the update code * @param random the random number generator */ static void updateRadii( @@ -1222,14 +1217,12 @@ static void updateGraph(Graph graph) { } /** - * Iterates through nodes to eliminate low flow edges preventing graph - * traversal. + * Iterates through nodes to eliminate low flow edges preventing graph traversal. * - * @param graph the graph object - * @param nodes the set of nodes - * @param removeMin {@code true} if minimum flow edges should be removed, - * {@code false} - * otherwise + * @param graph the graph object + * @param nodes the set of nodes + * @param removeMin {@code true} if minimum flow edges should be removed, {@code false} + * otherwise */ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean removeMin) { double minFlow = Double.MAX_VALUE; From 363d67f16660284c6f35eaa1eb5ab25bebef66fa Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:18:27 -0700 Subject: [PATCH 05/56] updated core graph utilities --- src/arcade/core/util/Graph.java | 31 ++++++++++++++ test/arcade/core/util/GraphTest.java | 62 ++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index ca950cd7b..42ade568b 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -95,10 +95,24 @@ public boolean contains(Edge edge) { return checkEdge(edge); } + /** + * Retrieve the node object for the given coordinates. Returns null if no node exists. + * + * @param x + * @param y + * @param z + * @return the node object + */ public Node lookup(int x, int y, int z) { return nodes.get("(" + x + "," + y + "," + z + ")"); } + /** + * Retrieve the node object for the given coordinates. Returns null if no node exists. + * + * @param node to lookup an original node + * @return the node object + */ public Node lookup(Node node) { return nodes.get(node.toString()); } @@ -383,6 +397,23 @@ public void removeEdge(Edge edge) { unsetOutMap(edge.getFrom(), edge); unsetInMap(edge.getTo(), edge); unsetLinks(edge); + removeNodes(edge); + } + + /** + * Remove any detached nodes from the graph. + * + * @param edge + */ + private void removeNodes(Edge edge) { + Node from = edge.getFrom(); + Node to = edge.getTo(); + if (!nodeToInBag.containsKey(from) && !nodeToOutBag.containsKey(from)) { + nodes.remove(from.toString()); + } + if (!nodeToInBag.containsKey(to) && !nodeToOutBag.containsKey(to)) { + nodes.remove(to.toString()); + } } /** diff --git a/test/arcade/core/util/GraphTest.java b/test/arcade/core/util/GraphTest.java index 683577224..ee55542da 100644 --- a/test/arcade/core/util/GraphTest.java +++ b/test/arcade/core/util/GraphTest.java @@ -861,4 +861,66 @@ public void graph_toString_returnsString() { assertEquals(expected, graph.toString()); } + + @Test + public void lookup_returnsNode() { + Graph graph = new Graph(); + Node node0 = new Node(-1, 0, 0); + Node node1 = new Node(0, 0, 0); + Node node2 = new Node(3, 0, 0); + + Edge edge0 = new Edge(node0, node1); + Edge edge1 = new Edge(node1, node2); + + graph.addEdge(edge0); + graph.addEdge(edge1); + + Node node0Copy = new Node(-1, 0, 0); + Node node1Copy = new Node(0, 0, 0); + Node node2Copy = new Node(3, 0, 0); + + assertAll( + () -> assertFalse(node0Copy == graph.lookup(-1, 0, 0)), + () -> assertFalse(node1Copy == graph.lookup(0, 0, 0)), + () -> assertFalse(node2Copy == graph.lookup(3, 0, 0)), + () -> assertTrue(graph.lookup(node0Copy) == graph.lookup(-1, 0, 0)), + () -> assertTrue(graph.lookup(node1Copy) == graph.lookup(0, 0, 0)), + () -> assertTrue(graph.lookup(node2Copy) == graph.lookup(3, 0, 0))); + } + + @Test + public void lookup_returnsNull() { + Graph graph = new Graph(); + Node node0 = new Node(-1, 0, 0); + Node node1 = new Node(0, 0, 0); + Node node2 = new Node(3, 0, 0); + + Edge edge0 = new Edge(node0, node1); + Edge edge1 = new Edge(node1, node2); + + graph.addEdge(edge0); + graph.addEdge(edge1); + + assertAll( + () -> assertNull(graph.lookup(-2, 0, 0)), () -> assertNull(graph.lookup(4, 0, 0))); + } + + @Test + public void lookup_returnsNullAfterEdgeIsRemoved() { + Graph graph = new Graph(); + Node node0 = new Node(-1, 0, 0); + Node node1 = new Node(0, 0, 0); + Node node2 = new Node(3, 0, 0); + + Edge edge0 = new Edge(node0, node1); + Edge edge1 = new Edge(node1, node2); + + graph.addEdge(edge0); + graph.addEdge(edge1); + graph.removeEdge(edge1); + + assertAll( + () -> assertNotNull(graph.lookup(0, 0, 0)), + () -> assertNull(graph.lookup(3, 0, 0))); + } } From 74c0da242179f59970c4913ea65cea11e65ae77a Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:23:32 -0700 Subject: [PATCH 06/56] doc string --- src/arcade/core/util/Graph.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 42ade568b..459c6014f 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -791,7 +791,8 @@ public static class Edge { private final ArrayList edgesOut; /** - * Creates an {@code Edge} between two {@link Node} objects. + * Creates an {@code Edge} between two {@link Node} objects. Default behavior is to + * duplicate nodes. * * @param from the node the edge is from * @param to the node the edge is to @@ -803,6 +804,14 @@ public Edge(Node from, Node to) { edgesOut = new ArrayList<>(); } + /** + * Creates an {@code Edge} object for graph sites. Used in cases where a new node object is + * not needed. + * + * @param from the node the edge is from + * @param to the node the edge is to + * @param duplicate {@code true} if nodes should be duplicated, {@code false} otherwise + */ public Edge(Node from, Node to, boolean duplicate) { this.from = duplicate ? from.duplicate() : from; this.to = duplicate ? to.duplicate() : to; From 86ba44414001e1ee7a259968592ab08ee59d31f5 Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:30:19 -0700 Subject: [PATCH 07/56] bisection precision tests --- test/arcade/core/util/SolverTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/arcade/core/util/SolverTest.java b/test/arcade/core/util/SolverTest.java index 81dcd7ef5..da07ea396 100644 --- a/test/arcade/core/util/SolverTest.java +++ b/test/arcade/core/util/SolverTest.java @@ -236,4 +236,21 @@ public void testBisection_quadraticFunctionAndSwappedInputs_returnsAnswer() { assertEquals(1.41421, result, 0.0001); } + + @Test + public void testBisection_exceedsMaxIterations_returnsNaN() { + Function f = (x) -> x * x - 2; + double result = Solver.bisection(f, 2, 0, 2); + + assertEquals(Double.NaN, result, 0.001); + } + + @Test + public void testBisection_givenPrecision_returnsAnswersWithinPrecision() { + Function f = (x) -> x * x - 2; + double result = Solver.bisection(f, 2, 0, 1E-3); + + assertEquals(1.41421, result, 1E-3); + assertNotEquals(1.41421, result, 1E-4); + } } From 3e15f8bbc2e24d4a4cd8b7ddcd9baa322318f018 Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:37:25 -0700 Subject: [PATCH 08/56] slight refactor --- src/arcade/core/util/Graph.java | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 459c6014f..86fa29dd3 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -98,9 +98,9 @@ public boolean contains(Edge edge) { /** * Retrieve the node object for the given coordinates. Returns null if no node exists. * - * @param x - * @param y - * @param z + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate * @return the node object */ public Node lookup(int x, int y, int z) { @@ -397,22 +397,18 @@ public void removeEdge(Edge edge) { unsetOutMap(edge.getFrom(), edge); unsetInMap(edge.getTo(), edge); unsetLinks(edge); - removeNodes(edge); + removeNode(edge.getFrom()); + removeNode(edge.getTo()); } /** - * Remove any detached nodes from the graph. + * Remove any detached nodes from the graph when removing an edge. * - * @param edge + * @param Node the edge with nodes to check */ - private void removeNodes(Edge edge) { - Node from = edge.getFrom(); - Node to = edge.getTo(); - if (!nodeToInBag.containsKey(from) && !nodeToOutBag.containsKey(from)) { - nodes.remove(from.toString()); - } - if (!nodeToInBag.containsKey(to) && !nodeToOutBag.containsKey(to)) { - nodes.remove(to.toString()); + private void removeNode(Node node) { + if (!nodeToInBag.containsKey(node) && !nodeToOutBag.containsKey(node)) { + nodes.remove(node.toString()); } } From e1465437775b2178088d1f30382e8a42fe1f8b85 Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:39:07 -0700 Subject: [PATCH 09/56] renamed function --- src/arcade/core/util/Graph.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 86fa29dd3..aa715a843 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -397,16 +397,16 @@ public void removeEdge(Edge edge) { unsetOutMap(edge.getFrom(), edge); unsetInMap(edge.getTo(), edge); unsetLinks(edge); - removeNode(edge.getFrom()); - removeNode(edge.getTo()); + removeNodeIfDetached(edge.getFrom()); + removeNodeIfDetached(edge.getTo()); } /** - * Remove any detached nodes from the graph when removing an edge. + * Remove a node if it is detached from the graph. * - * @param Node the edge with nodes to check + * @param Node the node to check */ - private void removeNode(Node node) { + private void removeNodeIfDetached(Node node) { if (!nodeToInBag.containsKey(node) && !nodeToOutBag.containsKey(node)) { nodes.remove(node.toString()); } From e9d09f9f0196e9455786dc407444b95efe098303 Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:41:52 -0700 Subject: [PATCH 10/56] capitalization error --- src/arcade/core/util/Graph.java | 2 +- .../patch/env/component/PatchComponentSitesGraphFactory.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index aa715a843..61163b661 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -404,7 +404,7 @@ public void removeEdge(Edge edge) { /** * Remove a node if it is detached from the graph. * - * @param Node the node to check + * @param node the node to check */ private void removeNodeIfDetached(Node node) { if (!nodeToInBag.containsKey(node) && !nodeToOutBag.containsKey(node)) { diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java index f2ec3a483..2ca0ecb48 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java @@ -62,7 +62,6 @@ public enum EdgeType { /** Code for angiogenic edge type. */ ANGIOGENIC(EdgeCategory.CAPILLARY); - /** Code for capillary edge type. */ /** Edge category corresponding to the edge type. */ final EdgeCategory category; From 17f8d9cb519bb25e35e180914fc0fb0bc7931bd3 Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 9 Jun 2025 17:49:39 -0700 Subject: [PATCH 11/56] java docs --- .../component/PatchComponentSitesGraph.java | 4 +- .../PatchComponentSitesGraphFactory.java | 6 +-- .../PatchComponentSitesGraphUtilities.java | 39 +++++++++++++++---- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 1033694c8..5ee88bddf 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -408,7 +408,9 @@ public static class SiteNode extends Node { /** Direction of the angiogenic sprout. */ EdgeDirection sproutDir; - /** {@ True} if the angiogenic sprrout is anastomotic/perfused, {@code false} otherwise. */ + /** + * {@code true} if the angiogenic sprout is anastomotic/perfused, {@code false} otherwise. + */ boolean anastomosis; /** Parent node. */ diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java index 2ca0ecb48..857acce57 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java @@ -259,7 +259,7 @@ public PatchComponentSitesGraphFactory(Series series) { * Gets the opposite direction of the given edge. * * @param edge the edge object - * @param scale the graph resolution level + * @param level the graph resolution level * @return the code for the opposite edge direction */ public EdgeDirection getOppositeDirection(SiteEdge edge, EdgeLevel level) { @@ -704,8 +704,6 @@ public Graph initializeRootGraph(MersenneTwisterFast random, String graphLayout) * Updates hemodynamic properties for graph sites with root layouts. * * @param graph the graph instance - * @param arteries the list of artery edges - * @param veins the list of vein edges * @param level the graph resolution level * @param random the random number generator */ @@ -834,8 +832,6 @@ private void updateRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFast r * Refines the graph for graph sites with root layouts. * * @param graph the graph instance - * @param arteries the list of artery edges - * @param veins the list of vein edges */ private void refineRootGraph(Graph graph) { // Reverse edges that are veins and venules. diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 10aff184f..53db19804 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -7,16 +7,9 @@ import sim.util.Bag; import ec.util.MersenneTwisterFast; import arcade.core.util.Graph; -import arcade.core.util.Graph.Edge; import arcade.core.util.Graph.Strategy; import arcade.core.util.Matrix; import arcade.core.util.Solver; -import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; -import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; -import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeCategory; -import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; -import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; -import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; import static arcade.core.util.Graph.Edge; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; @@ -166,6 +159,13 @@ private static double getCoefficient(SiteEdge edge) { return getCoefficient(edge.radius, edge.length); } + /** + * Gets flow rate coefficient in units of um3/(mmHg min). + * + * @param radius the edge radius + * @param length the edge length + * @return the flow rate coefficient + */ private static double getCoefficient(double radius, double length) { double mu = PLASMA_VISCOSITY * calculateViscosity(radius) / 60; return (Math.PI * Math.pow(radius, 4)) / (8 * mu * length); @@ -538,7 +538,14 @@ static void calculateStresses(Graph graph) { } } - /** Solve for Radius while maintaining mass balance */ + /** + * Calculate the the flow rate for a given set of edges without any branches. + * + * @param radius the radius of the edges + * @param edges the list of edges + * @param deltaP the pressure change + * @return the flow rate (in um3/min) + */ static double calculateLocalFlow(double radius, ArrayList edges, double deltaP) { double length = 0; @@ -549,6 +556,14 @@ static double calculateLocalFlow(double radius, ArrayList edges, doubl return getCoefficient(radius, length) * (deltaP); } + /** + * Calculate the the flow rate for a given edge without any branches. + * + * @param radius the radius of the edge + * @param length the length of the edge + * @param deltaP the pressure change + * @return the flow rate (in um3/min) + */ static double calculateLocalFlow(double radius, double length, double deltaP) { return getCoefficient(radius, length) * deltaP; } @@ -960,6 +975,14 @@ static void path(Graph graph, SiteNode start, SiteNode end) { } } + /** + * Get the path between two nodes in the graph. + * + * @param graph the graph object + * @param start the start node + * @param end the end node + * @return the list of edges in the path + */ static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { path(graph, start, end); ArrayList path = new ArrayList<>(); From a9fd46f79bfc029c99659a625ccff8406d4cab52 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 10 Jun 2025 13:55:17 -0700 Subject: [PATCH 12/56] placeholder docstrings --- src/arcade/core/util/Graph.java | 5 + src/arcade/core/util/Solver.java | 2 +- .../env/component/PatchComponentGrowth.java | 404 +++++++++++++++--- 3 files changed, 340 insertions(+), 71 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 61163b661..f839f5e28 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -309,6 +309,11 @@ public void addEdge(Edge edge) { addNodes(edge); } + /** + * Helper function to adds the nodes from an edge to a graph. + * + * @param edge the edge to add + */ private void addNodes(Edge edge) { Node from = edge.getFrom(); Node to = edge.getTo(); diff --git a/src/arcade/core/util/Solver.java b/src/arcade/core/util/Solver.java index a0bd6b525..9b98563ca 100644 --- a/src/arcade/core/util/Solver.java +++ b/src/arcade/core/util/Solver.java @@ -482,7 +482,7 @@ public static double bisection( * @return the root of the function */ public static double bisection(Function func, double a, double b) { - return bisection(func, a, b, MAX_ITERS, TOLERANCE); + return bisection(func, a, b, MAX_ITERS, DELTA); } /** diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 801b86c6f..5214c4fb1 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -43,18 +43,27 @@ /** * Implementation of {@link Component} for degrading graph edges. * - *

This component can only be used with {@link GraphSites}. The component is stepped every {@code - * DEGRADATION_INTERVAL} ticks. wall thickness of edges that are adjacent to a location with - * cancerous cells is decreased ({@code DEGRADATION_RATE}). Edges that are below a minimum wall - * thickness and have a shear stress below the shear threshold ({@code SHEAR_THRESHOLD}) are removed - * from the graph. At the end of a step, if no edges have been removed from the graph, then only the - * stresses in the graph are recalculated. Otherwise, all hemodynamic properties are recalculated. + *

+ * This component can only be used with {@link GraphSites}. The component is + * stepped every {@code + * DEGRADATION_INTERVAL} ticks. wall thickness of edges that are adjacent to a + * location with + * cancerous cells is decreased ({@code DEGRADATION_RATE}). Edges that are below + * a minimum wall + * thickness and have a shear stress below the shear threshold + * ({@code SHEAR_THRESHOLD}) are removed + * from the graph. At the end of a step, if no edges have been removed from the + * graph, then only the + * stresses in the graph are recalculated. Otherwise, all hemodynamic properties + * are recalculated. */ public class PatchComponentGrowth implements Component { private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); + /** Default edge level to add to the graph from this component. */ private final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_1; + /** Default edge type to add to the graph from this component. */ private final EdgeType DEFAULT_EDGE_TYPE = EdgeType.ANGIOGENIC; /** Calculation strategies. */ @@ -81,7 +90,10 @@ public enum MigrationDirection { /** Rate of migration. */ private double migrationRate; - /** Angiogenesis threshold for vegf concentration near SiteNode to initiate migration. */ + /** + * Angiogenesis threshold for vegf concentration near SiteNode to initiate + * migration. + */ private double vegfThreshold; /** Direction of migration. */ @@ -90,10 +102,16 @@ public enum MigrationDirection { /** Maximum length of migration. */ private double maxLength; + /** + * Maximum number of edges to be added to the graph from a sprout, based on + * maxLength. + */ private int maxEdges; + /** Interval at which to step the component, based on migration rate. */ private int interval; + /** The size of an edge, based on the grid size. */ private double edgeSize; /** The associated {@link GraphSites} object. */ @@ -102,15 +120,41 @@ public enum MigrationDirection { /** The {@link Graph} object representing the sites. */ private Graph graph; + /** + * Persistent map of angiographic edges, keyed by the node they originate from. + */ private HashMap> angioMap = new HashMap<>(); + + /** List of temporary edges to be added to the graph this step. */ private ArrayList tempEdges; + /** List of edges that have been added to the graph this step. */ private ArrayList added = new ArrayList<>(); + /** Number of offsets to be used in the migration. */ private int numOffsets; + + /** List of directions to be used in the migration. */ private ArrayList offsetDirections; + + /** Map of offsets to be used in the migration. */ private EnumMap offsets; + /** + * Creates a growth component for a {@link PatchComponentSitesGraph}. + * + * Loaded parameters include: + *

    + *
  • MIGRATION_RATE
  • = How quickly the tip cells migrate. + *
  • VEGF_THRESHOLD
  • = The threshold for the average VEGF concentration + * surrounding a node. + *
  • WALK_TYPE
  • = How the directional migration is to be performed. + *
  • MAX_LENGTH
  • = The maximum length of migration. + *
+ * + * @param series {@link Series} object. + * @param parameters {@link MiniBox} object. + */ public PatchComponentGrowth(Series series, MiniBox parameters) { // Set loaded parameters. migrationRate = parameters.getDouble("MIGRATION_RATE"); @@ -194,8 +238,7 @@ public void step(SimState simstate) { continue; } - EnumMap> vegfMap = - buildDirectionalVEGFMap(vegfLattice, node); + EnumMap> vegfMap = buildDirectionalVEGFMap(vegfLattice, node); if (averageDirectionalMap(vegfMap) > vegfThreshold) { angioMap.put(node, new ArrayList<>()); @@ -222,22 +265,18 @@ public void step(SimState simstate) { switch (walkType) { case RANDOM: - node.sproutDir = - performRandomWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = performRandomWalk(random, node, vegfAverages, tick, skipDirList); break; case BIASED: - node.sproutDir = - performBiasedWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = performBiasedWalk(random, node, vegfAverages, tick, skipDirList); break; case DETERMINISTIC: - node.sproutDir = - performDeterministicWalk( - random, node, vegfAverages, tick, skipDirList); + node.sproutDir = performDeterministicWalk( + random, node, vegfAverages, tick, skipDirList); break; default: - node.sproutDir = - performDeterministicWalk( - random, node, vegfAverages, tick, skipDirList); + node.sproutDir = performDeterministicWalk( + random, node, vegfAverages, tick, skipDirList); } } } @@ -338,14 +377,12 @@ public void step(SimState simstate) { } else { if (sproutNode.pressure == 0) { if (graph.getEdgesOut(sproutNode) != null) { - sproutNode = - ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); + sproutNode = ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); } } if (finalNode.pressure == 0) { if (graph.getEdgesOut(finalNode) != null) { - finalNode = - ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); + finalNode = ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); } } // path(graph, finalNode, sproutNode); @@ -382,6 +419,12 @@ public void step(SimState simstate) { } } + /** + * Checks if the node has any edges that are marked as ignored. + * + * @param node {@link SiteNode} object. + * @return {@code true} if the node has any connected ignored edges. + */ private boolean checkForIgnoredEdges(SiteNode node) { Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); @@ -404,6 +447,7 @@ private boolean checkForIgnoredEdges(SiteNode node) { return false; } + /** Criteria for skipping a node during the migration checks. */ private boolean checkNodeSkipStatus(SiteNode node, int tick) { if (angioMap.keySet().contains(node)) { return true; @@ -417,12 +461,25 @@ private boolean checkNodeSkipStatus(SiteNode node, int tick) { return false; } + /** + * Reverses all edges in the angioMap for a given node. + * + * @param node {@link SiteNode} object. + */ private void reverseAllEdges(SiteNode node) { for (SiteEdge edge : angioMap.get(node)) { edge.reverse(); } } + /** + * Private helper function for finding the key node in the angioMap for a given + * target node and skip node. + * + * @param targetNode {@link SiteNode} object. + * @param skipNode {@link SiteNode} object. + * @return {@link SiteNode} object. + */ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { for (SiteNode keyNode : angioMap.keySet()) { if (keyNode == skipNode) { @@ -438,6 +495,14 @@ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { return null; } + /** + * Private helper function for checking if an edge list contains a given target + * node. + * + * @param edgeList {@link ArrayList} of {@link SiteEdge} objects. + * @param targetNode {@link SiteNode} object. + * @return {@code true} if the edge list contains the target node. + */ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNode) { for (SiteEdge edge : edgeList) { if (edge.getTo() == targetNode) { @@ -447,6 +512,9 @@ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNo return false; } + /** + * Adds all temporary edges to the graph. + */ private void addTemporaryEdges() { tempEdges = new ArrayList<>(); for (Map.Entry> entry : angioMap.entrySet()) { @@ -456,6 +524,9 @@ private void addTemporaryEdges() { } } + /** + * Removes all temporary edges from the graph. + */ private void removeTemporaryEdges() { if (tempEdges.isEmpty()) { return; @@ -464,12 +535,28 @@ private void removeTemporaryEdges() { tempEdges.clear(); } + /** + * Removes all edges in an edge list from the graph. + * + * @param edgeList {@link ArrayList} of {@link SiteEdge} objects. + */ private void removeEdgeList(ArrayList edgeList) { for (SiteEdge edge : edgeList) { graph.removeEdge(edge); } } + /** + * Private helper function for choosing a sprout direction randomly on the from + * the node. + * + * @param random {@link MersenneTwisterFast} object. + * @param node {@link SiteNode} object. + * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. + * @param tick {@code int} object. + * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. + * @return {@link EdgeDirection} object. + */ private EdgeDirection performRandomWalk( MersenneTwisterFast random, SiteNode node, @@ -483,6 +570,18 @@ private EdgeDirection performRandomWalk( return randDir; } + /** + * Private helper function for choosing a sprout direction biased on the VEGF + * concentration around + * the node. + * + * @param random {@link MersenneTwisterFast} object. + * @param node {@link SiteNode} object. + * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. + * @param tick {@code int} object. + * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. + * @return {@link EdgeDirection} object. + */ private EdgeDirection performBiasedWalk( MersenneTwisterFast random, SiteNode node, @@ -503,6 +602,18 @@ private EdgeDirection performBiasedWalk( return offsetDirections.get(offsetDirections.size() - 1); } + /** + * Private helper function for choosing a sprout direction deterministically on + * the VEGF concentration + * around the node. + * + * @param random {@link MersenneTwisterFast} object. + * @param node {@link SiteNode} object. + * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. + * @param tick {@code int} object. + * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. + * @return {@link EdgeDirection} object. + */ private EdgeDirection performDeterministicWalk( MersenneTwisterFast random, SiteNode node, @@ -516,6 +627,15 @@ private EdgeDirection performDeterministicWalk( return maxDir; } + /** + * Private helper function for building a map of VEGF concentration values for a + * given node. + * + * @param lattice {@link Lattice} object. + * @param node {@link SiteNode} object. + * @return {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of + * double values. + */ private EnumMap> buildDirectionalVEGFMap( Lattice lattice, SiteNode node) { double[][][] field = lattice.getField(); @@ -538,6 +658,14 @@ private EnumMap> buildDirectionalVEGFMap( return vegfMap; } + /** + * Private helper function for getting the average VEGF concentration values for + * a given map. + * + * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of + * double values. + * @return {@link EnumMap} of {@link EdgeDirection} to double values. + */ private EnumMap getDirectionalAverages( EnumMap> map) { EnumMap averageMap = new EnumMap<>(EdgeDirection.class); @@ -551,6 +679,13 @@ private EnumMap getDirectionalAverages( return averageMap; } + /** + * Private helper function for getting the maximum VEGF concentration value for + * a given map. + * + * @param map {@link EnumMap} of {@link EdgeDirection} to double values. + * @return {@link EdgeDirection} object. + */ private EdgeDirection getMaxKey(EnumMap map) { EdgeDirection maxDir = EdgeDirection.UNDEFINED; double maxVal = 0; @@ -563,6 +698,12 @@ private EdgeDirection getMaxKey(EnumMap map) { return maxDir; } + /** + * Private helper function for normalizing a map of VEGF concentration values. + * + * @param map {@link EnumMap} of {@link EdgeDirection} to double values. + * @return {@link EnumMap} of {@link EdgeDirection} to double values. + */ private EnumMap normalizeDirectionalMap( EnumMap map) { EnumMap normalizedMap = new EnumMap<>(EdgeDirection.class); @@ -575,6 +716,12 @@ private EnumMap normalizeDirectionalMap( return normalizedMap; } + /** + * Private helper function for summing a map of VEGF concentration values. + * + * @param map {@link EnumMap} of {@link EdgeDirection} to double values. + * @return {@code double} object. + */ private double sumMap(EnumMap map) { double sum = 0; for (EdgeDirection dir : offsetDirections) { @@ -583,6 +730,13 @@ private double sumMap(EnumMap map) { return sum; } + /** + * Private helper function for averaging a map of VEGF concentration values. + * + * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of + * double values. + * @return {@code double} object. + */ private double averageDirectionalMap(EnumMap> map) { double sum = 0; int count = 0; @@ -595,14 +749,34 @@ private double averageDirectionalMap(EnumMap> m return sum / count; } + /** + * Private helper function for adding an edge list to the graph. + * + * @param list {@link ArrayList} of {@link SiteEdge} objects. + */ private void addEdgeList(ArrayList list) { addEdgeList(list, false); } + /** + * Private helper function for adding an edge list to the graph. + * + * @param list {@link ArrayList} of {@link SiteEdge} objects. + * @param updateProperties {@code boolean} object. + */ private void addEdgeList(ArrayList list, boolean updateProperties) { addEdgeList(list, updateProperties, DEFAULT_EDGE_TYPE); } + /** + * Private helper function for adding an edge list to the graph. + * + * @param list {@link ArrayList} of {@link SiteEdge} objects. + * @param start {@link SiteNode} object. + * @param end {@link SiteNode} object. + * @param tick {@code int} object. + * @param calc {@link Calculation} object. + */ private void addAngioEdges( ArrayList list, SiteNode start, SiteNode end, int tick, Calculation calc) { @@ -642,10 +816,9 @@ private void addAngioEdges( } for (SiteEdge edge : added) { - edge.radius = - (otherRadius > CAPILLARY_RADIUS) - ? CAPILLARY_RADIUS - : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); + edge.radius = (otherRadius > CAPILLARY_RADIUS) + ? CAPILLARY_RADIUS + : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); @@ -666,10 +839,8 @@ private void addAngioEdges( updateRootsAndRadii(added, start, end); break; case DIVERT: - SiteNode intersection = - (SiteNode) - graph.findDownstreamIntersection( - (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); + SiteNode intersection = (SiteNode) graph.findDownstreamIntersection( + (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); if (intersection != null) { recalcRadii(added, start, end, intersection); } else { @@ -679,17 +850,22 @@ private void addAngioEdges( } } + /** + * Private helper function for calculating the even split radius of an edge. + * + * @param edge {@link SiteEdge} object. + * @return {@code double} object. + */ private double calculateEvenSplitRadius(SiteEdge edge) { double radius = edge.radius; double length = edge.length; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); - double newRadius = - Solver.bisection( - (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), - 1E-6, - 5 * MAXIMUM_CAPILLARY_RADIUS, - 1E-6); + double newRadius = Solver.bisection( + (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), + 1E-6, + 5 * MAXIMUM_CAPILLARY_RADIUS, + 1E-6); // LOGGER.info("splitting radius, for checking if it happens directly before // bisection failing"); // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * @@ -700,6 +876,13 @@ private double calculateEvenSplitRadius(SiteEdge edge) { return newRadius; } + /** + * Private helper function for updating the roots and radii of an edge list. + * + * @param addedEdges {@link ArrayList} of {@link SiteEdge} objects. + * @param start {@link SiteNode} object. + * @param end {@link SiteNode} object. + */ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteNode end) { updateGraph(graph); ArrayList oldRadii = new ArrayList<>(); @@ -798,6 +981,14 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } } + /** + * Private helper function for recalculating the radii of an edge list. + * + * @param ignoredEdges {@link ArrayList} of {@link SiteEdge} objects. + * @param start {@link SiteNode} object. + * @param end {@link SiteNode} object. + * @param intersection {@link SiteNode} object. + */ private void recalcRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { @@ -841,23 +1032,21 @@ private void recalcRadii( } if (updateRadius( - (SiteEdge) edges.get(nonAngioIndex), - intersection, - divertedFlow, - true, - ignoredEdges) - == -1) { + (SiteEdge) edges.get(nonAngioIndex), + intersection, + divertedFlow, + true, + ignoredEdges) == -1) { return; } ; if (updateRadius( - (SiteEdge) edges.get(angioIndex), - intersection, - divertedFlow, - false, - ignoredEdges) - == -1) { + (SiteEdge) edges.get(angioIndex), + intersection, + divertedFlow, + false, + ignoredEdges) == -1) { // LOGGER.info("Failed to update radius when increasing size, something seems // up"); } @@ -886,6 +1075,16 @@ private void recalcRadii( } } + /** + * Private helper function for updating the radius of an edge. + * + * @param edge {@link SiteEdge} object. + * @param intersection {@link SiteNode} object. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + * @return {@code int} object. + */ private int updateRadius( SiteEdge edge, SiteNode intersection, @@ -898,10 +1097,27 @@ private int updateRadius( return updateRadiiOfEdgeList(edgesToUpdate, flow, decrease, ignored); } + /** + * Private helper function for updating the radii of an edge list. + * + * @param edges {@link ArrayList} of {@link SiteEdge} objects. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @return {@code int} object. + */ private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease) { return updateRadiiOfEdgeList(edges, flow, decrease, new ArrayList<>()); } + /** + * Private helper function for updating the radii of an edge list. + * + * @param edges {@link ArrayList} of {@link SiteEdge} objects. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + * @return {@code int} object. + */ private int updateRadiiOfEdgeList( ArrayList edges, double flow, boolean decrease, ArrayList ignored) { ArrayList oldRadii = new ArrayList<>(); @@ -918,14 +1134,20 @@ private int updateRadiiOfEdgeList( return 0; } + /** + * Private helper function for calculating the radius of an edge. + * + * @param edge {@link SiteEdge} object. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @return {@code int} object. + */ private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { int sign = decrease ? -1 : 1; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = - (double r) -> - originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); + Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); @@ -936,21 +1158,27 @@ private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { return 0; } + /** + * Private helper function for calculating the radius of an edge. + * + * @param edge {@link SiteEdge} object. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @return {@code int} object. + */ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease) { int sign = decrease ? -1 : 1; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = - (double r) -> - originalFlow - + sign * flow - - calculateLocalFlow( - r, - edge.length, - edge.getFrom().pressure - - calculatePressure(r, edge.type.category)); + Function f = (double r) -> originalFlow + + sign * flow + - calculateLocalFlow( + r, + edge.length, + edge.getFrom().pressure + - calculatePressure(r, edge.type.category)); double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); @@ -963,21 +1191,27 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease return 0; } + /** + * Private helper function for calculating the radius of an edge. + * + * @param edge {@link SiteEdge} object. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @return {@code int} object. + */ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrease) { int sign = decrease ? -1 : 1; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = - (double r) -> - originalFlow - + sign * flow - - calculateLocalFlow( - r, - edge.length, - calculatePressure(r, edge.type.category) - - edge.getTo().pressure); + Function f = (double r) -> originalFlow + + sign * flow + - calculateLocalFlow( + r, + edge.length, + calculatePressure(r, edge.type.category) + - edge.getTo().pressure); double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); if (newRadius == .5 * originalRadius @@ -991,6 +1225,15 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea return 0; } + /** + * Private helper function for updating the radius of an edge. + * + * @param edge {@link SiteEdge} object. + * @param intersection {@link SiteNode} object. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + */ private void updateRadiusToRoot( SiteEdge edge, SiteNode intersection, @@ -1026,12 +1269,25 @@ private void updateRadiusToRoot( } } + /** + * Private helper function for resetting the radii of an edge list. + * + * @param edges {@link ArrayList} of {@link SiteEdge} objects. + * @param oldRadii {@link ArrayList} of {@code double} objects. + */ private void resetRadii(ArrayList edges, ArrayList oldRadii) { for (int i = 0; i < oldRadii.size(); i++) { edges.get(i).radius = oldRadii.get(i); } } + /** + * Private helper function for adding an edge list to the graph. + * + * @param list {@link ArrayList} of {@link SiteEdge} objects. + * @param updateProperties {@code boolean} object. + * @param edgeType {@link EdgeType} object. + */ private void addEdgeList( ArrayList list, boolean updateProperties, EdgeType edgeType) { for (SiteEdge edge : list) { @@ -1039,6 +1295,14 @@ private void addEdgeList( } } + /** + * Private helper function for creating a new edge. + * + * @param direction {@link EdgeDirection} object. + * @param node {@link SiteNode} object. + * @param tick {@code int} object. + * @return {@link SiteEdge} object. + */ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node, int tick) { SiteNode proposed = sites.graphFactory.offsetNode(node, direction, DEFAULT_EDGE_LEVEL); proposed.lastUpdate = tick; From 9a2bc9139c9c8e407f4a9f95f429d04e5b8b2a70 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 10 Jun 2025 13:55:42 -0700 Subject: [PATCH 13/56] spotless --- .../env/component/PatchComponentGrowth.java | 298 +++++++++--------- 1 file changed, 144 insertions(+), 154 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 5214c4fb1..63085b5c7 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -43,19 +43,12 @@ /** * Implementation of {@link Component} for degrading graph edges. * - *

- * This component can only be used with {@link GraphSites}. The component is - * stepped every {@code - * DEGRADATION_INTERVAL} ticks. wall thickness of edges that are adjacent to a - * location with - * cancerous cells is decreased ({@code DEGRADATION_RATE}). Edges that are below - * a minimum wall - * thickness and have a shear stress below the shear threshold - * ({@code SHEAR_THRESHOLD}) are removed - * from the graph. At the end of a step, if no edges have been removed from the - * graph, then only the - * stresses in the graph are recalculated. Otherwise, all hemodynamic properties - * are recalculated. + *

This component can only be used with {@link GraphSites}. The component is stepped every {@code + * DEGRADATION_INTERVAL} ticks. wall thickness of edges that are adjacent to a location with + * cancerous cells is decreased ({@code DEGRADATION_RATE}). Edges that are below a minimum wall + * thickness and have a shear stress below the shear threshold ({@code SHEAR_THRESHOLD}) are removed + * from the graph. At the end of a step, if no edges have been removed from the graph, then only the + * stresses in the graph are recalculated. Otherwise, all hemodynamic properties are recalculated. */ public class PatchComponentGrowth implements Component { private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); @@ -90,10 +83,7 @@ public enum MigrationDirection { /** Rate of migration. */ private double migrationRate; - /** - * Angiogenesis threshold for vegf concentration near SiteNode to initiate - * migration. - */ + /** Angiogenesis threshold for vegf concentration near SiteNode to initiate migration. */ private double vegfThreshold; /** Direction of migration. */ @@ -102,10 +92,7 @@ public enum MigrationDirection { /** Maximum length of migration. */ private double maxLength; - /** - * Maximum number of edges to be added to the graph from a sprout, based on - * maxLength. - */ + /** Maximum number of edges to be added to the graph from a sprout, based on maxLength. */ private int maxEdges; /** Interval at which to step the component, based on migration rate. */ @@ -120,9 +107,7 @@ public enum MigrationDirection { /** The {@link Graph} object representing the sites. */ private Graph graph; - /** - * Persistent map of angiographic edges, keyed by the node they originate from. - */ + /** Persistent map of angiographic edges, keyed by the node they originate from. */ private HashMap> angioMap = new HashMap<>(); /** List of temporary edges to be added to the graph this step. */ @@ -143,16 +128,16 @@ public enum MigrationDirection { /** * Creates a growth component for a {@link PatchComponentSitesGraph}. * - * Loaded parameters include: + *

Loaded parameters include: + * *

    - *
  • MIGRATION_RATE
  • = How quickly the tip cells migrate. - *
  • VEGF_THRESHOLD
  • = The threshold for the average VEGF concentration - * surrounding a node. - *
  • WALK_TYPE
  • = How the directional migration is to be performed. - *
  • MAX_LENGTH
  • = The maximum length of migration. + *
  • MIGRATION_RATE = How quickly the tip cells migrate. + *
  • VEGF_THRESHOLD = The threshold for the average VEGF concentration surrounding a node. + *
  • WALK_TYPE = How the directional migration is to be performed. + *
  • MAX_LENGTH = The maximum length of migration. *
* - * @param series {@link Series} object. + * @param series {@link Series} object. * @param parameters {@link MiniBox} object. */ public PatchComponentGrowth(Series series, MiniBox parameters) { @@ -238,7 +223,8 @@ public void step(SimState simstate) { continue; } - EnumMap> vegfMap = buildDirectionalVEGFMap(vegfLattice, node); + EnumMap> vegfMap = + buildDirectionalVEGFMap(vegfLattice, node); if (averageDirectionalMap(vegfMap) > vegfThreshold) { angioMap.put(node, new ArrayList<>()); @@ -265,18 +251,22 @@ public void step(SimState simstate) { switch (walkType) { case RANDOM: - node.sproutDir = performRandomWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performRandomWalk(random, node, vegfAverages, tick, skipDirList); break; case BIASED: - node.sproutDir = performBiasedWalk(random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performBiasedWalk(random, node, vegfAverages, tick, skipDirList); break; case DETERMINISTIC: - node.sproutDir = performDeterministicWalk( - random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performDeterministicWalk( + random, node, vegfAverages, tick, skipDirList); break; default: - node.sproutDir = performDeterministicWalk( - random, node, vegfAverages, tick, skipDirList); + node.sproutDir = + performDeterministicWalk( + random, node, vegfAverages, tick, skipDirList); } } } @@ -377,12 +367,14 @@ public void step(SimState simstate) { } else { if (sproutNode.pressure == 0) { if (graph.getEdgesOut(sproutNode) != null) { - sproutNode = ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); + sproutNode = + ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); } } if (finalNode.pressure == 0) { if (graph.getEdgesOut(finalNode) != null) { - finalNode = ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); + finalNode = + ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); } } // path(graph, finalNode, sproutNode); @@ -473,11 +465,11 @@ private void reverseAllEdges(SiteNode node) { } /** - * Private helper function for finding the key node in the angioMap for a given - * target node and skip node. + * Private helper function for finding the key node in the angioMap for a given target node and + * skip node. * * @param targetNode {@link SiteNode} object. - * @param skipNode {@link SiteNode} object. + * @param skipNode {@link SiteNode} object. * @return {@link SiteNode} object. */ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { @@ -496,10 +488,9 @@ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { } /** - * Private helper function for checking if an edge list contains a given target - * node. + * Private helper function for checking if an edge list contains a given target node. * - * @param edgeList {@link ArrayList} of {@link SiteEdge} objects. + * @param edgeList {@link ArrayList} of {@link SiteEdge} objects. * @param targetNode {@link SiteNode} object. * @return {@code true} if the edge list contains the target node. */ @@ -512,9 +503,7 @@ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNo return false; } - /** - * Adds all temporary edges to the graph. - */ + /** Adds all temporary edges to the graph. */ private void addTemporaryEdges() { tempEdges = new ArrayList<>(); for (Map.Entry> entry : angioMap.entrySet()) { @@ -524,9 +513,7 @@ private void addTemporaryEdges() { } } - /** - * Removes all temporary edges from the graph. - */ + /** Removes all temporary edges from the graph. */ private void removeTemporaryEdges() { if (tempEdges.isEmpty()) { return; @@ -547,13 +534,12 @@ private void removeEdgeList(ArrayList edgeList) { } /** - * Private helper function for choosing a sprout direction randomly on the from - * the node. + * Private helper function for choosing a sprout direction randomly on the from the node. * - * @param random {@link MersenneTwisterFast} object. - * @param node {@link SiteNode} object. - * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. - * @param tick {@code int} object. + * @param random {@link MersenneTwisterFast} object. + * @param node {@link SiteNode} object. + * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. + * @param tick {@code int} object. * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. * @return {@link EdgeDirection} object. */ @@ -571,14 +557,13 @@ private EdgeDirection performRandomWalk( } /** - * Private helper function for choosing a sprout direction biased on the VEGF - * concentration around - * the node. + * Private helper function for choosing a sprout direction biased on the VEGF concentration + * around the node. * - * @param random {@link MersenneTwisterFast} object. - * @param node {@link SiteNode} object. - * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. - * @param tick {@code int} object. + * @param random {@link MersenneTwisterFast} object. + * @param node {@link SiteNode} object. + * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. + * @param tick {@code int} object. * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. * @return {@link EdgeDirection} object. */ @@ -603,14 +588,13 @@ private EdgeDirection performBiasedWalk( } /** - * Private helper function for choosing a sprout direction deterministically on - * the VEGF concentration - * around the node. + * Private helper function for choosing a sprout direction deterministically on the VEGF + * concentration around the node. * - * @param random {@link MersenneTwisterFast} object. - * @param node {@link SiteNode} object. - * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. - * @param tick {@code int} object. + * @param random {@link MersenneTwisterFast} object. + * @param node {@link SiteNode} object. + * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. + * @param tick {@code int} object. * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. * @return {@link EdgeDirection} object. */ @@ -628,13 +612,11 @@ private EdgeDirection performDeterministicWalk( } /** - * Private helper function for building a map of VEGF concentration values for a - * given node. + * Private helper function for building a map of VEGF concentration values for a given node. * * @param lattice {@link Lattice} object. - * @param node {@link SiteNode} object. - * @return {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of - * double values. + * @param node {@link SiteNode} object. + * @return {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of double values. */ private EnumMap> buildDirectionalVEGFMap( Lattice lattice, SiteNode node) { @@ -659,11 +641,9 @@ private EnumMap> buildDirectionalVEGFMap( } /** - * Private helper function for getting the average VEGF concentration values for - * a given map. + * Private helper function for getting the average VEGF concentration values for a given map. * - * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of - * double values. + * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of double values. * @return {@link EnumMap} of {@link EdgeDirection} to double values. */ private EnumMap getDirectionalAverages( @@ -680,8 +660,7 @@ private EnumMap getDirectionalAverages( } /** - * Private helper function for getting the maximum VEGF concentration value for - * a given map. + * Private helper function for getting the maximum VEGF concentration value for a given map. * * @param map {@link EnumMap} of {@link EdgeDirection} to double values. * @return {@link EdgeDirection} object. @@ -733,8 +712,7 @@ private double sumMap(EnumMap map) { /** * Private helper function for averaging a map of VEGF concentration values. * - * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of - * double values. + * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of double values. * @return {@code double} object. */ private double averageDirectionalMap(EnumMap> map) { @@ -761,7 +739,7 @@ private void addEdgeList(ArrayList list) { /** * Private helper function for adding an edge list to the graph. * - * @param list {@link ArrayList} of {@link SiteEdge} objects. + * @param list {@link ArrayList} of {@link SiteEdge} objects. * @param updateProperties {@code boolean} object. */ private void addEdgeList(ArrayList list, boolean updateProperties) { @@ -771,11 +749,11 @@ private void addEdgeList(ArrayList list, boolean updateProperties) { /** * Private helper function for adding an edge list to the graph. * - * @param list {@link ArrayList} of {@link SiteEdge} objects. + * @param list {@link ArrayList} of {@link SiteEdge} objects. * @param start {@link SiteNode} object. - * @param end {@link SiteNode} object. - * @param tick {@code int} object. - * @param calc {@link Calculation} object. + * @param end {@link SiteNode} object. + * @param tick {@code int} object. + * @param calc {@link Calculation} object. */ private void addAngioEdges( ArrayList list, SiteNode start, SiteNode end, int tick, Calculation calc) { @@ -816,9 +794,10 @@ private void addAngioEdges( } for (SiteEdge edge : added) { - edge.radius = (otherRadius > CAPILLARY_RADIUS) - ? CAPILLARY_RADIUS - : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); + edge.radius = + (otherRadius > CAPILLARY_RADIUS) + ? CAPILLARY_RADIUS + : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); @@ -839,8 +818,10 @@ private void addAngioEdges( updateRootsAndRadii(added, start, end); break; case DIVERT: - SiteNode intersection = (SiteNode) graph.findDownstreamIntersection( - (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); + SiteNode intersection = + (SiteNode) + graph.findDownstreamIntersection( + (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); if (intersection != null) { recalcRadii(added, start, end, intersection); } else { @@ -861,11 +842,12 @@ private double calculateEvenSplitRadius(SiteEdge edge) { double length = edge.length; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); - double newRadius = Solver.bisection( - (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), - 1E-6, - 5 * MAXIMUM_CAPILLARY_RADIUS, - 1E-6); + double newRadius = + Solver.bisection( + (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), + 1E-6, + 5 * MAXIMUM_CAPILLARY_RADIUS, + 1E-6); // LOGGER.info("splitting radius, for checking if it happens directly before // bisection failing"); // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * @@ -880,8 +862,8 @@ private double calculateEvenSplitRadius(SiteEdge edge) { * Private helper function for updating the roots and radii of an edge list. * * @param addedEdges {@link ArrayList} of {@link SiteEdge} objects. - * @param start {@link SiteNode} object. - * @param end {@link SiteNode} object. + * @param start {@link SiteNode} object. + * @param end {@link SiteNode} object. */ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteNode end) { updateGraph(graph); @@ -985,8 +967,8 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, * Private helper function for recalculating the radii of an edge list. * * @param ignoredEdges {@link ArrayList} of {@link SiteEdge} objects. - * @param start {@link SiteNode} object. - * @param end {@link SiteNode} object. + * @param start {@link SiteNode} object. + * @param end {@link SiteNode} object. * @param intersection {@link SiteNode} object. */ private void recalcRadii( @@ -1032,21 +1014,23 @@ private void recalcRadii( } if (updateRadius( - (SiteEdge) edges.get(nonAngioIndex), - intersection, - divertedFlow, - true, - ignoredEdges) == -1) { + (SiteEdge) edges.get(nonAngioIndex), + intersection, + divertedFlow, + true, + ignoredEdges) + == -1) { return; } ; if (updateRadius( - (SiteEdge) edges.get(angioIndex), - intersection, - divertedFlow, - false, - ignoredEdges) == -1) { + (SiteEdge) edges.get(angioIndex), + intersection, + divertedFlow, + false, + ignoredEdges) + == -1) { // LOGGER.info("Failed to update radius when increasing size, something seems // up"); } @@ -1078,11 +1062,11 @@ private void recalcRadii( /** * Private helper function for updating the radius of an edge. * - * @param edge {@link SiteEdge} object. + * @param edge {@link SiteEdge} object. * @param intersection {@link SiteNode} object. - * @param flow {@code double} object. - * @param decrease {@code boolean} object. - * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @param ignored {@link ArrayList} of {@link SiteEdge} objects. * @return {@code int} object. */ private int updateRadius( @@ -1100,8 +1084,8 @@ private int updateRadius( /** * Private helper function for updating the radii of an edge list. * - * @param edges {@link ArrayList} of {@link SiteEdge} objects. - * @param flow {@code double} object. + * @param edges {@link ArrayList} of {@link SiteEdge} objects. + * @param flow {@code double} object. * @param decrease {@code boolean} object. * @return {@code int} object. */ @@ -1112,10 +1096,10 @@ private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolea /** * Private helper function for updating the radii of an edge list. * - * @param edges {@link ArrayList} of {@link SiteEdge} objects. - * @param flow {@code double} object. + * @param edges {@link ArrayList} of {@link SiteEdge} objects. + * @param flow {@code double} object. * @param decrease {@code boolean} object. - * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + * @param ignored {@link ArrayList} of {@link SiteEdge} objects. * @return {@code int} object. */ private int updateRadiiOfEdgeList( @@ -1137,8 +1121,8 @@ private int updateRadiiOfEdgeList( /** * Private helper function for calculating the radius of an edge. * - * @param edge {@link SiteEdge} object. - * @param flow {@code double} object. + * @param edge {@link SiteEdge} object. + * @param flow {@code double} object. * @param decrease {@code boolean} object. * @return {@code int} object. */ @@ -1147,7 +1131,9 @@ private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); + Function f = + (double r) -> + originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); @@ -1161,8 +1147,8 @@ private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { /** * Private helper function for calculating the radius of an edge. * - * @param edge {@link SiteEdge} object. - * @param flow {@code double} object. + * @param edge {@link SiteEdge} object. + * @param flow {@code double} object. * @param decrease {@code boolean} object. * @return {@code int} object. */ @@ -1172,13 +1158,15 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow - + sign * flow - - calculateLocalFlow( - r, - edge.length, - edge.getFrom().pressure - - calculatePressure(r, edge.type.category)); + Function f = + (double r) -> + originalFlow + + sign * flow + - calculateLocalFlow( + r, + edge.length, + edge.getFrom().pressure + - calculatePressure(r, edge.type.category)); double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); @@ -1194,8 +1182,8 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease /** * Private helper function for calculating the radius of an edge. * - * @param edge {@link SiteEdge} object. - * @param flow {@code double} object. + * @param edge {@link SiteEdge} object. + * @param flow {@code double} object. * @param decrease {@code boolean} object. * @return {@code int} object. */ @@ -1205,13 +1193,15 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); - Function f = (double r) -> originalFlow - + sign * flow - - calculateLocalFlow( - r, - edge.length, - calculatePressure(r, edge.type.category) - - edge.getTo().pressure); + Function f = + (double r) -> + originalFlow + + sign * flow + - calculateLocalFlow( + r, + edge.length, + calculatePressure(r, edge.type.category) + - edge.getTo().pressure); double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); if (newRadius == .5 * originalRadius @@ -1228,11 +1218,11 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea /** * Private helper function for updating the radius of an edge. * - * @param edge {@link SiteEdge} object. + * @param edge {@link SiteEdge} object. * @param intersection {@link SiteNode} object. - * @param flow {@code double} object. - * @param decrease {@code boolean} object. - * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + * @param flow {@code double} object. + * @param decrease {@code boolean} object. + * @param ignored {@link ArrayList} of {@link SiteEdge} objects. */ private void updateRadiusToRoot( SiteEdge edge, @@ -1272,7 +1262,7 @@ private void updateRadiusToRoot( /** * Private helper function for resetting the radii of an edge list. * - * @param edges {@link ArrayList} of {@link SiteEdge} objects. + * @param edges {@link ArrayList} of {@link SiteEdge} objects. * @param oldRadii {@link ArrayList} of {@code double} objects. */ private void resetRadii(ArrayList edges, ArrayList oldRadii) { @@ -1283,10 +1273,10 @@ private void resetRadii(ArrayList edges, ArrayList oldRadii) { /** * Private helper function for adding an edge list to the graph. - * - * @param list {@link ArrayList} of {@link SiteEdge} objects. + * + * @param list {@link ArrayList} of {@link SiteEdge} objects. * @param updateProperties {@code boolean} object. - * @param edgeType {@link EdgeType} object. + * @param edgeType {@link EdgeType} object. */ private void addEdgeList( ArrayList list, boolean updateProperties, EdgeType edgeType) { @@ -1299,8 +1289,8 @@ private void addEdgeList( * Private helper function for creating a new edge. * * @param direction {@link EdgeDirection} object. - * @param node {@link SiteNode} object. - * @param tick {@code int} object. + * @param node {@link SiteNode} object. + * @param tick {@code int} object. * @return {@link SiteEdge} object. */ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node, int tick) { From 946889ce8677f4151539a7652c265b94d9f60dc6 Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 13 Jun 2025 11:46:43 -0700 Subject: [PATCH 14/56] added tests for register and scheduler --- .../IncompatibleFeatureException.java | 22 +++ .../env/component/PatchComponentGrowth.java | 10 +- src/arcade/patch/parameter.patch.xml | 8 + .../component/PatchComponentGrowthTest.java | 147 ++++++++++++++++++ .../PatchComponentSitesGraphTest.java | 3 + test/arcade/patch/sim/PatchSeriesTest.java | 6 +- 6 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 src/arcade/core/util/exceptions/IncompatibleFeatureException.java create mode 100644 test/arcade/patch/env/component/PatchComponentGrowthTest.java create mode 100644 test/arcade/patch/env/component/PatchComponentSitesGraphTest.java diff --git a/src/arcade/core/util/exceptions/IncompatibleFeatureException.java b/src/arcade/core/util/exceptions/IncompatibleFeatureException.java new file mode 100644 index 000000000..da417158d --- /dev/null +++ b/src/arcade/core/util/exceptions/IncompatibleFeatureException.java @@ -0,0 +1,22 @@ +package arcade.core.util.exceptions; + +/** Exception thrown when incompatible features are given. */ +public class IncompatibleFeatureException extends RuntimeException { + /** + * Constructs an {@code IncompatibleFeatureException} with the specified detail message. + * + * @param feature the feature name + * @param given the given associated feature + * @param expected the expected associated feature + */ + public IncompatibleFeatureException(String feature, String given, String expected) { + super( + String.format( + feature + + "is incompatible with " + + given + + ", must be associated with " + + expected + + ".")); + } +} diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 63085b5c7..9e83822e6 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -18,6 +18,7 @@ import arcade.core.util.MiniBox; import arcade.core.util.Solver; import arcade.core.util.Solver.Function; +import arcade.core.util.exceptions.IncompatibleFeatureException; import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; @@ -159,16 +160,17 @@ public double[][][] getField() { @Override public void schedule(Schedule schedule) { - interval = migrationRate < edgeSize ? 60 : 30; + interval = migrationRate < edgeSize ? 60 : (int) (edgeSize / migrationRate * 60); schedule.scheduleRepeating(this, Ordering.LAST_COMPONENT.ordinal() - 3, interval); } @Override - public void register(Simulation sim, String layer) { - Component component = sim.getComponent(layer); + public void register(Simulation sim, String componentID) { + Component component = sim.getComponent(componentID); if (!(component instanceof PatchComponentSitesGraph)) { - return; + throw new IncompatibleFeatureException( + "Growth Component", component.getClass().getName(), "PatchComponentSitesGraph"); } sites = (PatchComponentSitesGraph) component; diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index 40824712c..5ea4cb77a 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -155,6 +155,7 @@ + @@ -162,4 +163,11 @@ + + \ + + + + + diff --git a/test/arcade/patch/env/component/PatchComponentGrowthTest.java b/test/arcade/patch/env/component/PatchComponentGrowthTest.java new file mode 100644 index 000000000..0b9e2a8e0 --- /dev/null +++ b/test/arcade/patch/env/component/PatchComponentGrowthTest.java @@ -0,0 +1,147 @@ +package arcade.patch.env.component; + +import java.lang.reflect.Field; +import java.util.EnumMap; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import sim.engine.Schedule; +import ec.util.MersenneTwisterFast; +import arcade.core.util.MiniBox; +import arcade.core.util.Parameters; +import arcade.core.util.exceptions.IncompatibleFeatureException; +import arcade.patch.agent.process.PatchProcessMetabolism; +import arcade.patch.agent.process.PatchProcessSignaling; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; +import arcade.patch.env.grid.PatchGrid; +import arcade.patch.sim.PatchSeries; +import arcade.patch.sim.PatchSimulation; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class PatchComponentGrowthTest { + static final double EPSILON = 1E-8; + + static PatchSeries seriesMock; + + static PatchSimulation simMock; + + static Parameters parametersMock; + + static PatchProcessMetabolism metabolismMock; + + static PatchProcessSignaling signalingMock; + + static PatchGrid gridMock; + + static MersenneTwisterFast randomMock; + + @BeforeAll + public static void setup() { + + seriesMock = mock(PatchSeries.class, CALLS_REAL_METHODS); + simMock = mock(PatchSimulation.class); + doReturn(seriesMock).when(simMock).getSeries(); + parametersMock = spy(new Parameters(new MiniBox(), null, null)); + metabolismMock = mock(PatchProcessMetabolism.class); + signalingMock = mock(PatchProcessSignaling.class); + gridMock = mock(PatchGrid.class); + doReturn(gridMock).when(simMock).getGrid(); + randomMock = mock(MersenneTwisterFast.class); + simMock.random = randomMock; + } + + @Test + public void schedule_calledWithMigrationRateLowerThanEdgeSize_setsScheduleCorrectly() { + MiniBox parameters = new MiniBox(); + parameters.put("MIGRATION_RATE", 10); + parameters.put("VEGF_THRESHOLD", 0.5); + parameters.put("WALK_TYPE", "BIASED"); + parameters.put("MAX_LENGTH", 100); + + Schedule schedule = mock(Schedule.class); + + PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); + + int ds = 30; + + try { + Field field = PatchComponentGrowth.class.getDeclaredField("edgeSize"); + field.setAccessible(true); + field.set(component, ds); + } catch (Exception ignored) { + } + + component.schedule(schedule); + + verify(schedule).scheduleRepeating(component, 6, 60); + } + + @Test + public void schedule_calledWithMigrationRateGreaterThanEdgeSize_setsScheduleCorrectly() { + MiniBox parameters = new MiniBox(); + parameters.put("MIGRATION_RATE", 60); + parameters.put("VEGF_THRESHOLD", 0.5); + parameters.put("WALK_TYPE", "BIASED"); + parameters.put("MAX_LENGTH", 100); + + Schedule schedule = mock(Schedule.class); + + PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); + + int ds = 30; + + try { + Field field = PatchComponentGrowth.class.getDeclaredField("edgeSize"); + field.setAccessible(true); + field.set(component, ds); + } catch (Exception ignored) { + } + + component.schedule(schedule); + + verify(schedule).scheduleRepeating(component, 6, 30); + } + + @Test + public void register_calledWithIncompatibleFeature_throwsException() { + MiniBox parameters = new MiniBox(); + parameters.put("MIGRATION_RATE", 60); + parameters.put("VEGF_THRESHOLD", 0.5); + parameters.put("WALK_TYPE", "BIASED"); + parameters.put("MAX_LENGTH", 100); + + PatchComponentSitesPattern pattern = mock(PatchComponentSitesPattern.class); + doReturn(pattern).when(simMock).getComponent("INCOMPATIBLE"); + + PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); + + assertThrows( + IncompatibleFeatureException.class, + () -> component.register(simMock, "INCOMPATIBLE")); + } + + @Test + public void register_calledWithSitesGraphObject_doesNotThrowException() { + MiniBox parameters = new MiniBox(); + parameters.put("MIGRATION_RATE", 60); + parameters.put("VEGF_THRESHOLD", 0.5); + parameters.put("WALK_TYPE", "BIASED"); + parameters.put("MAX_LENGTH", 100); + + PatchComponentSitesGraph graph = mock(PatchComponentSitesGraph.class); + doReturn(graph).when(simMock).getComponent("COMPATIBLE"); + + try { + Field field = PatchComponentSitesGraph.class.getDeclaredField("graphFactory"); + field.setAccessible(true); + field.set(graph, mock(PatchComponentSitesGraphFactory.class)); + } catch (Exception ignored) { + } + + doReturn(new EnumMap(EdgeDirection.class)) + .when(graph.graphFactory) + .getOffsets(); + PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); + assertDoesNotThrow(() -> component.register(simMock, "COMPATIBLE")); + } +} diff --git a/test/arcade/patch/env/component/PatchComponentSitesGraphTest.java b/test/arcade/patch/env/component/PatchComponentSitesGraphTest.java new file mode 100644 index 000000000..2592890fe --- /dev/null +++ b/test/arcade/patch/env/component/PatchComponentSitesGraphTest.java @@ -0,0 +1,3 @@ +package arcade.patch.env.component; + +public class PatchComponentSitesGraphTest {} diff --git a/test/arcade/patch/sim/PatchSeriesTest.java b/test/arcade/patch/sim/PatchSeriesTest.java index 357a0e9d8..8d28989f9 100644 --- a/test/arcade/patch/sim/PatchSeriesTest.java +++ b/test/arcade/patch/sim/PatchSeriesTest.java @@ -80,9 +80,9 @@ public static void setupParameters() { PARAMETERS.addTag(PATCH_PARAMETER_NAMES[i], "PATCH"); PARAMETERS.addAtt(PATCH_PARAMETER_NAMES[i], "value", "" + PATCH_PARAMETER_VALUES[i]); } - MiniBox potts = PARAMETERS.getIdValForTag("PATCH"); - for (String key : potts.getKeys()) { - PATCH.put(key, potts.get(key)); + MiniBox patch = PARAMETERS.getIdValForTag("PATCH"); + for (String key : patch.getKeys()) { + PATCH.put(key, patch.get(key)); } // POPULATION From 253aad32916bc117b07d580cd2b12e465b038e1c Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 13 Jun 2025 13:42:00 -0700 Subject: [PATCH 15/56] added test for getPath --- .../PatchComponentSitesGraphUtilities.java | 3 + .../PatchComponentSitesGraphUtiltiesTest.java | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 test/arcade/patch/env/component/PatchComponentSitesGraphUtiltiesTest.java diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 53db19804..bd10d7e58 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -1002,6 +1002,9 @@ static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { } node = node.prev; } + if (node != start) { + return null; + } Collections.reverse(path); return path; } diff --git a/test/arcade/patch/env/component/PatchComponentSitesGraphUtiltiesTest.java b/test/arcade/patch/env/component/PatchComponentSitesGraphUtiltiesTest.java new file mode 100644 index 000000000..664ac2bc5 --- /dev/null +++ b/test/arcade/patch/env/component/PatchComponentSitesGraphUtiltiesTest.java @@ -0,0 +1,63 @@ +package arcade.patch.env.component; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; +import arcade.core.util.Graph; +import static org.junit.jupiter.api.Assertions.*; +import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; +import static arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; +import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; +import static arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; + +public class PatchComponentSitesGraphUtiltiesTest { + @Test + public void getPath_calledWithConnectedNodes_returnsPath() { + Graph graph = new Graph(); + SiteNode node0 = new SiteNode(0, 0, 0); + SiteNode node1 = new SiteNode(0, 1, 0); + SiteNode node2 = new SiteNode(0, 2, 0); + SiteNode node3 = new SiteNode(0, 3, 0); + SiteNode node4 = new SiteNode(0, 4, 0); + SiteNode node5 = new SiteNode(0, 5, 0); + SiteEdge edge0 = new SiteEdge(node0, node1, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge1 = new SiteEdge(node1, node2, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge2 = new SiteEdge(node2, node3, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge3 = new SiteEdge(node3, node4, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge4 = new SiteEdge(node4, node5, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + graph.addEdge(edge0); + graph.addEdge(edge1); + graph.addEdge(edge2); + graph.addEdge(edge3); + graph.addEdge(edge4); + + ArrayList expected = new ArrayList<>(); + expected.add(edge1); + expected.add(edge2); + expected.add(edge3); + + ArrayList actual = PatchComponentSitesGraphUtilities.getPath(graph, node1, node4); + assertIterableEquals(expected, actual); + } + + @Test + public void getPath_calledWithUnconnectedNodes_returnsNull() { + Graph graph = new Graph(); + SiteNode node0 = new SiteNode(0, 0, 0); + SiteNode node1 = new SiteNode(0, 1, 0); + SiteNode node2 = new SiteNode(0, 2, 0); + SiteNode node3 = new SiteNode(0, 3, 0); + SiteNode node4 = new SiteNode(0, 4, 0); + SiteNode node5 = new SiteNode(0, 5, 0); + SiteEdge edge0 = new SiteEdge(node0, node1, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge1 = new SiteEdge(node1, node2, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge2 = new SiteEdge(node2, node3, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + SiteEdge edge4 = new SiteEdge(node4, node5, EdgeType.CAPILLARY, EdgeLevel.LEVEL_1); + graph.addEdge(edge0); + graph.addEdge(edge1); + graph.addEdge(edge2); + graph.addEdge(edge4); + + ArrayList actual = PatchComponentSitesGraphUtilities.getPath(graph, node1, node5); + assertNull(actual); + } +} From 0ba65bfb153e1355734914e7bddcbfb850342390 Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 13 Jun 2025 16:53:42 -0700 Subject: [PATCH 16/56] cleaning up growth component --- .../env/component/PatchComponentGrowth.java | 343 ++++++++++-------- src/arcade/patch/parameter.patch.xml | 9 +- .../component/PatchComponentGrowthTest.java | 4 + 3 files changed, 191 insertions(+), 165 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 9e83822e6..93fc20585 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -44,21 +44,29 @@ /** * Implementation of {@link Component} for degrading graph edges. * - *

This component can only be used with {@link GraphSites}. The component is stepped every {@code - * DEGRADATION_INTERVAL} ticks. wall thickness of edges that are adjacent to a location with - * cancerous cells is decreased ({@code DEGRADATION_RATE}). Edges that are below a minimum wall - * thickness and have a shear stress below the shear threshold ({@code SHEAR_THRESHOLD}) are removed - * from the graph. At the end of a step, if no edges have been removed from the graph, then only the - * stresses in the graph are recalculated. Otherwise, all hemodynamic properties are recalculated. + *

This component can only be used with {@link PatchComponentsSitesGraph}. The component is + * stepped according to a reasonable interval based on the specified {@code MIGRATION_RATE}. + * Generally, if the average VEGF concentration in the immediate neighborhood of a node is greater + * than the threshold {@code VEGF_THRESHOLD} the node will be flagged for sprouting. The sprout + * direction is determined by the {@code WALK_TYPE} and the maximum length of migration is + * determined by the {@code MAX_LENGTH}. The walk types can be specified as {@code RANDOM}, {@code + * DETERMINISTIC}, or {@code BIASED}. + * + *

New edges are added based on the specified {@link CAPILLARY_RADIUS}. The calculation of the + * resulting hemodynamics is based on two strategies. The first is {@code COMPENSATE} which + * increases the flow of the original artery to compensate for the new edge. The second is {@code + * DIVERT} which calculates the diverted flow rate and pressure of the resulting edge by subtracting + * the flow rate of the new edge from the original edge. */ public class PatchComponentGrowth implements Component { + /** Logger for {@code PatchComponentGrowth}. */ private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); /** Default edge level to add to the graph from this component. */ - private final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_1; + private static final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_1; /** Default edge type to add to the graph from this component. */ - private final EdgeType DEFAULT_EDGE_TYPE = EdgeType.ANGIOGENIC; + private static final EdgeType DEFAULT_EDGE_TYPE = EdgeType.ANGIOGENIC; /** Calculation strategies. */ public enum Calculation { @@ -90,6 +98,9 @@ public enum MigrationDirection { /** Direction of migration. */ private MigrationDirection walkType; + /** Strategy for calculation of boundary conditions. */ + private Calculation calculationStrategy; + /** Maximum length of migration. */ private double maxLength; @@ -102,14 +113,14 @@ public enum MigrationDirection { /** The size of an edge, based on the grid size. */ private double edgeSize; - /** The associated {@link GraphSites} object. */ + /** The associated {@link PatchComponentSitesGraph} object. */ private PatchComponentSitesGraph sites; /** The {@link Graph} object representing the sites. */ private Graph graph; /** Persistent map of angiographic edges, keyed by the node they originate from. */ - private HashMap> angioMap = new HashMap<>(); + private HashMap> angiogenicNodeMap = new HashMap<>(); /** List of temporary edges to be added to the graph this step. */ private ArrayList tempEdges; @@ -126,6 +137,15 @@ public enum MigrationDirection { /** Map of offsets to be used in the migration. */ private EnumMap offsets; + /* Tick for the current step. */ + private int tick; + + /* Flag for whether to add edges if angiogenic nodes become perfused. */ + private boolean addFlag; + + /* List of nodes to be removed from the angiogenic node map this time step. */ + private ArrayList nodesToRemove; + /** * Creates a growth component for a {@link PatchComponentSitesGraph}. * @@ -147,6 +167,7 @@ public PatchComponentGrowth(Series series, MiniBox parameters) { vegfThreshold = parameters.getDouble("VEGF_THRESHOLD"); walkType = MigrationDirection.valueOf(parameters.get("WALK_TYPE")); maxLength = parameters.getDouble("MAX_LENGTH"); + calculationStrategy = Calculation.valueOf(parameters.get("CALCULATION_STRATEGY")); } /** @@ -185,19 +206,8 @@ public void register(Simulation sim, String componentID) { maxEdges = (int) Math.floor(maxLength / edgeSize); } - @Override - public void step(SimState simstate) { - Simulation sim = (Simulation) simstate; - int tick = (int) simstate.schedule.getTime(); - - Lattice vegfLattice = sim.getLattice("VEGF"); - - MersenneTwisterFast random = simstate.random; - - ArrayList nodesToRemove = new ArrayList<>(); - + private LinkedHashSet getValidNodes() { LinkedHashSet set = new LinkedHashSet<>(); - for (Object obj : graph.getAllEdges()) { SiteEdge edge = (SiteEdge) obj; if (edge.isIgnored) { @@ -219,8 +229,37 @@ public void step(SimState simstate) { set.add(to); } } + return set; + } - for (SiteNode node : set) { + /** Strategy pattern for the direction of migration. */ + private EdgeDirection performWalk( + MersenneTwisterFast random, + SiteNode node, + EnumMap valList, + ArrayList skipList) { + switch (walkType) { + case RANDOM: + return performRandomWalk(random, node, skipList); + case BIASED: + return performBiasedWalk(random, node, valList, skipList); + case DETERMINISTIC: + return performDeterministicWalk(random, node, valList, skipList); + default: + return performDeterministicWalk(random, node, valList, skipList); + } + } + + @Override + public void step(SimState simstate) { + Simulation sim = (Simulation) simstate; + int tick = (int) simstate.schedule.getTime(); + Lattice vegfLattice = sim.getLattice("VEGF"); + MersenneTwisterFast random = simstate.random; + addFlag = false; + + LinkedHashSet validNodes = getValidNodes(); + for (SiteNode node : validNodes) { if (checkNodeSkipStatus(node, tick)) { continue; } @@ -229,8 +268,8 @@ public void step(SimState simstate) { buildDirectionalVEGFMap(vegfLattice, node); if (averageDirectionalMap(vegfMap) > vegfThreshold) { - angioMap.put(node, new ArrayList<>()); - ArrayList skipDirList = new ArrayList(); + angiogenicNodeMap.put(node, new ArrayList<>()); + ArrayList ignoredDirectionList = new ArrayList(); Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); @@ -238,100 +277,44 @@ public void step(SimState simstate) { if (in != null) { for (Object edge : in) { SiteEdge inEdge = (SiteEdge) edge; - skipDirList.add( + ignoredDirectionList.add( sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); } } if (out != null) { for (Object edge : out) { SiteEdge outEdge = (SiteEdge) edge; - skipDirList.add(sites.graphFactory.getDirection(outEdge, outEdge.level)); + ignoredDirectionList.add( + sites.graphFactory.getDirection(outEdge, outEdge.level)); } } EnumMap vegfAverages = getDirectionalAverages(vegfMap); - switch (walkType) { - case RANDOM: - node.sproutDir = - performRandomWalk(random, node, vegfAverages, tick, skipDirList); - break; - case BIASED: - node.sproutDir = - performBiasedWalk(random, node, vegfAverages, tick, skipDirList); - break; - case DETERMINISTIC: - node.sproutDir = - performDeterministicWalk( - random, node, vegfAverages, tick, skipDirList); - break; - default: - node.sproutDir = - performDeterministicWalk( - random, node, vegfAverages, tick, skipDirList); - } + node.sproutDir = performWalk(random, node, vegfAverages, ignoredDirectionList); } } - boolean addFlag = false; - - addTemporaryEdges(); - - for (Map.Entry> entry : angioMap.entrySet()) { - // grab node in each list and add edge, check for perfusion - SiteNode keyNode = entry.getKey(); - - if (checkForIgnoredEdges(keyNode)) { - nodesToRemove.add(keyNode); - continue; - } - - ArrayList edgeList = entry.getValue(); - SiteNode tipNode; - SiteEdge newEdge; - if (edgeList.size() > 0) { - tipNode = edgeList.get(edgeList.size() - 1).getTo(); - } else { - tipNode = keyNode; - } - - if (tick - tipNode.lastUpdate < migrationRate) { - continue; - } - - newEdge = createNewEdge(keyNode.sproutDir, tipNode, tick); - - if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3) { - // LOGGER.info("Removing " + keyNode + " from angiomap, unsuccessful - // perfusion."); - nodesToRemove.add(keyNode); - } else { - edgeList.add(newEdge); - if (newEdge.isAnastomotic) { - keyNode.anastomosis = true; - addFlag = true; - } - } - } - - removeTemporaryEdges(); + propogateEdges(); if (addFlag) { added.clear(); // LOGGER.info("*****Adding edges to graph.****** Time: " + tick); // LOGGER.info("Current graph size: " + graph.getAllEdges().size()); - for (SiteNode sproutNode : angioMap.keySet()) { + for (SiteNode sproutNode : angiogenicNodeMap.keySet()) { if (nodesToRemove.contains(sproutNode)) { continue; } if (sproutNode.anastomosis) { - int leadingIndex = angioMap.get(sproutNode).size() - 1; + int leadingIndex = angiogenicNodeMap.get(sproutNode).size() - 1; if (leadingIndex < 0) { nodesToRemove.add(sproutNode); continue; } - SiteNode finalNode = angioMap.get(sproutNode).get(leadingIndex).getTo(); - SiteNode init, fin; + SiteNode finalNode = + angiogenicNodeMap.get(sproutNode).get(leadingIndex).getTo(); + SiteNode init; + SiteNode fin; calculatePressures(graph); boolean reversed = reversePressures(graph); @@ -341,29 +324,23 @@ public void step(SimState simstate) { calculateFlows(graph); calculateStresses(graph); - // maybe try to redo by iterating through list rather than using node if (!graph.contains(finalNode)) { - // LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); + // Connecting two angiogenic nodes SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); if (targetNode == null) { - // LOGGER.info("Likely removed node - skipping"); sproutNode.anastomosis = false; continue; } - // path(graph, targetNode, sproutNode); if (sproutNode.pressure < targetNode.pressure) { - // LOGGER.info("SWAPPING SPROUT NODE"); reverseAllEdges(sproutNode); init = targetNode; fin = sproutNode; } else { - // LOGGER.info("SWAPPING TARGET NODE"); reverseAllEdges(targetNode); init = sproutNode; fin = targetNode; } - angioMap.get(sproutNode).addAll(angioMap.get(targetNode)); - + angiogenicNodeMap.get(sproutNode).addAll(angiogenicNodeMap.get(targetNode)); nodesToRemove.add(sproutNode); nodesToRemove.add(targetNode); } else { @@ -379,40 +356,91 @@ public void step(SimState simstate) { ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); } } - // path(graph, finalNode, sproutNode); if (sproutNode.pressure < finalNode.pressure) { - // LOGGER.info("SWAPPING NODE"); reverseAllEdges(sproutNode); init = finalNode; fin = sproutNode; } else { - // LOGGER.info("DID NOT SWAP"); init = sproutNode; fin = finalNode; } nodesToRemove.add(sproutNode); } if (init.pressure == 0 || fin.pressure == 0) { - // LOGGER.info("Pressure is 0, skipping"); continue; } addAngioEdges( - angioMap.get(sproutNode), init, fin, tick, Calculation.COMPENSATE); + angiogenicNodeMap.get(sproutNode), + init, + fin, + tick, + calculationStrategy); } } } for (SiteNode n : nodesToRemove) { - angioMap.remove(n); + angiogenicNodeMap.remove(n); } - // If any edges are removed, update the graph edges that are ignored. - // Otherwise, recalculate calculate stresses. + nodesToRemove.clear(); + if (!added.isEmpty()) { - // LOGGER.info("*****Updating graph.****** Time: " + tick); updateGraph(graph); } } + /** + * Propogates the edges from each of the nodes in the angiogenic node map. + * + *

A node is stepped according to the sprout direction of the node. If the added edge + * connects to another node in the temporary graph, it becomes anastomotic. If the node is + * anastomotic, the add flag is set to true. If the node is not anastomotic, before reaching the + * max length, or cannot be added to the graph for another reason, the node added to the removal + * queue. + */ + private void propogateEdges() { + addTemporaryEdges(); + + ArrayList nodesToRemove = new ArrayList<>(); + + for (Map.Entry> entry : angiogenicNodeMap.entrySet()) { + // Grab node in each list and add edge, check for perfusion + SiteNode keyNode = entry.getKey(); + + if (checkForIgnoredEdges(keyNode)) { + nodesToRemove.add(keyNode); + continue; + } + + ArrayList edgeList = entry.getValue(); + SiteNode tipNode; + SiteEdge newEdge; + if (edgeList.size() > 0) { + tipNode = edgeList.get(edgeList.size() - 1).getTo(); + } else { + tipNode = keyNode; + } + + if (tick - tipNode.lastUpdate < migrationRate) { + continue; + } + + newEdge = createNewEdge(keyNode.sproutDir, tipNode, tick); + + if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3) { + nodesToRemove.add(keyNode); + } else { + edgeList.add(newEdge); + if (newEdge.isAnastomotic) { + keyNode.anastomosis = true; + addFlag = true; + } + } + } + + removeTemporaryEdges(); + } + /** * Checks if the node has any edges that are marked as ignored. * @@ -443,7 +471,7 @@ private boolean checkForIgnoredEdges(SiteNode node) { /** Criteria for skipping a node during the migration checks. */ private boolean checkNodeSkipStatus(SiteNode node, int tick) { - if (angioMap.keySet().contains(node)) { + if (angiogenicNodeMap.keySet().contains(node)) { return true; } if (node.isRoot) { @@ -456,33 +484,33 @@ private boolean checkNodeSkipStatus(SiteNode node, int tick) { } /** - * Reverses all edges in the angioMap for a given node. + * Reverses all edges in the angiogenicNodeMap for a given node. * * @param node {@link SiteNode} object. */ private void reverseAllEdges(SiteNode node) { - for (SiteEdge edge : angioMap.get(node)) { + for (SiteEdge edge : angiogenicNodeMap.get(node)) { edge.reverse(); } } /** - * Private helper function for finding the key node in the angioMap for a given target node and - * skip node. + * Private helper function for finding the key node in the angiogenicNodeMap for a given target + * node and skip node. * * @param targetNode {@link SiteNode} object. * @param skipNode {@link SiteNode} object. * @return {@link SiteNode} object. */ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { - for (SiteNode keyNode : angioMap.keySet()) { + for (SiteNode keyNode : angiogenicNodeMap.keySet()) { if (keyNode == skipNode) { continue; } if (keyNode == targetNode) { return keyNode; } - if (edgeListcontains(angioMap.get(keyNode), targetNode)) { + if (edgeListcontains(angiogenicNodeMap.get(keyNode), targetNode)) { return keyNode; } } @@ -505,17 +533,20 @@ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNo return false; } - /** Adds all temporary edges to the graph. */ + /** + * Creates a temporary version of the graph that contains all the proposed edges from the + * angiogenic node map. + */ private void addTemporaryEdges() { tempEdges = new ArrayList<>(); - for (Map.Entry> entry : angioMap.entrySet()) { + for (Map.Entry> entry : angiogenicNodeMap.entrySet()) { ArrayList edgeList = entry.getValue(); tempEdges.addAll(edgeList); addEdgeList(edgeList); } } - /** Removes all temporary edges from the graph. */ + /** Removes the proposed edges from the graph. */ private void removeTemporaryEdges() { if (tempEdges.isEmpty()) { return; @@ -538,19 +569,13 @@ private void removeEdgeList(ArrayList edgeList) { /** * Private helper function for choosing a sprout direction randomly on the from the node. * - * @param random {@link MersenneTwisterFast} object. - * @param node {@link SiteNode} object. - * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. - * @param tick {@code int} object. - * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. - * @return {@link EdgeDirection} object. + * @param random simulation random number generator + * @param node the angiogenic node object + * @param skipList list of directions to be skipped + * @return a random edge direction */ private EdgeDirection performRandomWalk( - MersenneTwisterFast random, - SiteNode node, - EnumMap valList, - int tick, - ArrayList skipList) { + MersenneTwisterFast random, SiteNode node, ArrayList skipList) { EdgeDirection randDir; do { randDir = offsetDirections.get(random.nextInt(numOffsets)); @@ -562,18 +587,16 @@ private EdgeDirection performRandomWalk( * Private helper function for choosing a sprout direction biased on the VEGF concentration * around the node. * - * @param random {@link MersenneTwisterFast} object. - * @param node {@link SiteNode} object. - * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. - * @param tick {@code int} object. - * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. - * @return {@link EdgeDirection} object. + * @param random simulation random number generator + * @param node the angiogenic node object + * @param valList map of direction to their respective VEGF concentration value + * @param skipList list of directions to be skipped + * @return a biased random edge direction */ private EdgeDirection performBiasedWalk( MersenneTwisterFast random, SiteNode node, EnumMap valList, - int tick, ArrayList skipList) { for (EdgeDirection dir : skipList) { valList.put(dir, 0.0); @@ -593,18 +616,16 @@ private EdgeDirection performBiasedWalk( * Private helper function for choosing a sprout direction deterministically on the VEGF * concentration around the node. * - * @param random {@link MersenneTwisterFast} object. - * @param node {@link SiteNode} object. - * @param valList {@link EnumMap} of {@link EdgeDirection} to double values. - * @param tick {@code int} object. - * @param skipList {@link ArrayList} of {@link EdgeDirection} objects. - * @return {@link EdgeDirection} object. + * @param random simulation random number generator + * @param node the angiogenic node object + * @param valList map of direction to their respective VEGF concentration value + * @param skipList list of directions to be skipped + * @return the edge direction with highest concentration */ private EdgeDirection performDeterministicWalk( MersenneTwisterFast random, SiteNode node, EnumMap valList, - int tick, ArrayList skipList) { for (EdgeDirection dir : skipList) { valList.put(dir, 0.0); @@ -616,9 +637,10 @@ private EdgeDirection performDeterministicWalk( /** * Private helper function for building a map of VEGF concentration values for a given node. * - * @param lattice {@link Lattice} object. - * @param node {@link SiteNode} object. - * @return {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of double values. + * @param lattice the VEGF lattice object + * @param node the angiogenic node object + * @return map of {@link EdgeDirection} to {@link ArrayList} of double values from the span of + * the edge in that direction */ private EnumMap> buildDirectionalVEGFMap( Lattice lattice, SiteNode node) { @@ -645,8 +667,10 @@ private EnumMap> buildDirectionalVEGFMap( /** * Private helper function for getting the average VEGF concentration values for a given map. * - * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of double values. - * @return {@link EnumMap} of {@link EdgeDirection} to double values. + * @param map map of {@link EdgeDirection} to {@link ArrayList} of double values from the span + * of the edge in that direction + * @return map of {@link EdgeDirection} to the average VEGF concentration value across the span + * in that direction */ private EnumMap getDirectionalAverages( EnumMap> map) { @@ -664,8 +688,9 @@ private EnumMap getDirectionalAverages( /** * Private helper function for getting the maximum VEGF concentration value for a given map. * - * @param map {@link EnumMap} of {@link EdgeDirection} to double values. - * @return {@link EdgeDirection} object. + * @param map map of {@link EdgeDirection} to the average VEGF concentration value across the + * span in that direction + * @return direction with the highest concentration */ private EdgeDirection getMaxKey(EnumMap map) { EdgeDirection maxDir = EdgeDirection.UNDEFINED; @@ -765,7 +790,6 @@ private void addAngioEdges( // check for cycle path(graph, end, start); if (end.prev != null) { - // LOGGER.info("Cycle detected, not adding edge"); return; } @@ -773,7 +797,6 @@ private void addAngioEdges( for (SiteEdge e : list) { tempG.addEdge(e); } - // LOGGER.info("" + tempG); path(tempG, start, end); SiteNode n = end; while (n != start) { @@ -803,9 +826,6 @@ private void addAngioEdges( edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); - - // update later (updateSpans method should take care of most of these, need to - // check for perfusion first) edge.isPerfused = true; } @@ -825,7 +845,7 @@ private void addAngioEdges( graph.findDownstreamIntersection( (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); if (intersection != null) { - recalcRadii(added, start, end, intersection); + recalculateRadii(added, start, end, intersection); } else { removeEdgeList(added); } @@ -939,7 +959,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, for (ArrayList path : pathsVeins) { if (!path.get(0).getFrom().isRoot) { - throw new ArithmeticException("Root is not the start of the path"); + throw new ArithmeticException("Root is not the start of the path."); } SiteEdge rootEdge = path.remove(path.size() - 1); oldRadii.add(rootEdge.radius); @@ -955,7 +975,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } if (arteries.size() == 0 || veins.size() == 0) { - LOGGER.info("No arteries or veins found, not updating roots"); + LOGGER.info("No arteries or veins found, not updating roots."); failed = true; } @@ -973,7 +993,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, * @param end {@link SiteNode} object. * @param intersection {@link SiteNode} object. */ - private void recalcRadii( + private void recalculateRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { updateGraph(graph); @@ -1038,7 +1058,8 @@ private void recalcRadii( } ; } else { - // maybe also TODO: check for perfusion first + // maybe also + // TODO: check for perfusion first // TODO: check to add flow to radius with new flow after changes to other // potential edge, need to do this math out? // this should only work for single vein simulations diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index 5ea4cb77a..ed1ea0750 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -165,9 +165,10 @@ \ + + + + + - - - - diff --git a/test/arcade/patch/env/component/PatchComponentGrowthTest.java b/test/arcade/patch/env/component/PatchComponentGrowthTest.java index 0b9e2a8e0..d7780117e 100644 --- a/test/arcade/patch/env/component/PatchComponentGrowthTest.java +++ b/test/arcade/patch/env/component/PatchComponentGrowthTest.java @@ -57,6 +57,7 @@ public void schedule_calledWithMigrationRateLowerThanEdgeSize_setsScheduleCorrec parameters.put("VEGF_THRESHOLD", 0.5); parameters.put("WALK_TYPE", "BIASED"); parameters.put("MAX_LENGTH", 100); + parameters.put("CALCULATION_STRATEGY", "DIVERT"); Schedule schedule = mock(Schedule.class); @@ -83,6 +84,7 @@ public void schedule_calledWithMigrationRateGreaterThanEdgeSize_setsScheduleCorr parameters.put("VEGF_THRESHOLD", 0.5); parameters.put("WALK_TYPE", "BIASED"); parameters.put("MAX_LENGTH", 100); + parameters.put("CALCULATION_STRATEGY", "DIVERT"); Schedule schedule = mock(Schedule.class); @@ -109,6 +111,7 @@ public void register_calledWithIncompatibleFeature_throwsException() { parameters.put("VEGF_THRESHOLD", 0.5); parameters.put("WALK_TYPE", "BIASED"); parameters.put("MAX_LENGTH", 100); + parameters.put("CALCULATION_STRATEGY", "DIVERT"); PatchComponentSitesPattern pattern = mock(PatchComponentSitesPattern.class); doReturn(pattern).when(simMock).getComponent("INCOMPATIBLE"); @@ -127,6 +130,7 @@ public void register_calledWithSitesGraphObject_doesNotThrowException() { parameters.put("VEGF_THRESHOLD", 0.5); parameters.put("WALK_TYPE", "BIASED"); parameters.put("MAX_LENGTH", 100); + parameters.put("CALCULATION_STRATEGY", "DIVERT"); PatchComponentSitesGraph graph = mock(PatchComponentSitesGraph.class); doReturn(graph).when(simMock).getComponent("COMPATIBLE"); From 2c832e6633366b4436734b43fa0e77dc1a9eb35f Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 13 Jun 2025 17:04:17 -0700 Subject: [PATCH 17/56] removing redundant variables --- .../env/component/PatchComponentGrowth.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 93fc20585..e765b97cf 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -60,7 +60,7 @@ */ public class PatchComponentGrowth implements Component { /** Logger for {@code PatchComponentGrowth}. */ - private static Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); + private static final Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); /** Default edge level to add to the graph from this component. */ private static final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_1; @@ -137,13 +137,13 @@ public enum MigrationDirection { /** Map of offsets to be used in the migration. */ private EnumMap offsets; - /* Tick for the current step. */ + /** Tick for the current step. */ private int tick; - /* Flag for whether to add edges if angiogenic nodes become perfused. */ + /** Flag for whether to add edges if angiogenic nodes become perfused. */ private boolean addFlag; - /* List of nodes to be removed from the angiogenic node map this time step. */ + /** List of nodes to be removed from the angiogenic node map this time step. */ private ArrayList nodesToRemove; /** @@ -253,14 +253,14 @@ private EdgeDirection performWalk( @Override public void step(SimState simstate) { Simulation sim = (Simulation) simstate; - int tick = (int) simstate.schedule.getTime(); + tick = (int) simstate.schedule.getTime(); Lattice vegfLattice = sim.getLattice("VEGF"); MersenneTwisterFast random = simstate.random; addFlag = false; LinkedHashSet validNodes = getValidNodes(); for (SiteNode node : validNodes) { - if (checkNodeSkipStatus(node, tick)) { + if (checkNodeSkipStatus(node)) { continue; } @@ -370,11 +370,7 @@ public void step(SimState simstate) { continue; } addAngioEdges( - angiogenicNodeMap.get(sproutNode), - init, - fin, - tick, - calculationStrategy); + angiogenicNodeMap.get(sproutNode), init, fin, calculationStrategy); } } } @@ -425,7 +421,7 @@ private void propogateEdges() { continue; } - newEdge = createNewEdge(keyNode.sproutDir, tipNode, tick); + newEdge = createNewEdge(keyNode.sproutDir, tipNode); if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3) { nodesToRemove.add(keyNode); @@ -469,8 +465,14 @@ private boolean checkForIgnoredEdges(SiteNode node) { return false; } - /** Criteria for skipping a node during the migration checks. */ - private boolean checkNodeSkipStatus(SiteNode node, int tick) { + /** + * Criteria for skipping a node during the migration checks. + * + * @param node the node to check + * @param tick the current tick + * @return {@code true} if the node should be skipped, {@code false} otherwise + */ + private boolean checkNodeSkipStatus(SiteNode node) { if (angiogenicNodeMap.keySet().contains(node)) { return true; } @@ -783,10 +785,7 @@ private void addEdgeList(ArrayList list, boolean updateProperties) { * @param calc {@link Calculation} object. */ private void addAngioEdges( - ArrayList list, SiteNode start, SiteNode end, int tick, Calculation calc) { - - ArrayList added = new ArrayList<>(); - + ArrayList list, SiteNode start, SiteNode end, Calculation calc) { // check for cycle path(graph, end, start); if (end.prev != null) { @@ -840,6 +839,7 @@ private void addAngioEdges( updateRootsAndRadii(added, start, end); break; case DIVERT: + default: SiteNode intersection = (SiteNode) graph.findDownstreamIntersection( @@ -1044,7 +1044,6 @@ private void recalculateRadii( == -1) { return; } - ; if (updateRadius( (SiteEdge) edges.get(angioIndex), @@ -1053,10 +1052,11 @@ private void recalculateRadii( false, ignoredEdges) == -1) { + return; // LOGGER.info("Failed to update radius when increasing size, something seems // up"); } - ; + } else { // maybe also // TODO: check for perfusion first @@ -1316,7 +1316,7 @@ private void addEdgeList( * @param tick {@code int} object. * @return {@link SiteEdge} object. */ - private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node, int tick) { + private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { SiteNode proposed = sites.graphFactory.offsetNode(node, direction, DEFAULT_EDGE_LEVEL); proposed.lastUpdate = tick; if (sites.graphFactory.checkNode(proposed) && graph.getDegree(node) < 3) { From 5dda2c929526d0b6985cd6019bcc5432404bef70 Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 13 Jun 2025 18:10:36 -0700 Subject: [PATCH 18/56] updated some javadocs --- .../env/component/PatchComponentGrowth.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index e765b97cf..8f402208d 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -206,6 +206,12 @@ public void register(Simulation sim, String componentID) { maxEdges = (int) Math.floor(maxLength / edgeSize); } + /** + * Returns a set of valid nodes. A node is valid if it not associated with an ignored edge, is + * not a root, and has fewer than 3 degrees. + * + * @return a set of valid nodes + */ private LinkedHashSet getValidNodes() { LinkedHashSet set = new LinkedHashSet<>(); for (Object obj : graph.getAllEdges()) { @@ -232,7 +238,15 @@ private LinkedHashSet getValidNodes() { return set; } - /** Strategy pattern for the direction of migration. */ + /** + * Helper function to handle which version of walk to use for the direction of migration. + * + * @param random the random number generator + * @param node the node to start from + * @param valList the map of directions to their corresponding values + * @param skipList the list of directions to skip + * @return the direction to sprout + */ private EdgeDirection performWalk( MersenneTwisterFast random, SiteNode node, From bd7ee7f14b8980c760f3af6a61518bcfe478af1b Mon Sep 17 00:00:00 2001 From: cainja Date: Sat, 14 Jun 2025 21:48:57 -0700 Subject: [PATCH 19/56] linter updates --- src/arcade/patch/env/component/PatchComponentGrowth.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 8f402208d..5934d8f54 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -411,8 +411,6 @@ public void step(SimState simstate) { private void propogateEdges() { addTemporaryEdges(); - ArrayList nodesToRemove = new ArrayList<>(); - for (Map.Entry> entry : angiogenicNodeMap.entrySet()) { // Grab node in each list and add edge, check for perfusion SiteNode keyNode = entry.getKey(); @@ -483,7 +481,6 @@ private boolean checkForIgnoredEdges(SiteNode node) { * Criteria for skipping a node during the migration checks. * * @param node the node to check - * @param tick the current tick * @return {@code true} if the node should be skipped, {@code false} otherwise */ private boolean checkNodeSkipStatus(SiteNode node) { @@ -795,7 +792,6 @@ private void addEdgeList(ArrayList list, boolean updateProperties) { * @param list {@link ArrayList} of {@link SiteEdge} objects. * @param start {@link SiteNode} object. * @param end {@link SiteNode} object. - * @param tick {@code int} object. * @param calc {@link Calculation} object. */ private void addAngioEdges( @@ -1327,7 +1323,6 @@ private void addEdgeList( * * @param direction {@link EdgeDirection} object. * @param node {@link SiteNode} object. - * @param tick {@code int} object. * @return {@link SiteEdge} object. */ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { From fdd8d077c98f508ff1241f334ded61d7bfac8e8c Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 15 Jun 2025 18:47:54 -0700 Subject: [PATCH 20/56] updated to work with multiple veins --- .../MissingSpecificationException.java | 13 ++ .../env/component/PatchComponentGrowth.java | 141 +++++++----------- .../component/PatchComponentGrowthTest.java | 36 ++++- 3 files changed, 102 insertions(+), 88 deletions(-) create mode 100644 src/arcade/core/util/exceptions/MissingSpecificationException.java diff --git a/src/arcade/core/util/exceptions/MissingSpecificationException.java b/src/arcade/core/util/exceptions/MissingSpecificationException.java new file mode 100644 index 000000000..10960e771 --- /dev/null +++ b/src/arcade/core/util/exceptions/MissingSpecificationException.java @@ -0,0 +1,13 @@ +package arcade.core.util.exceptions; + +/** Exception thrown when a specification is missing. */ +public class MissingSpecificationException extends RuntimeException { + /** + * Constructs an {@code MissingSpecificationException} with the specified detail message. + * + * @param missing the expected specification + */ + public MissingSpecificationException(String missing) { + super("The input file must contain the following specification: "); + } +} diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 5934d8f54..4c33ddc23 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -19,6 +19,7 @@ import arcade.core.util.Solver; import arcade.core.util.Solver.Function; import arcade.core.util.exceptions.IncompatibleFeatureException; +import arcade.core.util.exceptions.MissingSpecificationException; import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; @@ -95,6 +96,9 @@ public enum MigrationDirection { /** Angiogenesis threshold for vegf concentration near SiteNode to initiate migration. */ private double vegfThreshold; + /** Lattice containing vegf concentration. */ + private Lattice vegfLattice; + /** Direction of migration. */ private MigrationDirection walkType; @@ -140,9 +144,6 @@ public enum MigrationDirection { /** Tick for the current step. */ private int tick; - /** Flag for whether to add edges if angiogenic nodes become perfused. */ - private boolean addFlag; - /** List of nodes to be removed from the angiogenic node map this time step. */ private ArrayList nodesToRemove; @@ -189,11 +190,17 @@ public void schedule(Schedule schedule) { public void register(Simulation sim, String componentID) { Component component = sim.getComponent(componentID); + // validate if (!(component instanceof PatchComponentSitesGraph)) { throw new IncompatibleFeatureException( "Growth Component", component.getClass().getName(), "PatchComponentSitesGraph"); } + vegfLattice = sim.getLattice("VEGF"); + if (vegfLattice == null) { + throw new MissingSpecificationException("VEGF layer must be included."); + } + sites = (PatchComponentSitesGraph) component; graph = sites.graph; offsets = sites.graphFactory.getOffsets(); @@ -266,11 +273,8 @@ private EdgeDirection performWalk( @Override public void step(SimState simstate) { - Simulation sim = (Simulation) simstate; tick = (int) simstate.schedule.getTime(); - Lattice vegfLattice = sim.getLattice("VEGF"); MersenneTwisterFast random = simstate.random; - addFlag = false; LinkedHashSet validNodes = getValidNodes(); for (SiteNode node : validNodes) { @@ -309,12 +313,8 @@ public void step(SimState simstate) { } } - propogateEdges(); - + boolean addFlag = propogateEdges(); if (addFlag) { - added.clear(); - // LOGGER.info("*****Adding edges to graph.****** Time: " + tick); - // LOGGER.info("Current graph size: " + graph.getAllEdges().size()); for (SiteNode sproutNode : angiogenicNodeMap.keySet()) { if (nodesToRemove.contains(sproutNode)) { continue; @@ -358,6 +358,7 @@ public void step(SimState simstate) { nodesToRemove.add(sproutNode); nodesToRemove.add(targetNode); } else { + // Connecting sprout to existing node if (sproutNode.pressure == 0) { if (graph.getEdgesOut(sproutNode) != null) { sproutNode = @@ -394,7 +395,7 @@ public void step(SimState simstate) { } nodesToRemove.clear(); - if (!added.isEmpty()) { + if (addFlag) { updateGraph(graph); } } @@ -407,8 +408,11 @@ public void step(SimState simstate) { * anastomotic, the add flag is set to true. If the node is not anastomotic, before reaching the * max length, or cannot be added to the graph for another reason, the node added to the removal * queue. + * + * @return {@code true} if an angiogenic edge becomes perfused, {@code false} otherwise */ - private void propogateEdges() { + private boolean propogateEdges() { + boolean addFlag = false; addTemporaryEdges(); for (Map.Entry> entry : angiogenicNodeMap.entrySet()) { @@ -447,6 +451,7 @@ private void propogateEdges() { } removeTemporaryEdges(); + return addFlag; } /** @@ -789,10 +794,10 @@ private void addEdgeList(ArrayList list, boolean updateProperties) { /** * Private helper function for adding an edge list to the graph. * - * @param list {@link ArrayList} of {@link SiteEdge} objects. - * @param start {@link SiteNode} object. - * @param end {@link SiteNode} object. - * @param calc {@link Calculation} object. + * @param list list of angiogenic edges to add to the graph + * @param start the starting site node object + * @param end the ending site node object + * @param calc code for the type of calculation to perform */ private void addAngioEdges( ArrayList list, SiteNode start, SiteNode end, Calculation calc) { @@ -802,24 +807,7 @@ private void addAngioEdges( return; } - Graph tempG = new Graph(); - for (SiteEdge e : list) { - tempG.addEdge(e); - } - path(tempG, start, end); - SiteNode n = end; - while (n != start) { - added.add(new SiteEdge(n.prev, n, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL)); - n = n.prev; - if (n != start) { - if (n == null) { - return; - } - n.addTime = (int) tick; - } - } - - double otherRadius = 0; + double otherRadius; Bag outEdges = graph.getEdgesOut(start); if (outEdges != null) { otherRadius = ((SiteEdge) outEdges.get(0)).radius; @@ -827,7 +815,15 @@ private void addAngioEdges( return; } - for (SiteEdge edge : added) { + Graph tempGraph = new Graph(); + for (SiteEdge e : list) { + tempGraph.addEdge(e); + } + + // update edges in the minimal path between start and end + ArrayList angioPath = getPath(tempGraph, start, end); + for (SiteEdge edge : angioPath) { + edge.getTo().addTime = tick; edge.radius = (otherRadius > CAPILLARY_RADIUS) ? CAPILLARY_RADIUS @@ -838,36 +834,33 @@ private void addAngioEdges( edge.isPerfused = true; } - if (start.pressure * end.pressure <= 0) { - return; - } - - addEdgeList(added); + addEdgeList(angioPath); switch (calc) { case COMPENSATE: - updateRootsAndRadii(added, start, end); + updateRootsAndRadii(angioPath, start, end); break; case DIVERT: default: SiteNode intersection = (SiteNode) graph.findDownstreamIntersection( - (SiteEdge) outEdges.get(0), (SiteEdge) added.get(0)); + (SiteEdge) outEdges.get(0), (SiteEdge) angioPath.get(0)); if (intersection != null) { - recalculateRadii(added, start, end, intersection); + recalculateRadii(angioPath, start, end, intersection); } else { - removeEdgeList(added); + removeEdgeList(angioPath); } break; } } /** - * Private helper function for calculating the even split radius of an edge. + * Private helper function for calculating the new radius of two edges after splitting flow + * evenly. * - * @param edge {@link SiteEdge} object. - * @return {@code double} object. + * @param edge the original edge with specified radius + * @return new radius for the edges */ private double calculateEvenSplitRadius(SiteEdge edge) { double radius = edge.radius; @@ -880,13 +873,6 @@ private double calculateEvenSplitRadius(SiteEdge edge) { 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); - // LOGGER.info("splitting radius, for checking if it happens directly before - // bisection failing"); - // double newRadius = Solver.bisection((double r) -> Math.pow(flow - 2 * - // calculateLocalFlow(r, length, deltaP), 2), 0, MAXIMUM_CAPILLARY_RADIUS); - // double newRadius = Solver.boundedGradientDescent((double r) -> Math.pow(flow - // - 2 * calculateLocalFlow(r, length, deltaP), 2), radius, 1E-17, - // MINIMUM_CAPILLARY_RADIUS, MAXIMUM_CAPILLARY_RADIUS); return newRadius; } @@ -1015,16 +1001,10 @@ private void recalculateRadii( if (edges.size() < 2) { return; } - // if (((SiteEdge) edges.get(0)).isIgnored || ((SiteEdge) - // edges.get(1)).isIgnored) { - // return; - // } Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; Integer nonAngioIndex = angioIndex ^ 1; double deltaP = start.pressure - end.pressure; - // double deltaP = ((SiteNode) graph.lookup(start)).pressure - ((SiteNode) - // graph.lookup(end)).pressure; Double divertedFlow = calculateLocalFlow(CAPILLARY_RADIUS, ignoredEdges, deltaP); Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { @@ -1039,10 +1019,6 @@ private void recalculateRadii( false, ignoredEdges); return; - // updateRadiusToRoot((SiteEdge) edges.get(angioIndex), intersection, - // divertedFlow, false, ignoredEdges); - // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, - // divertedFlow, true, ignoredEdges); } if (updateRadius( @@ -1063,32 +1039,23 @@ private void recalculateRadii( ignoredEdges) == -1) { return; - // LOGGER.info("Failed to update radius when increasing size, something seems - // up"); } } else { - // maybe also - // TODO: check for perfusion first - // TODO: check to add flow to radius with new flow after changes to other - // potential edge, need to do this math out? - // this should only work for single vein simulations - SiteNode boundary = sites.graphFactory.veins.get(0).node; - path(graph, start, boundary); - if (boundary.prev != null - && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { - // LOGGER.info("Calculating additional flow to vein"); - updateRadiusToRoot( - (SiteEdge) edges.get(angioIndex), - sites.graphFactory.veins.get(0).node, - divertedFlow, - false, - ignoredEdges); - } else { - return; + for (Root vein : sites.graphFactory.veins) { + SiteNode boundary = vein.node; + path(graph, start, boundary); + if (boundary.prev != null + && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { + updateRadiusToRoot( + (SiteEdge) edges.get(angioIndex), + sites.graphFactory.veins.get(0).node, + divertedFlow, + false, + ignoredEdges); + } + break; } - // updateRadiusToRoot((SiteEdge) edges.get(nonAngioIndex), intersection, - // divertedFlow, true, ignoredEdges); } } diff --git a/test/arcade/patch/env/component/PatchComponentGrowthTest.java b/test/arcade/patch/env/component/PatchComponentGrowthTest.java index d7780117e..1914050b9 100644 --- a/test/arcade/patch/env/component/PatchComponentGrowthTest.java +++ b/test/arcade/patch/env/component/PatchComponentGrowthTest.java @@ -9,10 +9,12 @@ import arcade.core.util.MiniBox; import arcade.core.util.Parameters; import arcade.core.util.exceptions.IncompatibleFeatureException; +import arcade.core.util.exceptions.MissingSpecificationException; import arcade.patch.agent.process.PatchProcessMetabolism; import arcade.patch.agent.process.PatchProcessSignaling; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; import arcade.patch.env.grid.PatchGrid; +import arcade.patch.env.lattice.PatchLattice; import arcade.patch.sim.PatchSeries; import arcade.patch.sim.PatchSimulation; import static org.junit.jupiter.api.Assertions.*; @@ -35,9 +37,10 @@ public class PatchComponentGrowthTest { static MersenneTwisterFast randomMock; + static PatchLattice latticeMock; + @BeforeAll public static void setup() { - seriesMock = mock(PatchSeries.class, CALLS_REAL_METHODS); simMock = mock(PatchSimulation.class); doReturn(seriesMock).when(simMock).getSeries(); @@ -45,6 +48,7 @@ public static void setup() { metabolismMock = mock(PatchProcessMetabolism.class); signalingMock = mock(PatchProcessSignaling.class); gridMock = mock(PatchGrid.class); + latticeMock = mock(PatchLattice.class); doReturn(gridMock).when(simMock).getGrid(); randomMock = mock(MersenneTwisterFast.class); simMock.random = randomMock; @@ -123,6 +127,35 @@ public void register_calledWithIncompatibleFeature_throwsException() { () -> component.register(simMock, "INCOMPATIBLE")); } + @Test + public void register_calledWithoutVEGFLattice_throwsException() { + MiniBox parameters = new MiniBox(); + parameters.put("MIGRATION_RATE", 60); + parameters.put("VEGF_THRESHOLD", 0.5); + parameters.put("WALK_TYPE", "BIASED"); + parameters.put("MAX_LENGTH", 100); + parameters.put("CALCULATION_STRATEGY", "DIVERT"); + + PatchComponentSitesGraph graph = mock(PatchComponentSitesGraph.class); + when(simMock.getLattice("VEGF")).thenReturn(null); + + try { + Field field = PatchComponentSitesGraph.class.getDeclaredField("graphFactory"); + field.setAccessible(true); + field.set(graph, mock(PatchComponentSitesGraphFactory.class)); + } catch (Exception ignored) { + } + + doReturn(new EnumMap(EdgeDirection.class)) + .when(graph.graphFactory) + .getOffsets(); + + PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); + assertThrows( + MissingSpecificationException.class, + () -> component.register(simMock, "COMPATIBLE")); + } + @Test public void register_calledWithSitesGraphObject_doesNotThrowException() { MiniBox parameters = new MiniBox(); @@ -134,6 +167,7 @@ public void register_calledWithSitesGraphObject_doesNotThrowException() { PatchComponentSitesGraph graph = mock(PatchComponentSitesGraph.class); doReturn(graph).when(simMock).getComponent("COMPATIBLE"); + when(simMock.getLattice("VEGF")).thenReturn(latticeMock); try { Field field = PatchComponentSitesGraph.class.getDeclaredField("graphFactory"); From 943281db7a21e929f1093a5783ac4b0929749ded Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 15 Jun 2025 19:17:22 -0700 Subject: [PATCH 21/56] updated boolean argument --- .../env/component/PatchComponentGrowth.java | 148 ++++++++---------- 1 file changed, 68 insertions(+), 80 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 4c33ddc23..ab7f3367e 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -90,6 +90,14 @@ public enum MigrationDirection { BIASED; } + private enum Adjustment { + /** Code for increasing flow. */ + INCREASE, + + /** Code for decreasing flow. */ + DECREASE; + } + /** Rate of migration. */ private double migrationRate; @@ -129,9 +137,6 @@ public enum MigrationDirection { /** List of temporary edges to be added to the graph this step. */ private ArrayList tempEdges; - /** List of edges that have been added to the graph this step. */ - private ArrayList added = new ArrayList<>(); - /** Number of offsets to be used in the migration. */ private int numOffsets; @@ -725,8 +730,9 @@ private EdgeDirection getMaxKey(EnumMap map) { /** * Private helper function for normalizing a map of VEGF concentration values. * - * @param map {@link EnumMap} of {@link EdgeDirection} to double values. - * @return {@link EnumMap} of {@link EdgeDirection} to double values. + * @param map {@link EnumMap} of {@link EdgeDirection} to real values. + * @return {@link EnumMap} of {@link EdgeDirection} to normalized values (range between 0 and + * 1). */ private EnumMap normalizeDirectionalMap( EnumMap map) { @@ -744,7 +750,7 @@ private EnumMap normalizeDirectionalMap( * Private helper function for summing a map of VEGF concentration values. * * @param map {@link EnumMap} of {@link EdgeDirection} to double values. - * @return {@code double} object. + * @return sum of the values in the map */ private double sumMap(EnumMap map) { double sum = 0; @@ -772,25 +778,6 @@ private double averageDirectionalMap(EnumMap> m return sum / count; } - /** - * Private helper function for adding an edge list to the graph. - * - * @param list {@link ArrayList} of {@link SiteEdge} objects. - */ - private void addEdgeList(ArrayList list) { - addEdgeList(list, false); - } - - /** - * Private helper function for adding an edge list to the graph. - * - * @param list {@link ArrayList} of {@link SiteEdge} objects. - * @param updateProperties {@code boolean} object. - */ - private void addEdgeList(ArrayList list, boolean updateProperties) { - addEdgeList(list, updateProperties, DEFAULT_EDGE_TYPE); - } - /** * Private helper function for adding an edge list to the graph. * @@ -879,9 +866,9 @@ private double calculateEvenSplitRadius(SiteEdge edge) { /** * Private helper function for updating the roots and radii of an edge list. * - * @param addedEdges {@link ArrayList} of {@link SiteEdge} objects. - * @param start {@link SiteNode} object. - * @param end {@link SiteNode} object. + * @param addedEdges list of edges that were added + * @param start the starting {@link SiteNode} + * @param end the ending {@link SiteNode} */ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteNode end) { updateGraph(graph); @@ -927,11 +914,11 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteEdge rootEdge = path.remove(0); - if (calculateArteryRootRadius(rootEdge, arteryFlow, false) == -1) { + if (calculateArteryRootRadius(rootEdge, arteryFlow, Adjustment.INCREASE) == -1) { failed = true; break; } - if (updateRadiiOfEdgeList(path, arteryFlow, false) == -1) { + if (updateRadiiOfEdgeList(path, arteryFlow, Adjustment.INCREASE) == -1) { failed = true; break; } @@ -960,11 +947,11 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteEdge rootEdge = path.remove(path.size() - 1); oldRadii.add(rootEdge.radius); updatedEdges.add(rootEdge); - if (calculateArteryRootRadius(rootEdge, veinFlow, false) == -1) { + if (calculateArteryRootRadius(rootEdge, veinFlow, Adjustment.INCREASE) == -1) { failed = true; break; } - if (updateRadiiOfEdgeList(path, veinFlow, false) == -1) { + if (updateRadiiOfEdgeList(path, veinFlow, Adjustment.DECREASE) == -1) { failed = true; break; } @@ -984,10 +971,10 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, /** * Private helper function for recalculating the radii of an edge list. * - * @param ignoredEdges {@link ArrayList} of {@link SiteEdge} objects. - * @param start {@link SiteNode} object. - * @param end {@link SiteNode} object. - * @param intersection {@link SiteNode} object. + * @param ignoredEdges list of edges that should not be changed + * @param start starting {@link SiteNode} + * @param end ending {@link SiteNode} + * @param intersection the intersecting node object from the edges out of start */ private void recalculateRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { @@ -1016,7 +1003,7 @@ private void recalculateRadii( (SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, - false, + Adjustment.INCREASE, ignoredEdges); return; } @@ -1025,7 +1012,7 @@ private void recalculateRadii( (SiteEdge) edges.get(nonAngioIndex), intersection, divertedFlow, - true, + Adjustment.DECREASE, ignoredEdges) == -1) { return; @@ -1035,7 +1022,7 @@ private void recalculateRadii( (SiteEdge) edges.get(angioIndex), intersection, divertedFlow, - false, + Adjustment.INCREASE, ignoredEdges) == -1) { return; @@ -1051,7 +1038,7 @@ private void recalculateRadii( (SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, - false, + Adjustment.INCREASE, ignoredEdges); } break; @@ -1062,10 +1049,10 @@ private void recalculateRadii( /** * Private helper function for updating the radius of an edge. * - * @param edge {@link SiteEdge} object. - * @param intersection {@link SiteNode} object. - * @param flow {@code double} object. - * @param decrease {@code boolean} object. + * @param edge the {@link SiteEdge} + * @param intersection downstream intersection {@link SiteNode} + * @param flow flow adjustment through the edge + * @param adjustment code for flow change * @param ignored {@link ArrayList} of {@link SiteEdge} objects. * @return {@code int} object. */ @@ -1073,12 +1060,12 @@ private int updateRadius( SiteEdge edge, SiteNode intersection, double flow, - boolean decrease, + Adjustment adjustment, ArrayList ignored) { ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); edgesToUpdate.add(0, edge); - return updateRadiiOfEdgeList(edgesToUpdate, flow, decrease, ignored); + return updateRadiiOfEdgeList(edgesToUpdate, flow, adjustment, ignored); } /** @@ -1086,11 +1073,12 @@ private int updateRadius( * * @param edges {@link ArrayList} of {@link SiteEdge} objects. * @param flow {@code double} object. - * @param decrease {@code boolean} object. + * @param adjustment code for flow change * @return {@code int} object. */ - private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolean decrease) { - return updateRadiiOfEdgeList(edges, flow, decrease, new ArrayList<>()); + private int updateRadiiOfEdgeList( + ArrayList edges, double flow, Adjustment adjustment) { + return updateRadiiOfEdgeList(edges, flow, adjustment, new ArrayList<>()); } /** @@ -1098,19 +1086,22 @@ private int updateRadiiOfEdgeList(ArrayList edges, double flow, boolea * * @param edges {@link ArrayList} of {@link SiteEdge} objects. * @param flow {@code double} object. - * @param decrease {@code boolean} object. + * @param adjustment code for flow change * @param ignored {@link ArrayList} of {@link SiteEdge} objects. * @return {@code int} object. */ private int updateRadiiOfEdgeList( - ArrayList edges, double flow, boolean decrease, ArrayList ignored) { + ArrayList edges, + double flow, + Adjustment adjustment, + ArrayList ignored) { ArrayList oldRadii = new ArrayList<>(); for (SiteEdge e : edges) { oldRadii.add(e.radius); if (ignored.contains(e)) { continue; } - if (calculateRadius(e, flow, decrease) == -1) { + if (calculateRadius(e, flow, adjustment) == -1) { resetRadii(edges, oldRadii); return -1; } @@ -1123,17 +1114,17 @@ private int updateRadiiOfEdgeList( * * @param edge {@link SiteEdge} object. * @param flow {@code double} object. - * @param decrease {@code boolean} object. - * @return {@code int} object. + * @param adjustment code for flow change + * @return 0 for successful update, -1 for failure */ - private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { - int sign = decrease ? -1 : 1; + private int calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { + double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); Function f = (double r) -> - originalFlow + sign * flow - calculateLocalFlow(r, edge.length, deltaP); + originalFlow + updatedFlow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); @@ -1149,11 +1140,11 @@ private int calculateRadius(SiteEdge edge, double flow, boolean decrease) { * * @param edge {@link SiteEdge} object. * @param flow {@code double} object. - * @param decrease {@code boolean} object. + * @param adjustment code for flow change * @return {@code int} object. */ - private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease) { - int sign = decrease ? -1 : 1; + private int calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { + double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); @@ -1161,7 +1152,7 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease Function f = (double r) -> originalFlow - + sign * flow + + updatedFlow - calculateLocalFlow( r, edge.length, @@ -1184,11 +1175,11 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, boolean decrease * * @param edge {@link SiteEdge} object. * @param flow {@code double} object. - * @param decrease {@code boolean} object. + * @param adjustment code for flow change * @return {@code int} object. */ - private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrease) { - int sign = decrease ? -1 : 1; + private int calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { + double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); @@ -1196,7 +1187,7 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea Function f = (double r) -> originalFlow - + sign * flow + + updatedFlow - calculateLocalFlow( r, edge.length, @@ -1221,14 +1212,14 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, boolean decrea * @param edge {@link SiteEdge} object. * @param intersection {@link SiteNode} object. * @param flow {@code double} object. - * @param decrease {@code boolean} object. + * @param adjustment code for flow change * @param ignored {@link ArrayList} of {@link SiteEdge} objects. */ private void updateRadiusToRoot( SiteEdge edge, SiteNode intersection, double flow, - boolean decrease, + Adjustment adjustment, ArrayList ignored) { ArrayList veins = sites.graphFactory.veins; ArrayList oldRadii = new ArrayList<>(); @@ -1244,12 +1235,12 @@ private void updateRadiusToRoot( continue; } if (e.getTo().isRoot) { - if (calculateVeinRootRadius(e, flow, decrease) == -1) { + if (calculateVeinRootRadius(e, flow, adjustment) == -1) { resetRadii(path, oldRadii); return; } } else { - if (calculateRadius(e, flow, decrease) == -1) { + if (calculateRadius(e, flow, adjustment) == -1) { resetRadii(path, oldRadii); return; } @@ -1262,8 +1253,8 @@ private void updateRadiusToRoot( /** * Private helper function for resetting the radii of an edge list. * - * @param edges {@link ArrayList} of {@link SiteEdge} objects. - * @param oldRadii {@link ArrayList} of {@code double} objects. + * @param edges {@link ArrayList} of {@link SiteEdge} objects to be reset + * @param oldRadii {@link ArrayList} of radii */ private void resetRadii(ArrayList edges, ArrayList oldRadii) { for (int i = 0; i < oldRadii.size(); i++) { @@ -1274,23 +1265,20 @@ private void resetRadii(ArrayList edges, ArrayList oldRadii) { /** * Private helper function for adding an edge list to the graph. * - * @param list {@link ArrayList} of {@link SiteEdge} objects. - * @param updateProperties {@code boolean} object. - * @param edgeType {@link EdgeType} object. + * @param list {@link ArrayList} of {@link SiteEdge}s to add to the graph */ - private void addEdgeList( - ArrayList list, boolean updateProperties, EdgeType edgeType) { + private void addEdgeList(ArrayList list) { for (SiteEdge edge : list) { graph.addEdge(edge); } } /** - * Private helper function for creating a new edge. + * Private helper function for creating a valid new edge. * - * @param direction {@link EdgeDirection} object. - * @param node {@link SiteNode} object. - * @return {@link SiteEdge} object. + * @param direction {@link EdgeDirection} describing the offset direction + * @param node the original {@link SiteNode} object + * @return the created {@link SiteEdge} object, or null if the edge would not be valid */ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { SiteNode proposed = sites.graphFactory.offsetNode(node, direction, DEFAULT_EDGE_LEVEL); From 51971584b9bcfd4594e3a78367384d512e03bca3 Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 15 Jun 2025 19:44:00 -0700 Subject: [PATCH 22/56] updated doc strings --- .../env/component/PatchComponentGrowth.java | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index ab7f3367e..e45a3d0db 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -507,9 +507,9 @@ private boolean checkNodeSkipStatus(SiteNode node) { } /** - * Reverses all edges in the angiogenicNodeMap for a given node. + * Private helper function for reversing all edges in the angiogenicNodeMap for a given node. * - * @param node {@link SiteNode} object. + * @param node {@link SiteNode} key to reverse edges for */ private void reverseAllEdges(SiteNode node) { for (SiteEdge edge : angiogenicNodeMap.get(node)) { @@ -518,12 +518,12 @@ private void reverseAllEdges(SiteNode node) { } /** - * Private helper function for finding the key node in the angiogenicNodeMap for a given target - * node and skip node. + * Private helper function for lazily searching the key node in the angiogenicNodeMap for a + * given target node and skip node. * - * @param targetNode {@link SiteNode} object. - * @param skipNode {@link SiteNode} object. - * @return {@link SiteNode} object. + * @param targetNode {@link SiteNode} the node to look for in the map list values + * @param skipNode {@link SiteNode} to ignore in the map + * @return {@link SiteNode} key for the targetNode object */ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { for (SiteNode keyNode : angiogenicNodeMap.keySet()) { @@ -543,9 +543,9 @@ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { /** * Private helper function for checking if an edge list contains a given target node. * - * @param edgeList {@link ArrayList} of {@link SiteEdge} objects. - * @param targetNode {@link SiteNode} object. - * @return {@code true} if the edge list contains the target node. + * @param edgeList {@link ArrayList} of {@link SiteEdge}s + * @param targetNode {@link SiteNode} to search for + * @return {@code true} if the edge list contains the target node */ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNode) { for (SiteEdge edge : edgeList) { @@ -569,7 +569,7 @@ private void addTemporaryEdges() { } } - /** Removes the proposed edges from the graph. */ + /** Removes the proposed edges from the temporary version of the graph. */ private void removeTemporaryEdges() { if (tempEdges.isEmpty()) { return; @@ -581,7 +581,7 @@ private void removeTemporaryEdges() { /** * Removes all edges in an edge list from the graph. * - * @param edgeList {@link ArrayList} of {@link SiteEdge} objects. + * @param edgeList {@link SiteEdge}s to remove */ private void removeEdgeList(ArrayList edgeList) { for (SiteEdge edge : edgeList) { @@ -1053,8 +1053,8 @@ private void recalculateRadii( * @param intersection downstream intersection {@link SiteNode} * @param flow flow adjustment through the edge * @param adjustment code for flow change - * @param ignored {@link ArrayList} of {@link SiteEdge} objects. - * @return {@code int} object. + * @param ignored list of {@link SiteEdge} to not update + * @return -1 for unsuccessful calculation, 0 for successful calculation */ private int updateRadius( SiteEdge edge, @@ -1071,10 +1071,10 @@ private int updateRadius( /** * Private helper function for updating the radii of an edge list. * - * @param edges {@link ArrayList} of {@link SiteEdge} objects. - * @param flow {@code double} object. + * @param edges list of edges to update + * @param flow new flow * @param adjustment code for flow change - * @return {@code int} object. + * @return -1 for unsuccessful calculation, 0 for successful calculation */ private int updateRadiiOfEdgeList( ArrayList edges, double flow, Adjustment adjustment) { @@ -1084,11 +1084,11 @@ private int updateRadiiOfEdgeList( /** * Private helper function for updating the radii of an edge list. * - * @param edges {@link ArrayList} of {@link SiteEdge} objects. - * @param flow {@code double} object. + * @param edges list of edges to update + * @param flow new flow * @param adjustment code for flow change - * @param ignored {@link ArrayList} of {@link SiteEdge} objects. - * @return {@code int} object. + * @param ignored list of {@link SiteEdge} to not update + * @return -1 for unsuccessful calculation, 0 for successful calculation */ private int updateRadiiOfEdgeList( ArrayList edges, @@ -1112,8 +1112,8 @@ private int updateRadiiOfEdgeList( /** * Private helper function for calculating the radius of an edge. * - * @param edge {@link SiteEdge} object. - * @param flow {@code double} object. + * @param edge edge to update + * @param flow new flow * @param adjustment code for flow change * @return 0 for successful update, -1 for failure */ @@ -1139,9 +1139,9 @@ private int calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { * Private helper function for calculating the radius of an edge. * * @param edge {@link SiteEdge} object. - * @param flow {@code double} object. + * @param flow new flow * @param adjustment code for flow change - * @return {@code int} object. + * @return -1 for unsuccessful calculation, 0 for successful calculation */ private int calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; @@ -1174,9 +1174,9 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjus * Private helper function for calculating the radius of an edge. * * @param edge {@link SiteEdge} object. - * @param flow {@code double} object. + * @param flow new flow * @param adjustment code for flow change - * @return {@code int} object. + * @return -1 for unsuccessful calculation, 0 for successful calculation */ private int calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; @@ -1209,11 +1209,11 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adj /** * Private helper function for updating the radius of an edge. * - * @param edge {@link SiteEdge} object. - * @param intersection {@link SiteNode} object. - * @param flow {@code double} object. + * @param edge edge to update + * @param intersection downstream {@link SiteNode} intersection + * @param flow new flow * @param adjustment code for flow change - * @param ignored {@link ArrayList} of {@link SiteEdge} objects. + * @param ignored list of {@link SiteEdge} to not update */ private void updateRadiusToRoot( SiteEdge edge, From eb06923f2b3341a0fa7d29faaf3b8792231980e7 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 17 Jun 2025 14:04:31 -0700 Subject: [PATCH 23/56] updated for consistency --- src/arcade/core/util/Graph.java | 19 +++++++++++++++++++ src/arcade/core/util/Solver.java | 10 +++++----- test/arcade/core/util/GraphTest.java | 3 ++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index f839f5e28..bf25bc76d 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -57,6 +57,24 @@ public void update(Graph graph) { allEdges.addAll(graph.allEdges); nodeToOutBag.putAll(graph.nodeToOutBag); nodeToInBag.putAll(graph.nodeToInBag); + updateNodes(); + } + + /** Updates nodes from graph. */ + private void updateNodes() { + nodes.clear(); + for (Object obj : allEdges) { + Edge edge = (Edge) obj; + Node from = edge.getFrom(); + Node to = edge.getTo(); + + if (!nodes.containsKey(from.toString())) { + nodes.put(from.toString(), from); + } + if (!nodes.containsKey(to.toString())) { + nodes.put(to.toString(), to); + } + } } /** Clear edges and nodes from graph. */ @@ -64,6 +82,7 @@ public void clear() { allEdges.clear(); nodeToOutBag.clear(); nodeToInBag.clear(); + nodes.clear(); } /** diff --git a/src/arcade/core/util/Solver.java b/src/arcade/core/util/Solver.java index 9b98563ca..88800297a 100644 --- a/src/arcade/core/util/Solver.java +++ b/src/arcade/core/util/Solver.java @@ -428,7 +428,7 @@ private static double[] sparseSOR( * @param a the lower bound on the interval * @param b the upper bound on the interval * @param maxIters the maximum number of iterations - * @param tolerance the error tolerance + * @param tolerance the error * @return the root of the function */ public static double bisection( @@ -494,11 +494,11 @@ public static double bisection(Function func, double a, double b) { * @param func the function * @param a the lower bound on the interval * @param b the upper bound on the interval - * @param tolerance the error tolerance + * @param delta the error tolerance * @return the root of the function */ - public static double bisection(Function func, double a, double b, double tolerance) { - return bisection(func, a, b, MAX_ITERS, tolerance); + public static double bisection(Function func, double a, double b, double delta) { + return bisection(func, a, b, MAX_ITERS, delta); } /** @@ -514,6 +514,6 @@ public static double bisection(Function func, double a, double b, double toleran * @return the root of the function */ public static double bisection(Function func, double a, double b, int maxIters) { - return bisection(func, a, b, maxIters, TOLERANCE); + return bisection(func, a, b, maxIters, DELTA); } } diff --git a/test/arcade/core/util/GraphTest.java b/test/arcade/core/util/GraphTest.java index ee55542da..984f2ef45 100644 --- a/test/arcade/core/util/GraphTest.java +++ b/test/arcade/core/util/GraphTest.java @@ -797,7 +797,8 @@ public void update_updatesGraph() { () -> assertTrue(graph.contains(edge3)), () -> assertTrue(graph.contains(edge4)), () -> assertTrue(graph.contains(edge5)), - () -> assertTrue(graph.contains(edge6))); + () -> assertTrue(graph.contains(edge6)), + () -> assertEquals(graph.lookup(node3B), graph.lookup(new Node(-2, -2, 0)))); } @Test From 4d2fe520377aeea0802d0dda71ab669f23467315 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 17 Jun 2025 14:38:32 -0700 Subject: [PATCH 24/56] added input argument into string --- .../core/util/exceptions/MissingSpecificationException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arcade/core/util/exceptions/MissingSpecificationException.java b/src/arcade/core/util/exceptions/MissingSpecificationException.java index 10960e771..4a175a798 100644 --- a/src/arcade/core/util/exceptions/MissingSpecificationException.java +++ b/src/arcade/core/util/exceptions/MissingSpecificationException.java @@ -8,6 +8,6 @@ public class MissingSpecificationException extends RuntimeException { * @param missing the expected specification */ public MissingSpecificationException(String missing) { - super("The input file must contain the following specification: "); + super("The input file must contain the following specification: " + missing); } } From a7572c19bbfeca561cd49ed3d43c7c01330defca Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 17 Jun 2025 18:57:58 -0700 Subject: [PATCH 25/56] removed some useless code --- .../patch/env/component/PatchComponentGrowth.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index e45a3d0db..560801069 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -176,15 +176,6 @@ public PatchComponentGrowth(Series series, MiniBox parameters) { calculationStrategy = Calculation.valueOf(parameters.get("CALCULATION_STRATEGY")); } - /** - * Component does not have a relevant field; returns {@code null}. - * - * @return {@code null} - */ - public double[][][] getField() { - return null; - } - @Override public void schedule(Schedule schedule) { interval = migrationRate < edgeSize ? 60 : (int) (edgeSize / migrationRate * 60); @@ -233,8 +224,6 @@ private LinkedHashSet getValidNodes() { } SiteNode from = edge.getFrom(); SiteNode to = edge.getTo(); - from.id = -1; - to.id = -1; if ((graph.getDegree(from) < 3) && !from.isRoot @@ -497,9 +486,6 @@ private boolean checkNodeSkipStatus(SiteNode node) { if (angiogenicNodeMap.keySet().contains(node)) { return true; } - if (node.isRoot) { - return true; - } if ((tick - node.addTime) < (72 * 60)) { return true; } From 98ad1bf0e7c2d12102835ad07ec23efc3e514a54 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 17 Jun 2025 19:53:40 -0700 Subject: [PATCH 26/56] added doc string --- src/arcade/patch/env/component/PatchComponentGrowth.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 560801069..8d2537482 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -90,6 +90,7 @@ public enum MigrationDirection { BIASED; } + /** How to adjust the flow rate during radius calculations. */ private enum Adjustment { /** Code for increasing flow. */ INCREASE, From bf1ca4c8b4f1b9a7fbe7a3b11b86eaa6722de628 Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 23 Jun 2025 15:25:35 -0700 Subject: [PATCH 27/56] cleaned up some error passing --- .../env/component/PatchComponentGrowth.java | 110 ++++++++++-------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 8d2537482..41be807dc 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -99,6 +99,14 @@ private enum Adjustment { DECREASE; } + private enum Outcome { + /** Code for successful calculation. */ + SUCCESS, + + /** Code for unsuccessful calculation. */ + FAILURE; + } + /** Rate of migration. */ private double migrationRate; @@ -820,9 +828,8 @@ private void addAngioEdges( (SiteNode) graph.findDownstreamIntersection( (SiteEdge) outEdges.get(0), (SiteEdge) angioPath.get(0)); - if (intersection != null) { - recalculateRadii(angioPath, start, end, intersection); - } else { + Outcome result = recalculateRadii(angioPath, start, end, intersection); + if (result == Outcome.FAILURE) { removeEdgeList(angioPath); } break; @@ -901,11 +908,12 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteEdge rootEdge = path.remove(0); - if (calculateArteryRootRadius(rootEdge, arteryFlow, Adjustment.INCREASE) == -1) { + if (calculateArteryRootRadius(rootEdge, arteryFlow, Adjustment.INCREASE) + == Outcome.FAILURE) { failed = true; break; } - if (updateRadiiOfEdgeList(path, arteryFlow, Adjustment.INCREASE) == -1) { + if (updateRadiiOfEdgeList(path, arteryFlow, Adjustment.INCREASE) == Outcome.FAILURE) { failed = true; break; } @@ -934,11 +942,12 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteEdge rootEdge = path.remove(path.size() - 1); oldRadii.add(rootEdge.radius); updatedEdges.add(rootEdge); - if (calculateArteryRootRadius(rootEdge, veinFlow, Adjustment.INCREASE) == -1) { + if (calculateArteryRootRadius(rootEdge, veinFlow, Adjustment.INCREASE) + == Outcome.FAILURE) { failed = true; break; } - if (updateRadiiOfEdgeList(path, veinFlow, Adjustment.DECREASE) == -1) { + if (updateRadiiOfEdgeList(path, veinFlow, Adjustment.DECREASE) == Outcome.FAILURE) { failed = true; break; } @@ -963,17 +972,17 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, * @param end ending {@link SiteNode} * @param intersection the intersecting node object from the edges out of start */ - private void recalculateRadii( + private Outcome recalculateRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { updateGraph(graph); Bag edges = graph.getEdgesOut(start); if (edges == null) { - return; + return Outcome.FAILURE; } if (edges.size() < 2) { - return; + return Outcome.FAILURE; } Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; @@ -982,17 +991,16 @@ private void recalculateRadii( Double divertedFlow = calculateLocalFlow(CAPILLARY_RADIUS, ignoredEdges, deltaP); Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { - return; + return Outcome.FAILURE; } if (intersection != null) { if (intersection.isRoot) { - updateRadiusToRoot( + return updateRadiusToRoot( (SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, Adjustment.INCREASE, ignoredEdges); - return; } if (updateRadius( @@ -1001,8 +1009,8 @@ private void recalculateRadii( divertedFlow, Adjustment.DECREASE, ignoredEdges) - == -1) { - return; + == Outcome.FAILURE) { + return Outcome.FAILURE; } if (updateRadius( @@ -1011,8 +1019,8 @@ private void recalculateRadii( divertedFlow, Adjustment.INCREASE, ignoredEdges) - == -1) { - return; + == Outcome.FAILURE) { + return Outcome.FAILURE; } } else { @@ -1021,16 +1029,16 @@ private void recalculateRadii( path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { - updateRadiusToRoot( + return updateRadiusToRoot( (SiteEdge) edges.get(angioIndex), sites.graphFactory.veins.get(0).node, divertedFlow, Adjustment.INCREASE, ignoredEdges); } - break; } } + return Outcome.FAILURE; } /** @@ -1041,9 +1049,9 @@ private void recalculateRadii( * @param flow flow adjustment through the edge * @param adjustment code for flow change * @param ignored list of {@link SiteEdge} to not update - * @return -1 for unsuccessful calculation, 0 for successful calculation + * @return Outcome.FAILURE for unsuccessful calculation, 0 for successful calculation */ - private int updateRadius( + private Outcome updateRadius( SiteEdge edge, SiteNode intersection, double flow, @@ -1056,14 +1064,14 @@ private int updateRadius( } /** - * Private helper function for updating the radii of an edge list. + * Private helper function for updating the radii of an edge list without ignoring edges. * * @param edges list of edges to update * @param flow new flow * @param adjustment code for flow change - * @return -1 for unsuccessful calculation, 0 for successful calculation + * @return Outcome.FAILURE for unsuccessful calculation, 0 for successful calculation */ - private int updateRadiiOfEdgeList( + private Outcome updateRadiiOfEdgeList( ArrayList edges, double flow, Adjustment adjustment) { return updateRadiiOfEdgeList(edges, flow, adjustment, new ArrayList<>()); } @@ -1075,9 +1083,9 @@ private int updateRadiiOfEdgeList( * @param flow new flow * @param adjustment code for flow change * @param ignored list of {@link SiteEdge} to not update - * @return -1 for unsuccessful calculation, 0 for successful calculation + * @return Outcome.FAILURE for unsuccessful calculation, 0 for successful calculation */ - private int updateRadiiOfEdgeList( + private Outcome updateRadiiOfEdgeList( ArrayList edges, double flow, Adjustment adjustment, @@ -1088,23 +1096,23 @@ private int updateRadiiOfEdgeList( if (ignored.contains(e)) { continue; } - if (calculateRadius(e, flow, adjustment) == -1) { + if (calculateRadius(e, flow, adjustment) == Outcome.FAILURE) { resetRadii(edges, oldRadii); - return -1; + return Outcome.FAILURE; } } - return 0; + return Outcome.SUCCESS; } /** - * Private helper function for calculating the radius of an edge. + * Private helper function for calculating the radius of an edge based on a change in flow. * * @param edge edge to update * @param flow new flow * @param adjustment code for flow change - * @return 0 for successful update, -1 for failure + * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ - private int calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { + private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; @@ -1116,21 +1124,21 @@ private int calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); if (newRadius == 1E-6) { - return -1; + return Outcome.FAILURE; } edge.radius = newRadius; - return 0; + return Outcome.SUCCESS; } /** - * Private helper function for calculating the radius of an edge. + * Private helper function for calculating the radius of an edge associated with a vein root. * * @param edge {@link SiteEdge} object. * @param flow new flow * @param adjustment code for flow change - * @return -1 for unsuccessful calculation, 0 for successful calculation + * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ - private int calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { + private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; @@ -1149,23 +1157,23 @@ private int calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjus double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); if (newRadius == .5 * originalRadius || newRadius == Double.NaN) { - return -1; + return Outcome.FAILURE; } edge.radius = newRadius; edge.getTo().pressure = calculatePressure(newRadius, edge.type.category); - return 0; + return Outcome.SUCCESS; } /** - * Private helper function for calculating the radius of an edge. + * Private helper function for calculating the radius of an edge when updating the source root. * * @param edge {@link SiteEdge} object. * @param flow new flow * @param adjustment code for flow change - * @return -1 for unsuccessful calculation, 0 for successful calculation + * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ - private int calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { + private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; @@ -1185,24 +1193,25 @@ private int calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adj if (newRadius == .5 * originalRadius || newRadius == Double.NaN || newRadius == 1.5 * originalRadius) { - return -1; + return Outcome.FAILURE; } edge.radius = newRadius; edge.getFrom().pressure = calculatePressure(newRadius, edge.type.category); - return 0; + return Outcome.SUCCESS; } /** - * Private helper function for updating the radius of an edge. + * Private helper function for updating the radius of an edge to the sink root node. * * @param edge edge to update * @param intersection downstream {@link SiteNode} intersection * @param flow new flow * @param adjustment code for flow change * @param ignored list of {@link SiteEdge} to not update + * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ - private void updateRadiusToRoot( + private Outcome updateRadiusToRoot( SiteEdge edge, SiteNode intersection, double flow, @@ -1222,19 +1231,20 @@ private void updateRadiusToRoot( continue; } if (e.getTo().isRoot) { - if (calculateVeinRootRadius(e, flow, adjustment) == -1) { + if (calculateVeinRootRadius(e, flow, adjustment) == Outcome.FAILURE) { resetRadii(path, oldRadii); - return; + return Outcome.FAILURE; } } else { - if (calculateRadius(e, flow, adjustment) == -1) { + if (calculateRadius(e, flow, adjustment) == Outcome.FAILURE) { resetRadii(path, oldRadii); - return; + return Outcome.FAILURE; } } } break; } + return Outcome.SUCCESS; } /** From a19778e14533fc6ca9f096970b5dddebcd420a37 Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 27 Jun 2025 12:07:20 -0700 Subject: [PATCH 28/56] i have a lot of logging stuff right now --- .../patch/agent/cell/PatchCellTissue.java | 5 + .../process/PatchProcessSensingSimple.java | 4 +- .../env/component/PatchComponentDegrade.java | 6 + .../env/component/PatchComponentGrowth.java | 151 +++++++++++++----- .../env/component/PatchComponentRemodel.java | 3 + .../component/PatchComponentSitesGraph.java | 2 +- .../PatchComponentSitesGraphUtilities.java | 23 ++- src/arcade/patch/sim/PatchSimulationHex.java | 3 + 8 files changed, 150 insertions(+), 47 deletions(-) diff --git a/src/arcade/patch/agent/cell/PatchCellTissue.java b/src/arcade/patch/agent/cell/PatchCellTissue.java index 352260b28..398b05147 100644 --- a/src/arcade/patch/agent/cell/PatchCellTissue.java +++ b/src/arcade/patch/agent/cell/PatchCellTissue.java @@ -134,6 +134,11 @@ public void step(SimState simstate) { if (module != null) { module.step(simstate.random, sim); } + + // Step remaining processes. + if (processes.get(Domain.SENSING) != null) { + processes.get(Domain.SENSING).step(simstate.random, sim); + } } /** diff --git a/src/arcade/patch/agent/process/PatchProcessSensingSimple.java b/src/arcade/patch/agent/process/PatchProcessSensingSimple.java index c81ba8cca..c9d2c3895 100644 --- a/src/arcade/patch/agent/process/PatchProcessSensingSimple.java +++ b/src/arcade/patch/agent/process/PatchProcessSensingSimple.java @@ -1,5 +1,6 @@ package arcade.patch.agent.process; +import java.util.logging.Logger; import ec.util.MersenneTwisterFast; import arcade.core.agent.process.Process; import arcade.core.sim.Simulation; @@ -13,6 +14,8 @@ * added to the environment at the rate specified by the input parameter VEGF_SECRETION_RATE. */ public class PatchProcessSensingSimple extends PatchProcessSensing { + private static final Logger LOGGER = + Logger.getLogger(PatchProcessSensingSimple.class.getName()); /** Rate of secretion of VEGF [VEGF/min]. */ private final double secretionRate; @@ -32,7 +35,6 @@ public class PatchProcessSensingSimple extends PatchProcessSensing { */ public PatchProcessSensingSimple(PatchCell cell) { super(cell); - secretionRate = cell.getParameters().getDouble("sensing/VEGF_SECRETION_RATE"); } diff --git a/src/arcade/patch/env/component/PatchComponentDegrade.java b/src/arcade/patch/env/component/PatchComponentDegrade.java index 35da75184..76662aa89 100644 --- a/src/arcade/patch/env/component/PatchComponentDegrade.java +++ b/src/arcade/patch/env/component/PatchComponentDegrade.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashSet; +import java.util.logging.Logger; import sim.engine.Schedule; import sim.engine.SimState; import sim.util.Bag; @@ -13,8 +14,10 @@ import arcade.core.util.Graph; import arcade.core.util.MiniBox; import arcade.patch.agent.cell.PatchCellCancer; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.grid.PatchGrid; import arcade.patch.env.location.CoordinateXYZ; +import arcade.patch.util.PatchEnums.Ordering; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_WALL_THICKNESS; import static arcade.patch.util.PatchEnums.Ordering; @@ -31,6 +34,8 @@ * properties are recalculated. */ public class PatchComponentDegrade implements Component { + private static final Logger LOGGER = Logger.getLogger(PatchComponentDegrade.class.getName()); + /** Interval between degradation steps [min]. */ private final int degradationInterval; @@ -128,6 +133,7 @@ public void step(SimState state) { // If any edges are removed, update the graph edges that are ignored. // Otherwise, recalculate calculate stresses. if (removed) { + LOGGER.info("REMOVING EDGES."); PatchComponentSitesGraphUtilities.updateGraph(graph); } else { PatchComponentSitesGraphUtilities.calculateStresses(graph); diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 41be807dc..a0338c22b 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -64,7 +64,7 @@ public class PatchComponentGrowth implements Component { private static final Logger LOGGER = Logger.getLogger(PatchComponentGrowth.class.getName()); /** Default edge level to add to the graph from this component. */ - private static final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_1; + private static final EdgeLevel DEFAULT_EDGE_LEVEL = EdgeLevel.LEVEL_2; /** Default edge type to add to the graph from this component. */ private static final EdgeType DEFAULT_EDGE_TYPE = EdgeType.ANGIOGENIC; @@ -144,7 +144,7 @@ private enum Outcome { private HashMap> angiogenicNodeMap = new HashMap<>(); /** List of temporary edges to be added to the graph this step. */ - private ArrayList tempEdges; + private ArrayList tempEdges = new ArrayList<>(); /** Number of offsets to be used in the migration. */ private int numOffsets; @@ -159,7 +159,7 @@ private enum Outcome { private int tick; /** List of nodes to be removed from the angiogenic node map this time step. */ - private ArrayList nodesToRemove; + private ArrayList nodesToRemove = new ArrayList<>(); /** * Creates a growth component for a {@link PatchComponentSitesGraph}. @@ -234,6 +234,13 @@ private LinkedHashSet getValidNodes() { SiteNode from = edge.getFrom(); SiteNode to = edge.getTo(); + if (from.pressure == Double.NaN) { + LOGGER.info("Uh Oh. Pressure is NaN. @ " + from); + } + if (to.pressure == Double.NaN) { + LOGGER.info("Uh Oh. Pressure is NaN. @ " + to); + } + if ((graph.getDegree(from) < 3) && !from.isRoot && !(graph.getInDegree(from) == 0 && graph.getOutDegree(from) == 1)) { @@ -277,6 +284,7 @@ private EdgeDirection performWalk( @Override public void step(SimState simstate) { tick = (int) simstate.schedule.getTime(); + MersenneTwisterFast random = simstate.random; LinkedHashSet validNodes = getValidNodes(); @@ -361,6 +369,11 @@ public void step(SimState simstate) { nodesToRemove.add(sproutNode); nodesToRemove.add(targetNode); } else { + if (finalNode.sproutDir != null + || finalNode.anastomosis + || finalNode.isRoot) { + continue; + } // Connecting sprout to existing node if (sproutNode.pressure == 0) { if (graph.getEdgesOut(sproutNode) != null) { @@ -418,36 +431,35 @@ private boolean propogateEdges() { boolean addFlag = false; addTemporaryEdges(); - for (Map.Entry> entry : angiogenicNodeMap.entrySet()) { - // Grab node in each list and add edge, check for perfusion - SiteNode keyNode = entry.getKey(); - - if (checkForIgnoredEdges(keyNode)) { - nodesToRemove.add(keyNode); + for (SiteNode sprout : angiogenicNodeMap.keySet()) { + if (checkForIgnoredEdges(sprout)) { + nodesToRemove.add(sprout); continue; } - ArrayList edgeList = entry.getValue(); + ArrayList edgeList = angiogenicNodeMap.get(sprout); SiteNode tipNode; SiteEdge newEdge; + if (edgeList.size() > 0) { tipNode = edgeList.get(edgeList.size() - 1).getTo(); } else { - tipNode = keyNode; + tipNode = sprout; } if (tick - tipNode.lastUpdate < migrationRate) { continue; } - newEdge = createNewEdge(keyNode.sproutDir, tipNode); + newEdge = createNewEdge(sprout.sproutDir, tipNode); - if (edgeList.size() > maxEdges || newEdge == null || graph.getDegree(keyNode) > 3) { - nodesToRemove.add(keyNode); + if (edgeList.size() == maxEdges || newEdge == null || graph.getDegree(sprout) > 3) { + nodesToRemove.add(sprout); + continue; } else { edgeList.add(newEdge); if (newEdge.isAnastomotic) { - keyNode.anastomosis = true; + sprout.anastomosis = true; addFlag = true; } } @@ -522,10 +534,10 @@ private void reverseAllEdges(SiteNode node) { */ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { for (SiteNode keyNode : angiogenicNodeMap.keySet()) { - if (keyNode == skipNode) { + if (keyNode.equals(skipNode)) { continue; } - if (keyNode == targetNode) { + if (keyNode.equals(targetNode)) { return keyNode; } if (edgeListcontains(angiogenicNodeMap.get(keyNode), targetNode)) { @@ -544,7 +556,7 @@ private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { */ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNode) { for (SiteEdge edge : edgeList) { - if (edge.getTo() == targetNode) { + if (edge.getTo().equals(targetNode)) { return true; } } @@ -556,7 +568,6 @@ private boolean edgeListcontains(ArrayList edgeList, SiteNode targetNo * angiogenic node map. */ private void addTemporaryEdges() { - tempEdges = new ArrayList<>(); for (Map.Entry> entry : angiogenicNodeMap.entrySet()) { ArrayList edgeList = entry.getValue(); tempEdges.addAll(edgeList); @@ -783,6 +794,13 @@ private double averageDirectionalMap(EnumMap> m */ private void addAngioEdges( ArrayList list, SiteNode start, SiteNode end, Calculation calc) { + + LOGGER.info("ADDING ANGIOGENIC EDGES."); + LOGGER.info("TRYING TO ADD: " + start.id + " -> " + end.id); + LOGGER.info("LIST: " + list); + if (list.size() > 1) { + LOGGER.info("LIST SIZE: " + list.size()); + } // check for cycle path(graph, end, start); if (end.prev != null) { @@ -804,12 +822,24 @@ private void addAngioEdges( // update edges in the minimal path between start and end ArrayList angioPath = getPath(tempGraph, start, end); + + if (angioPath == null) { + LOGGER.info("START: " + start); + LOGGER.info("END: " + end); + LOGGER.info("The list is: " + list); + LOGGER.info("angioPath is null."); + return; + } + for (SiteEdge edge : angioPath) { edge.getTo().addTime = tick; edge.radius = (otherRadius > CAPILLARY_RADIUS) ? CAPILLARY_RADIUS : calculateEvenSplitRadius((SiteEdge) outEdges.get(0)); + if (Double.isNaN(edge.radius)) { + return; + } edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); @@ -832,6 +862,7 @@ private void addAngioEdges( if (result == Outcome.FAILURE) { removeEdgeList(angioPath); } + LOGGER.info("Added " + angioPath.size() + " edges."); break; } } @@ -848,12 +879,17 @@ private double calculateEvenSplitRadius(SiteEdge edge) { double length = edge.length; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); - double newRadius = - Solver.bisection( - (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), - 1E-6, - 5 * MAXIMUM_CAPILLARY_RADIUS, - 1E-6); + double newRadius; + try { + newRadius = + Solver.bisection( + (double r) -> flow - 2 * calculateLocalFlow(r, length, deltaP), + 1E-6, + 5 * MAXIMUM_CAPILLARY_RADIUS, + 1E-6); + } catch (Exception e) { + return Double.NaN; + } return newRadius; } @@ -995,12 +1031,13 @@ private Outcome recalculateRadii( } if (intersection != null) { if (intersection.isRoot) { - return updateRadiusToRoot( - (SiteEdge) edges.get(angioIndex), - sites.graphFactory.veins.get(0).node, - divertedFlow, - Adjustment.INCREASE, - ignoredEdges); + // return updateRadiusToRoot( + // (SiteEdge) edges.get(angioIndex), + // sites.graphFactory.veins.get(0).node, + // divertedFlow, + // Adjustment.INCREASE, + // ignoredEdges); + return Outcome.FAILURE; } if (updateRadius( @@ -1029,12 +1066,13 @@ private Outcome recalculateRadii( path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { - return updateRadiusToRoot( - (SiteEdge) edges.get(angioIndex), - sites.graphFactory.veins.get(0).node, - divertedFlow, - Adjustment.INCREASE, - ignoredEdges); + return Outcome.FAILURE; + // return updateRadiusToRoot( + // (SiteEdge) edges.get(angioIndex), + // sites.graphFactory.veins.get(0).node, + // divertedFlow, + // Adjustment.INCREASE, + // ignoredEdges); } } } @@ -1058,6 +1096,12 @@ private Outcome updateRadius( Adjustment adjustment, ArrayList ignored) { ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); + + if (edgesToUpdate == null) { + LOGGER.info("No path found from " + edge.getTo() + " to " + intersection); + return Outcome.FAILURE; + } + edgesToUpdate.add(0, edge); return updateRadiiOfEdgeList(edgesToUpdate, flow, adjustment, ignored); @@ -1113,6 +1157,7 @@ private Outcome updateRadiiOfEdgeList( * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { + assert flow >= 0; double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; @@ -1121,7 +1166,12 @@ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustmen (double r) -> originalFlow + updatedFlow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; - newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); + + try { + newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); + } catch (Exception e) { + return Outcome.FAILURE; + } if (newRadius == 1E-6) { return Outcome.FAILURE; @@ -1139,6 +1189,8 @@ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustmen * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { + assert flow >= 0; + double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; @@ -1154,7 +1206,12 @@ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment a edge.getFrom().pressure - calculatePressure(r, edge.type.category)); - double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); + double newRadius; + try { + newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); + } catch (Exception e) { + return Outcome.FAILURE; + } if (newRadius == .5 * originalRadius || newRadius == Double.NaN) { return Outcome.FAILURE; @@ -1174,6 +1231,8 @@ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment a * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { + assert flow >= 0; + double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; @@ -1189,7 +1248,13 @@ private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment calculatePressure(r, edge.type.category) - edge.getTo().pressure); - double newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); + double newRadius; + try { + newRadius = Solver.bisection(f, .5 * originalRadius, 1.5 * originalRadius); + } catch (Exception e) { + return Outcome.FAILURE; + } + if (newRadius == .5 * originalRadius || newRadius == Double.NaN || newRadius == 1.5 * originalRadius) { @@ -1217,6 +1282,9 @@ private Outcome updateRadiusToRoot( double flow, Adjustment adjustment, ArrayList ignored) { + + assert flow >= 0; + ArrayList veins = sites.graphFactory.veins; ArrayList oldRadii = new ArrayList<>(); for (Root vein : veins) { @@ -1289,6 +1357,9 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { return null; } SiteNode existing = (SiteNode) graph.lookup(proposed); + + assert existing != null; + edge = new SiteEdge(node, existing, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); edge.isAnastomotic = true; return edge; diff --git a/src/arcade/patch/env/component/PatchComponentRemodel.java b/src/arcade/patch/env/component/PatchComponentRemodel.java index b2e1b4f6d..521d0662d 100644 --- a/src/arcade/patch/env/component/PatchComponentRemodel.java +++ b/src/arcade/patch/env/component/PatchComponentRemodel.java @@ -1,5 +1,6 @@ package arcade.patch.env.component; +import java.util.logging.Logger; import sim.engine.Schedule; import sim.engine.SimState; import sim.util.Bag; @@ -28,6 +29,8 @@ * All hemodynamic properties are recalculated at the end of the step. */ public class PatchComponentRemodel implements Component { + private static final Logger LOGGER = Logger.getLogger(PatchComponentRemodel.class.getName()); + /** Interval between remodeling steps [min]. */ private final int remodelingInterval; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 5ee88bddf..aa86f134f 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -430,7 +430,7 @@ public static class SiteNode extends Node { } @Override - public Node duplicate() { + public SiteNode duplicate() { return new SiteNode(x, y, z); } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index bd10d7e58..dfb79b8a2 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.logging.Logger; import sim.util.Bag; import ec.util.MersenneTwisterFast; import arcade.core.util.Graph; @@ -20,6 +21,9 @@ /** Container for utility functions used by {@link PatchComponentSitesGraph}. */ abstract class PatchComponentSitesGraphUtilities { + private static final Logger LOGGER = + Logger.getLogger(PatchComponentSitesGraphUtilities.class.getName()); + /** Calculation types. */ enum CalculationType { /** Code for upstream radius calculation for all edge types. */ @@ -944,7 +948,7 @@ static void path(Graph graph, SiteNode start, SiteNode end) { settled.add(evalNode); // If end node found, exit from loop. - if (evalNode == end) { + if (evalNode.equals(end)) { break; } @@ -986,25 +990,34 @@ static void path(Graph graph, SiteNode start, SiteNode end) { static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { path(graph, start, end); ArrayList path = new ArrayList<>(); - SiteNode node = end; - while (node != null && node != start) { + SiteNode node = (SiteNode) graph.lookup(end); + while (node != null && !node.equals(start)) { Bag b = graph.getEdgesIn(node); if (b.numObjs == 1) { path.add((SiteEdge) b.objs[0]); } else if (b.numObjs == 2) { SiteEdge edgeA = ((SiteEdge) b.objs[0]); SiteEdge edgeB = ((SiteEdge) b.objs[1]); - if (edgeA.getFrom() == node.prev) { + if (edgeA.getFrom().equals(node.prev)) { path.add(edgeA); } else { path.add(edgeB); } } + + if (node.prev == null) { + LOGGER.info("START: " + start + " END: " + end); + LOGGER.info("PREV IS NULL" + node); + } + node = node.prev; } - if (node != start) { + + if (node == null) { + LOGGER.info("Path in getPath is: " + path); return null; } + Collections.reverse(path); return path; } diff --git a/src/arcade/patch/sim/PatchSimulationHex.java b/src/arcade/patch/sim/PatchSimulationHex.java index 6eddf28b5..24da8a3cd 100644 --- a/src/arcade/patch/sim/PatchSimulationHex.java +++ b/src/arcade/patch/sim/PatchSimulationHex.java @@ -10,6 +10,7 @@ import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentCycle; import arcade.patch.env.component.PatchComponentDegrade; +import arcade.patch.env.component.PatchComponentGrowth; import arcade.patch.env.component.PatchComponentPulse; import arcade.patch.env.component.PatchComponentRemodel; import arcade.patch.env.component.PatchComponentSitesGraphTri; @@ -82,6 +83,8 @@ public Component makeComponent(String componentClass, MiniBox parameters) { return new PatchComponentDegrade(series, parameters); case "remodel": return new PatchComponentRemodel(series, parameters); + case "growth": + return new PatchComponentGrowth(series, parameters); default: return null; } From 28dfc5c27c6428ee1a2db85222fe23b046ec7046 Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 29 Jun 2025 20:49:12 -0700 Subject: [PATCH 29/56] it's a different bug now --- .../env/component/PatchComponentGrowth.java | 26 +++++++++---------- src/arcade/patch/parameter.patch.xml | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index a0338c22b..a9571c33d 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -39,7 +39,6 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.reversePressures; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.util.PatchEnums.Ordering; /** @@ -284,7 +283,6 @@ private EdgeDirection performWalk( @Override public void step(SimState simstate) { tick = (int) simstate.schedule.getTime(); - MersenneTwisterFast random = simstate.random; LinkedHashSet validNodes = getValidNodes(); @@ -341,6 +339,10 @@ public void step(SimState simstate) { SiteNode init; SiteNode fin; + LOGGER.info(" SPROUT NODE: " + sproutNode); + LOGGER.info("LEADING INDEX: " + leadingIndex); + LOGGER.info("FINAL NODE: " + finalNode); + calculatePressures(graph); boolean reversed = reversePressures(graph); if (reversed) { @@ -350,6 +352,7 @@ public void step(SimState simstate) { calculateStresses(graph); if (!graph.contains(finalNode)) { + LOGGER.info("TRYING TO CONNECT ANGIOGENIC NODES."); // Connecting two angiogenic nodes SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); if (targetNode == null) { @@ -410,10 +413,6 @@ public void step(SimState simstate) { angiogenicNodeMap.remove(n); } nodesToRemove.clear(); - - if (addFlag) { - updateGraph(graph); - } } /** @@ -795,7 +794,7 @@ private double averageDirectionalMap(EnumMap> m private void addAngioEdges( ArrayList list, SiteNode start, SiteNode end, Calculation calc) { - LOGGER.info("ADDING ANGIOGENIC EDGES."); + LOGGER.info("ADDING ANGIOGENIC EDGES. TICK: " + tick); LOGGER.info("TRYING TO ADD: " + start.id + " -> " + end.id); LOGGER.info("LIST: " + list); if (list.size() > 1) { @@ -861,8 +860,10 @@ private void addAngioEdges( Outcome result = recalculateRadii(angioPath, start, end, intersection); if (result == Outcome.FAILURE) { removeEdgeList(angioPath); + LOGGER.info("Failed to add " + list + "."); + } else { + LOGGER.info("Added " + angioPath.size() + " edges."); } - LOGGER.info("Added " + angioPath.size() + " edges."); break; } } @@ -901,7 +902,6 @@ private double calculateEvenSplitRadius(SiteEdge edge) { * @param end the ending {@link SiteNode} */ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, SiteNode end) { - updateGraph(graph); ArrayList oldRadii = new ArrayList<>(); ArrayList updatedEdges = new ArrayList<>(); Boolean failed = false; @@ -1011,7 +1011,6 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, private Outcome recalculateRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { - updateGraph(graph); Bag edges = graph.getEdgesOut(start); if (edges == null) { @@ -1076,7 +1075,7 @@ private Outcome recalculateRadii( } } } - return Outcome.FAILURE; + return Outcome.SUCCESS; } /** @@ -1157,11 +1156,12 @@ private Outcome updateRadiiOfEdgeList( * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { - assert flow >= 0; + assert flow > 0; double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); + // double originalFlow = edge.flow; Function f = (double r) -> originalFlow + updatedFlow - calculateLocalFlow(r, edge.length, deltaP); @@ -1232,7 +1232,7 @@ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment a */ private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { assert flow >= 0; - + double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index ed1ea0750..7d4034b61 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -166,9 +166,9 @@ \ - + - + From cab2f028f458e52d615d7ff7955f46ca4c800d75 Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 3 Jul 2025 10:55:24 -0700 Subject: [PATCH 30/56] updated some matrix maths, adding some log statements trying to figure out what's happening --- src/arcade/core/util/MatrixArray.java | 129 +++++++ src/arcade/core/util/Solver.java | 12 +- .../env/component/PatchComponentDegrade.java | 1 + .../env/component/PatchComponentGrowth.java | 319 ++++++++---------- .../env/component/PatchComponentRemodel.java | 16 +- .../PatchComponentSitesGraphUtilities.java | 52 ++- src/arcade/patch/parameter.patch.xml | 5 +- .../component/PatchComponentGrowthTest.java | 64 ++++ 8 files changed, 389 insertions(+), 209 deletions(-) create mode 100644 src/arcade/core/util/MatrixArray.java diff --git a/src/arcade/core/util/MatrixArray.java b/src/arcade/core/util/MatrixArray.java new file mode 100644 index 000000000..c66e512ce --- /dev/null +++ b/src/arcade/core/util/MatrixArray.java @@ -0,0 +1,129 @@ +package arcade.core.util; + +import java.util.ArrayList; +import java.util.Collections; +import arcade.core.util.Matrix.Value; + +/** + * Container class for array-based sparse matrix representation. + * + *

Class provides a subset of matrix operations needed for solving a system of linear equations + * using the successive over-relaxation method in {@link arcade.core.util.Solver}. + */ +public class MatrixArray { + public MatrixArray(double[][] a) { + nRows = a.length; + if (nRows == 0) { + throw new IllegalArgumentException("MatrixArray ctor: input is empty"); + } + nColumns = a[0].length; + + if (nColumns == 0) { + throw new IllegalArgumentException("Matrix array ctor: empty columns"); + } + + ArrayList alValues = new ArrayList<>(); + int nNonZero = 0; + for (int row = 0; row < nRows; row++) { + if (a[row].length != nColumns) { + throw new IllegalArgumentException( + "Matrix array ctor: not all columns are the same length"); + } + + for (int column = 0; column < nColumns; column++) { + if (a[row][column] != 0.) { + alValues.add(new Value(row, column, a[row][column])); + nNonZero++; + } + } + } + + // + // No need to sort, since we built them in order. + // + + values = new Value[nNonZero]; + int i = 0; + for (Value v : alValues) { + values[i] = v; + i++; + } + } // ctor + + public MatrixArray(ArrayList alValues, int i_nRows, int i_Columns) { + nRows = i_nRows; + nColumns = i_Columns; + values = new Value[alValues.size()]; + + Collections.sort( + alValues, + (v1, v2) -> (v1.i == v2.i ? Integer.compare(v1.j, v2.j) : (v1.i > v2.i ? 1 : -1))); + + int i = 0; + for (Value v : alValues) { + values[i] = v; + i++; + } + } + + /** + * Solves the equation {@code Lx = b} using forward substitution for an array-based sparse + * matrix. + * + * @param b the right-hand side vector + * @return the left-hand side vector + */ + public double[] forwardSubstitution(double[] b) { + + int n = b.length; + double[] subbed = new double[n]; + double[] diag = new double[n]; + + // Group lower diagonal by row. + ArrayList> rowsL = new ArrayList>(); + for (int r = 0; r < n; r++) { + rowsL.add(new ArrayList<>()); + } + for (Value v : values) { + rowsL.get(v.i).add(v); + } + + // Get values along diagonal. + for (Value v : values) { + if (v.i == v.j) { + diag[v.i] = v.v; + } + } + + // Iterate only through non-zero entries in the lower diagonal matrix. + for (int i = 0; i < n; i++) { + ArrayList rowL = rowsL.get(i); + double val = 0; + for (Value v : rowL) { + val += subbed[v.j] * v.v; + } + val = b[i] - val; + subbed[i] = val / diag[i]; + } + + return subbed; + } + + public double[] multiply(double[] b) { + if (b.length != nRows) { + throw new IllegalArgumentException("MatrixArray.multiply (by a vector): conformation"); + } + double[] multiplied = new double[nRows]; + + // Iterate through all entries and multiply. + for (Value a : values) { + multiplied[a.i] += a.v * b[a.j]; + } + + return multiplied; + } + + int nRows; + int nColumns; + Value[] values; +} diff --git a/src/arcade/core/util/Solver.java b/src/arcade/core/util/Solver.java index 88800297a..306ad862a 100644 --- a/src/arcade/core/util/Solver.java +++ b/src/arcade/core/util/Solver.java @@ -38,13 +38,13 @@ public class Solver { private static final double OMEGA = 1.4; /** Maximum number of iterations. */ - private static final int MAX_ITERS = 10000; + private static final int MAX_ITERS = 20000; /** Error tolerance for SOR. */ - private static final double TOLERANCE = 1E-8; + private static final double TOLERANCE = 1E-5; /** Convergence delta for bisection method. */ - private static final double DELTA = 1E-5; + private static final double DELTA = 1E-6; /** Matrix size threshold for dense representation. */ private static final int MATRIX_THRESHOLD = 100; @@ -370,7 +370,7 @@ private static double[] denseSOR( double[] r = subtract(vec, multiply(mat, xCurr)); error = normalize(r); } - + LOGGER.info("Finished SOR iteration, error: " + error + ", i: " + i); return xCurr; } @@ -396,6 +396,7 @@ private static double[] sparseSOR( double[] c = forwardSubstitution(sparseA, vec); ArrayList t = forwardSubstitution(sparseA); t = scale(t, -1); + MatrixArray tArray = new MatrixArray(t, mat.length, mat[0].length); // Set initial guess. double[] xCurr = x0; @@ -404,7 +405,7 @@ private static double[] sparseSOR( // Iterate until convergence. while (i < maxIters && error > tolerance) { // Calculate new guess for x. - xCurr = add(scale(add(multiply(t, xPrev), c), OMEGA), scale(xPrev, 1 - OMEGA)); + xCurr = add(scale(add(tArray.multiply(xPrev), c), OMEGA), scale(xPrev, 1 - OMEGA)); // Set previous to copy of current and increment iteration count. xPrev = xCurr; @@ -414,7 +415,6 @@ private static double[] sparseSOR( double[] r = subtract(vec, multiply(sparseA, xCurr)); error = normalize(r); } - return xCurr; } diff --git a/src/arcade/patch/env/component/PatchComponentDegrade.java b/src/arcade/patch/env/component/PatchComponentDegrade.java index 76662aa89..88bdb527c 100644 --- a/src/arcade/patch/env/component/PatchComponentDegrade.java +++ b/src/arcade/patch/env/component/PatchComponentDegrade.java @@ -119,6 +119,7 @@ public void step(SimState state) { if (edge.wall <= MINIMUM_WALL_THICKNESS && (edge.shear < shearThreshold || Double.isNaN(edge.shear))) { + LOGGER.info("Removing Edge."); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index a9571c33d..21a041e60 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -30,15 +30,12 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateFlows; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateCurrentState; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateLocalFlow; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressure; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculatePressures; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateStresses; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThickness; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.reversePressures; import static arcade.patch.util.PatchEnums.Ordering; /** @@ -145,9 +142,6 @@ private enum Outcome { /** List of temporary edges to be added to the graph this step. */ private ArrayList tempEdges = new ArrayList<>(); - /** Number of offsets to be used in the migration. */ - private int numOffsets; - /** List of directions to be used in the migration. */ private ArrayList offsetDirections; @@ -158,7 +152,7 @@ private enum Outcome { private int tick; /** List of nodes to be removed from the angiogenic node map this time step. */ - private ArrayList nodesToRemove = new ArrayList<>(); + private ArrayList keyNodesToRemove = new ArrayList<>(); /** * Creates a growth component for a {@link PatchComponentSitesGraph}. @@ -207,11 +201,10 @@ public void register(Simulation sim, String componentID) { sites = (PatchComponentSitesGraph) component; graph = sites.graph; - offsets = sites.graphFactory.getOffsets(); + offsets = sites.graphFactory.getOffsets(); offsetDirections = new ArrayList<>(offsets.keySet()); offsetDirections.remove(EdgeDirection.UNDEFINED); - numOffsets = offsetDirections.size(); edgeSize = sim.getSeries().ds; maxEdges = (int) Math.floor(maxLength / edgeSize); @@ -219,7 +212,7 @@ public void register(Simulation sim, String componentID) { /** * Returns a set of valid nodes. A node is valid if it not associated with an ignored edge, is - * not a root, and has fewer than 3 degrees. + * not a root, and has fewer than 3 degrees (effectively 2 degrees exactly). * * @return a set of valid nodes */ @@ -233,21 +226,10 @@ private LinkedHashSet getValidNodes() { SiteNode from = edge.getFrom(); SiteNode to = edge.getTo(); - if (from.pressure == Double.NaN) { - LOGGER.info("Uh Oh. Pressure is NaN. @ " + from); - } - if (to.pressure == Double.NaN) { - LOGGER.info("Uh Oh. Pressure is NaN. @ " + to); - } - - if ((graph.getDegree(from) < 3) - && !from.isRoot - && !(graph.getInDegree(from) == 0 && graph.getOutDegree(from) == 1)) { + if ((graph.getDegree(from) == 2) && !from.isRoot) { set.add(from); } - if ((graph.getDegree(to) < 3) - && !to.isRoot - && !(graph.getInDegree(to) == 1 && graph.getOutDegree(to) == 0)) { + if ((graph.getDegree(to) == 2) && !to.isRoot) { set.add(to); } } @@ -259,24 +241,20 @@ private LinkedHashSet getValidNodes() { * * @param random the random number generator * @param node the node to start from - * @param valList the map of directions to their corresponding values - * @param skipList the list of directions to skip + * @param spanMap the map of directions to their corresponding values * @return the direction to sprout */ private EdgeDirection performWalk( - MersenneTwisterFast random, - SiteNode node, - EnumMap valList, - ArrayList skipList) { + MersenneTwisterFast random, SiteNode node, EnumMap spanMap) { switch (walkType) { case RANDOM: - return performRandomWalk(random, node, skipList); + return performRandomWalk(random, node, spanMap); case BIASED: - return performBiasedWalk(random, node, valList, skipList); + return performBiasedWalk(random, node, spanMap); case DETERMINISTIC: - return performDeterministicWalk(random, node, valList, skipList); + return performDeterministicWalk(random, node, spanMap); default: - return performDeterministicWalk(random, node, valList, skipList); + return performDeterministicWalk(random, node, spanMap); } } @@ -292,67 +270,52 @@ public void step(SimState simstate) { } EnumMap> vegfMap = - buildDirectionalVEGFMap(vegfLattice, node); + buildDirectionalSpanMap(vegfLattice, node); if (averageDirectionalMap(vegfMap) > vegfThreshold) { angiogenicNodeMap.put(node, new ArrayList<>()); - ArrayList ignoredDirectionList = new ArrayList(); Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); + SiteEdge inEdge = (SiteEdge) in.get(0); + vegfMap.remove(sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); - if (in != null) { - for (Object edge : in) { - SiteEdge inEdge = (SiteEdge) edge; - ignoredDirectionList.add( - sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); - } - } - if (out != null) { - for (Object edge : out) { - SiteEdge outEdge = (SiteEdge) edge; - ignoredDirectionList.add( - sites.graphFactory.getDirection(outEdge, outEdge.level)); - } - } + SiteEdge outEdge = (SiteEdge) out.get(0); + vegfMap.remove(sites.graphFactory.getDirection(outEdge, outEdge.level)); EnumMap vegfAverages = getDirectionalAverages(vegfMap); - node.sproutDir = performWalk(random, node, vegfAverages, ignoredDirectionList); + node.sproutDir = performWalk(random, node, vegfAverages); } } boolean addFlag = propogateEdges(); if (addFlag) { for (SiteNode sproutNode : angiogenicNodeMap.keySet()) { - if (nodesToRemove.contains(sproutNode)) { + if (keyNodesToRemove.contains(sproutNode)) { continue; } if (sproutNode.anastomosis) { int leadingIndex = angiogenicNodeMap.get(sproutNode).size() - 1; - if (leadingIndex < 0) { - nodesToRemove.add(sproutNode); - continue; - } + assert leadingIndex >= 0; SiteNode finalNode = angiogenicNodeMap.get(sproutNode).get(leadingIndex).getTo(); SiteNode init; SiteNode fin; - LOGGER.info(" SPROUT NODE: " + sproutNode); - LOGGER.info("LEADING INDEX: " + leadingIndex); - LOGGER.info("FINAL NODE: " + finalNode); + calculateCurrentState(graph); - calculatePressures(graph); - boolean reversed = reversePressures(graph); - if (reversed) { - calculatePressures(graph); - } - calculateFlows(graph); - calculateStresses(graph); + // LOGGER.info("CHECKING NEGATIVE FLOW: pt.1"); + + // if (sproutNode.equals(new SiteNode(8, 80, 0))) { + // calculateCurrentState(graph); + // } + + // LOGGER.info("CHECKING NEGATIVE FLOW: pt.2"); if (!graph.contains(finalNode)) { - LOGGER.info("TRYING TO CONNECT ANGIOGENIC NODES."); + assert finalNode.pressure > 0; + assert sproutNode.pressure > 0; // Connecting two angiogenic nodes SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); if (targetNode == null) { @@ -369,8 +332,8 @@ public void step(SimState simstate) { fin = targetNode; } angiogenicNodeMap.get(sproutNode).addAll(angiogenicNodeMap.get(targetNode)); - nodesToRemove.add(sproutNode); - nodesToRemove.add(targetNode); + keyNodesToRemove.add(sproutNode); + keyNodesToRemove.add(targetNode); } else { if (finalNode.sproutDir != null || finalNode.anastomosis @@ -398,21 +361,24 @@ public void step(SimState simstate) { init = sproutNode; fin = finalNode; } - nodesToRemove.add(sproutNode); - } - if (init.pressure == 0 || fin.pressure == 0) { - continue; + keyNodesToRemove.add(sproutNode); + keyNodesToRemove.add(finalNode); } + assert init.pressure != 0; + assert fin.pressure != 0; + assert init != fin; addAngioEdges( angiogenicNodeMap.get(sproutNode), init, fin, calculationStrategy); } } } - for (SiteNode n : nodesToRemove) { - angiogenicNodeMap.remove(n); + for (SiteNode n : keyNodesToRemove) { + if (angiogenicNodeMap.containsKey(n)) { + angiogenicNodeMap.remove(n); + } } - nodesToRemove.clear(); + keyNodesToRemove.clear(); } /** @@ -432,7 +398,7 @@ private boolean propogateEdges() { for (SiteNode sprout : angiogenicNodeMap.keySet()) { if (checkForIgnoredEdges(sprout)) { - nodesToRemove.add(sprout); + keyNodesToRemove.add(sprout); continue; } @@ -440,7 +406,7 @@ private boolean propogateEdges() { SiteNode tipNode; SiteEdge newEdge; - if (edgeList.size() > 0) { + if (!edgeList.isEmpty()) { tipNode = edgeList.get(edgeList.size() - 1).getTo(); } else { tipNode = sprout; @@ -452,9 +418,8 @@ private boolean propogateEdges() { newEdge = createNewEdge(sprout.sproutDir, tipNode); - if (edgeList.size() == maxEdges || newEdge == null || graph.getDegree(sprout) > 3) { - nodesToRemove.add(sprout); - continue; + if (edgeList.size() == maxEdges || newEdge == null || graph.getDegree(sprout) == 3) { + keyNodesToRemove.add(sprout); } else { edgeList.add(newEdge); if (newEdge.isAnastomotic) { @@ -594,21 +559,47 @@ private void removeEdgeList(ArrayList edgeList) { } } + /** + * Private helper function for building a map of VEGF concentration values for a given node. + * + * @param lattice the VEGF lattice object + * @param node the angiogenic node object + * @return map of {@link EdgeDirection} to {@link ArrayList} of double values from the span of + * the edge in that direction + */ + private EnumMap> buildDirectionalSpanMap( + Lattice lattice, SiteNode node) { + double[][][] field = lattice.getField(); + EnumMap> vegfMap = new EnumMap<>(EdgeDirection.class); + for (EdgeDirection dir : offsetDirections) { + SiteNode proposed = sites.graphFactory.offsetNode(node, dir, DEFAULT_EDGE_LEVEL); + if (sites.graphFactory.checkNode(proposed)) { + ArrayList span = sites.getSpan(node, proposed); + vegfMap.put(dir, new ArrayList<>()); + for (CoordinateXYZ coordinate : span) { + int i = coordinate.x; + int j = coordinate.y; + int k = coordinate.z; + vegfMap.get(dir).add(field[k][i][j]); + } + } else { + vegfMap.put(dir, new ArrayList<>(0)); + } + } + return vegfMap; + } + /** * Private helper function for choosing a sprout direction randomly on the from the node. * * @param random simulation random number generator * @param node the angiogenic node object - * @param skipList list of directions to be skipped * @return a random edge direction */ - private EdgeDirection performRandomWalk( - MersenneTwisterFast random, SiteNode node, ArrayList skipList) { - EdgeDirection randDir; - do { - randDir = offsetDirections.get(random.nextInt(numOffsets)); - } while (!skipList.contains(randDir)); - return randDir; + static EdgeDirection performRandomWalk( + MersenneTwisterFast random, SiteNode node, EnumMap spanMap) { + ArrayList possibleDirections = new ArrayList<>(spanMap.keySet()); + return possibleDirections.get(random.nextInt(possibleDirections.size())); } /** @@ -617,27 +608,20 @@ private EdgeDirection performRandomWalk( * * @param random simulation random number generator * @param node the angiogenic node object - * @param valList map of direction to their respective VEGF concentration value - * @param skipList list of directions to be skipped + * @param spanMap map of direction to their respective VEGF concentration value * @return a biased random edge direction */ - private EdgeDirection performBiasedWalk( - MersenneTwisterFast random, - SiteNode node, - EnumMap valList, - ArrayList skipList) { - for (EdgeDirection dir : skipList) { - valList.put(dir, 0.0); - } - EnumMap seqMap = normalizeDirectionalMap(valList); + static EdgeDirection performBiasedWalk( + MersenneTwisterFast random, SiteNode node, EnumMap spanMap) { + EnumMap seqMap = normalizeDirectionalMap(spanMap); double val = random.nextDouble(); - for (EdgeDirection dir : offsetDirections) { - if (val < seqMap.get(dir)) { + for (EdgeDirection dir : seqMap.keySet()) { + if (val <= seqMap.get(dir)) { return dir; } } - // otherwise return last direction - return offsetDirections.get(offsetDirections.size() - 1); + assert false; + return EdgeDirection.UNDEFINED; } /** @@ -646,50 +630,12 @@ private EdgeDirection performBiasedWalk( * * @param random simulation random number generator * @param node the angiogenic node object - * @param valList map of direction to their respective VEGF concentration value - * @param skipList list of directions to be skipped + * @param spanMap map of direction to their respective VEGF concentration value * @return the edge direction with highest concentration */ - private EdgeDirection performDeterministicWalk( - MersenneTwisterFast random, - SiteNode node, - EnumMap valList, - ArrayList skipList) { - for (EdgeDirection dir : skipList) { - valList.put(dir, 0.0); - } - EdgeDirection maxDir = getMaxKey(valList); - return maxDir; - } - - /** - * Private helper function for building a map of VEGF concentration values for a given node. - * - * @param lattice the VEGF lattice object - * @param node the angiogenic node object - * @return map of {@link EdgeDirection} to {@link ArrayList} of double values from the span of - * the edge in that direction - */ - private EnumMap> buildDirectionalVEGFMap( - Lattice lattice, SiteNode node) { - double[][][] field = lattice.getField(); - EnumMap> vegfMap = new EnumMap<>(EdgeDirection.class); - for (EdgeDirection dir : offsetDirections) { - SiteNode proposed = sites.graphFactory.offsetNode(node, dir, DEFAULT_EDGE_LEVEL); - if (sites.graphFactory.checkNode(proposed)) { - ArrayList span = sites.getSpan(node, proposed); - vegfMap.put(dir, new ArrayList<>()); - for (CoordinateXYZ coordinate : span) { - int i = coordinate.x; - int j = coordinate.y; - int k = coordinate.z; - vegfMap.get(dir).add(field[k][i][j]); - } - } else { - vegfMap.put(dir, new ArrayList<>(0)); - } - } - return vegfMap; + static EdgeDirection performDeterministicWalk( + MersenneTwisterFast random, SiteNode node, EnumMap spanMap) { + return getMaxKey(spanMap); } /** @@ -700,10 +646,10 @@ private EnumMap> buildDirectionalVEGFMap( * @return map of {@link EdgeDirection} to the average VEGF concentration value across the span * in that direction */ - private EnumMap getDirectionalAverages( + static EnumMap getDirectionalAverages( EnumMap> map) { EnumMap averageMap = new EnumMap<>(EdgeDirection.class); - for (EdgeDirection dir : offsetDirections) { + for (EdgeDirection dir : map.keySet()) { double sum = 0; for (double value : map.get(dir)) { sum += value; @@ -720,15 +666,16 @@ private EnumMap getDirectionalAverages( * span in that direction * @return direction with the highest concentration */ - private EdgeDirection getMaxKey(EnumMap map) { + static EdgeDirection getMaxKey(EnumMap map) { EdgeDirection maxDir = EdgeDirection.UNDEFINED; double maxVal = 0; - for (EdgeDirection dir : offsetDirections) { + for (EdgeDirection dir : map.keySet()) { if (map.get(dir) > maxVal) { maxDir = dir; maxVal = map.get(dir); } } + assert maxDir != EdgeDirection.UNDEFINED; return maxDir; } @@ -739,12 +686,12 @@ private EdgeDirection getMaxKey(EnumMap map) { * @return {@link EnumMap} of {@link EdgeDirection} to normalized values (range between 0 and * 1). */ - private EnumMap normalizeDirectionalMap( + static EnumMap normalizeDirectionalMap( EnumMap map) { EnumMap normalizedMap = new EnumMap<>(EdgeDirection.class); double norm = sumMap(map); double prev = 0; - for (EdgeDirection dir : offsetDirections) { + for (EdgeDirection dir : map.keySet()) { prev = prev + map.get(dir) / norm; normalizedMap.put(dir, prev); } @@ -757,9 +704,9 @@ private EnumMap normalizeDirectionalMap( * @param map {@link EnumMap} of {@link EdgeDirection} to double values. * @return sum of the values in the map */ - private double sumMap(EnumMap map) { + static double sumMap(EnumMap map) { double sum = 0; - for (EdgeDirection dir : offsetDirections) { + for (EdgeDirection dir : map.keySet()) { sum += map.get(dir); } return sum; @@ -771,10 +718,10 @@ private double sumMap(EnumMap map) { * @param map {@link EnumMap} of {@link EdgeDirection} to {@link ArrayList} of double values. * @return {@code double} object. */ - private double averageDirectionalMap(EnumMap> map) { + static double averageDirectionalMap(EnumMap> map) { double sum = 0; int count = 0; - for (EdgeDirection dir : offsetDirections) { + for (EdgeDirection dir : map.keySet()) { for (double value : map.get(dir)) { sum += value; count++; @@ -794,12 +741,12 @@ private double averageDirectionalMap(EnumMap> m private void addAngioEdges( ArrayList list, SiteNode start, SiteNode end, Calculation calc) { - LOGGER.info("ADDING ANGIOGENIC EDGES. TICK: " + tick); - LOGGER.info("TRYING TO ADD: " + start.id + " -> " + end.id); - LOGGER.info("LIST: " + list); - if (list.size() > 1) { - LOGGER.info("LIST SIZE: " + list.size()); - } + // LOGGER.info("ADDING ANGIOGENIC EDGES. TICK: " + tick); + // LOGGER.info("TRYING TO ADD: " + start.id + " -> " + end.id); + // LOGGER.info("LIST: " + list); + // if (list.size() > 1) { + // LOGGER.info("LIST SIZE: " + list.size()); + // } // check for cycle path(graph, end, start); if (end.prev != null) { @@ -822,14 +769,6 @@ private void addAngioEdges( // update edges in the minimal path between start and end ArrayList angioPath = getPath(tempGraph, start, end); - if (angioPath == null) { - LOGGER.info("START: " + start); - LOGGER.info("END: " + end); - LOGGER.info("The list is: " + list); - LOGGER.info("angioPath is null."); - return; - } - for (SiteEdge edge : angioPath) { edge.getTo().addTime = tick; edge.radius = @@ -862,7 +801,7 @@ private void addAngioEdges( removeEdgeList(angioPath); LOGGER.info("Failed to add " + list + "."); } else { - LOGGER.info("Added " + angioPath.size() + " edges."); + LOGGER.info("Added " + angioPath.size() + " edges at tick: " + tick); } break; } @@ -875,12 +814,14 @@ private void addAngioEdges( * @param edge the original edge with specified radius * @return new radius for the edges */ - private double calculateEvenSplitRadius(SiteEdge edge) { + private static double calculateEvenSplitRadius(SiteEdge edge) { double radius = edge.radius; double length = edge.length; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double flow = calculateLocalFlow(radius, length, deltaP); double newRadius; + + LOGGER.info("calculating even split, maybe leading to failure"); try { newRadius = Solver.bisection( @@ -989,11 +930,6 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, } } - if (arteries.size() == 0 || veins.size() == 0) { - LOGGER.info("No arteries or veins found, not updating roots."); - failed = true; - } - if (failed) { resetRadii(updatedEdges, oldRadii); return; @@ -1010,15 +946,10 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, */ private Outcome recalculateRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { - Bag edges = graph.getEdgesOut(start); - if (edges == null) { - return Outcome.FAILURE; - } - if (edges.size() < 2) { - return Outcome.FAILURE; - } + assert edges != null; + assert edges.size() >= 2; Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; Integer nonAngioIndex = angioIndex ^ 1; @@ -1026,6 +957,12 @@ private Outcome recalculateRadii( Double divertedFlow = calculateLocalFlow(CAPILLARY_RADIUS, ignoredEdges, deltaP); Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { + LOGGER.info("Diverted flow is greater than original flow, cannot update radius."); + LOGGER.info("Diverted flow: " + divertedFlow); + LOGGER.info("Original flow: " + originalFlow); + if (originalFlow < 0) { + LOGGER.info("Original flow is negative, cannot update radius."); + } return Outcome.FAILURE; } if (intersection != null) { @@ -1036,6 +973,7 @@ private Outcome recalculateRadii( // divertedFlow, // Adjustment.INCREASE, // ignoredEdges); + LOGGER.info("Intersection is a root, cannot update radius."); return Outcome.FAILURE; } @@ -1046,6 +984,7 @@ private Outcome recalculateRadii( Adjustment.DECREASE, ignoredEdges) == Outcome.FAILURE) { + LOGGER.info("Failed to update radius for non-angiogenic edge."); return Outcome.FAILURE; } @@ -1056,6 +995,7 @@ private Outcome recalculateRadii( Adjustment.INCREASE, ignoredEdges) == Outcome.FAILURE) { + LOGGER.info("Failed to update radius for angiogenic edge."); return Outcome.FAILURE; } @@ -1065,6 +1005,7 @@ private Outcome recalculateRadii( path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { + LOGGER.info("Boundary is not null and radius is greater than minimum."); return Outcome.FAILURE; // return updateRadiusToRoot( // (SiteEdge) edges.get(angioIndex), @@ -1074,6 +1015,8 @@ private Outcome recalculateRadii( // ignoredEdges); } } + LOGGER.info("No intersection found, cannot update radius."); + return Outcome.FAILURE; } return Outcome.SUCCESS; } @@ -1141,6 +1084,7 @@ private Outcome updateRadiiOfEdgeList( } if (calculateRadius(e, flow, adjustment) == Outcome.FAILURE) { resetRadii(edges, oldRadii); + LOGGER.info("Failed to update radius for edges, resetting to old radii."); return Outcome.FAILURE; } } @@ -1160,7 +1104,12 @@ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustmen double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; + + assert edge.getFrom().pressure > 0; + assert edge.getTo().pressure > 0; + double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); + // double originalFlow = edge.flow; Function f = (double r) -> @@ -1170,10 +1119,12 @@ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustmen try { newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); } catch (Exception e) { + LOGGER.info("Bisection failed: " + e.getMessage()); return Outcome.FAILURE; } if (newRadius == 1E-6) { + LOGGER.info("New radius is too small: " + newRadius); return Outcome.FAILURE; } edge.radius = newRadius; diff --git a/src/arcade/patch/env/component/PatchComponentRemodel.java b/src/arcade/patch/env/component/PatchComponentRemodel.java index 521d0662d..b351a117b 100644 --- a/src/arcade/patch/env/component/PatchComponentRemodel.java +++ b/src/arcade/patch/env/component/PatchComponentRemodel.java @@ -9,11 +9,16 @@ import arcade.core.sim.Simulation; import arcade.core.util.Graph; import arcade.core.util.MiniBox; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.location.CoordinateXYZ; +import arcade.patch.util.PatchEnums.Ordering; + import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_WALL_RADIUS_FRACTION; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_WALL_THICKNESS; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateCurrentState; import static arcade.patch.util.PatchEnums.Ordering; /** @@ -168,6 +173,7 @@ public void step(SimState state) { if (edge.radius < MINIMUM_CAPILLARY_RADIUS || edge.wall < MINIMUM_WALL_THICKNESS || Double.isNaN(edge.radius)) { + LOGGER.info("Removing Edge."); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -178,15 +184,9 @@ public void step(SimState state) { // If any edges are removed, update the graph edges that are ignored. // Otherwise, recalculate pressure, flow, and stresses. if (removed) { - PatchComponentSitesGraphUtilities.updateGraph(graph); + updateGraph(graph); } else { - PatchComponentSitesGraphUtilities.calculatePressures(graph); - boolean reversed = PatchComponentSitesGraphUtilities.reversePressures(graph); - if (reversed) { - PatchComponentSitesGraphUtilities.calculatePressures(graph); - } - PatchComponentSitesGraphUtilities.calculateFlows(graph); - PatchComponentSitesGraphUtilities.calculateStresses(graph); + calculateCurrentState(graph); } } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index dfb79b8a2..c950a6314 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -489,6 +489,10 @@ static void calculatePressures(Graph graph) { if (div != 0) { x0[id] /= div; } + + if (node.pressure > 0) { + x0[id] = node.pressure; + } } double[][] sA = Matrix.scale(mA, 1E-7); @@ -990,7 +994,10 @@ static void path(Graph graph, SiteNode start, SiteNode end) { static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { path(graph, start, end); ArrayList path = new ArrayList<>(); - SiteNode node = (SiteNode) graph.lookup(end); + SiteNode node = end; + if (node.prev == null) { + node = (SiteNode) graph.lookup(end); + } while (node != null && !node.equals(start)) { Bag b = graph.getEdgesIn(node); if (b.numObjs == 1) { @@ -1233,13 +1240,7 @@ static void updateGraph(Graph graph) { gCurr = gNew; } while (list.size() != 0); - calculatePressures(graph); - boolean reversed = reversePressures(graph); - if (reversed) { - calculatePressures(graph); - } - calculateFlows(graph); - calculateStresses(graph); + calculateCurrentState(graph); // Set oxygen nodes. for (Object obj : graph.getAllEdges()) { @@ -1255,6 +1256,36 @@ static void updateGraph(Graph graph) { } } + /** + * Updates hemodynamic properties based on the current state of the graph. + * + * @param graph the graph object + */ + static void calculateCurrentState(Graph graph) { + do { + calculatePressures(graph); + boolean reversed = reversePressures(graph); + if (reversed) { + calculatePressures(graph); + } + calculateFlows(graph); + calculateStresses(graph); + } while (checkForNegativeFlow(graph)); + } + + static boolean checkForNegativeFlow(Graph graph) { + boolean negative = false; + for (Object obj : graph.getAllEdges()) { + SiteEdge edge = (SiteEdge) obj; + if (edge.flow < 0) { + negative = true; + LOGGER.info("Negative flow detected, recalculating."); + break; + } + } + return negative; + } + /** * Iterates through nodes to eliminate low flow edges preventing graph traversal. * @@ -1275,6 +1306,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r for (Object obj : out) { SiteEdge edge = (SiteEdge) obj; if (edge.flow < MINIMUM_FLOW_RATE || Double.isNaN(edge.flow)) { + LOGGER.info("Removing Edge."); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -1290,6 +1322,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r for (Object obj : in) { SiteEdge edge = (SiteEdge) obj; if (edge.flow < MINIMUM_FLOW_RATE || Double.isNaN(edge.flow)) { + LOGGER.info("Removing Edge."); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -1307,11 +1340,13 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r double totalFlow = edge1.flow + edge2.flow; if (edge1.flow / totalFlow < MINIMUM_FLOW_PERCENT) { + LOGGER.info("Removing Edge."); graph.removeEdge(edge1); edge1.getFrom().pressure = Double.NaN; edge1.getTo().pressure = Double.NaN; updateGraph(graph); } else if (edge2.flow / totalFlow < MINIMUM_FLOW_PERCENT) { + LOGGER.info("Removing Edge."); graph.removeEdge(edge2); edge2.getFrom().pressure = Double.NaN; edge2.getTo().pressure = Double.NaN; @@ -1322,6 +1357,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r } if (removeMin) { + LOGGER.info("Removing Edge."); graph.removeEdge(minEdge); minEdge.getFrom().pressure = Double.NaN; minEdge.getTo().pressure = Double.NaN; diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index 7d4034b61..c5bc42f8d 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -94,7 +94,7 @@ - + @@ -164,11 +164,10 @@ - \ + - diff --git a/test/arcade/patch/env/component/PatchComponentGrowthTest.java b/test/arcade/patch/env/component/PatchComponentGrowthTest.java index 1914050b9..d576d915c 100644 --- a/test/arcade/patch/env/component/PatchComponentGrowthTest.java +++ b/test/arcade/patch/env/component/PatchComponentGrowthTest.java @@ -1,6 +1,8 @@ package arcade.patch.env.component; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumMap; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -182,4 +184,66 @@ public void register_calledWithSitesGraphObject_doesNotThrowException() { PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); assertDoesNotThrow(() -> component.register(simMock, "COMPATIBLE")); } + + @Test + public void getDirectionalAverages_givenMap_returnsCorrectMap() { + EnumMap> map = new EnumMap<>(EdgeDirection.class); + map.put(EdgeDirection.UP, new ArrayList<>(Arrays.asList(1.0, 2.0, 3.0))); + map.put(EdgeDirection.DOWN, new ArrayList<>(Arrays.asList(4.0, 5.0, 6.0))); + map.put(EdgeDirection.LEFT, new ArrayList<>(Arrays.asList(7.0, 8.0, 9.0))); + map.put(EdgeDirection.RIGHT, new ArrayList<>(Arrays.asList(10.0, 11.0, 12.0))); + EnumMap averageMap = + PatchComponentGrowth.getDirectionalAverages(map); + assertEquals(averageMap.get(EdgeDirection.UP), 2.0); + assertEquals(averageMap.get(EdgeDirection.DOWN), 5.0); + assertEquals(averageMap.get(EdgeDirection.LEFT), 8.0); + assertEquals(averageMap.get(EdgeDirection.RIGHT), 11.0); + } + + @Test + public void getMaxKey_givenMap_returnsCorrectDirection() { + EnumMap map = new EnumMap<>(EdgeDirection.class); + map.put(EdgeDirection.UP, 1.0); + map.put(EdgeDirection.DOWN, 2.0); + map.put(EdgeDirection.LEFT, 3.0); + map.put(EdgeDirection.RIGHT, 4.0); + assertEquals(EdgeDirection.RIGHT, PatchComponentGrowth.getMaxKey(map)); + } + + @Test + public void normalizeDirectionalMap_givenMap_returnsNormalizedMap() { + EnumMap map = new EnumMap<>(EdgeDirection.class); + map.put(EdgeDirection.UP, 0.5); + map.put(EdgeDirection.DOWN, 0.5); + map.put(EdgeDirection.LEFT, 0.5); + map.put(EdgeDirection.RIGHT, 0.5); + EnumMap normalizedMap = + PatchComponentGrowth.normalizeDirectionalMap(map); + assertEquals(normalizedMap.get(EdgeDirection.UP), 0.25); + assertEquals(normalizedMap.get(EdgeDirection.RIGHT), 0.5); + assertEquals(normalizedMap.get(EdgeDirection.DOWN), 0.75); + assertEquals(normalizedMap.get(EdgeDirection.LEFT), 1.0); + assertEquals(normalizedMap.keySet().size(), 4); + assertNull(normalizedMap.get(EdgeDirection.UNDEFINED)); + } + + @Test + public void sumMap_givenMap_returnsCorrectSum() { + EnumMap map = new EnumMap<>(EdgeDirection.class); + map.put(EdgeDirection.UP, 1.0); + map.put(EdgeDirection.DOWN, 2.0); + map.put(EdgeDirection.LEFT, 3.0); + map.put(EdgeDirection.RIGHT, 4.0); + assertEquals(10.0, PatchComponentGrowth.sumMap(map)); + } + + @Test + public void averageDirectionalMap_givenMap_returnsCorrectAverage() { + EnumMap> map = new EnumMap<>(EdgeDirection.class); + map.put(EdgeDirection.UP, new ArrayList<>(Arrays.asList(1.0, 2.0, 3.0))); + map.put(EdgeDirection.DOWN, new ArrayList<>(Arrays.asList(4.0, 5.0, 6.0))); + map.put(EdgeDirection.LEFT, new ArrayList<>(Arrays.asList(7.0, 8.0, 9.0))); + map.put(EdgeDirection.RIGHT, new ArrayList<>(Arrays.asList(10.0, 11.0, 12.0))); + assertEquals(6.5, PatchComponentGrowth.averageDirectionalMap(map)); + } } From 9f169a0aa3ad9160c325034126767b5ed6953c2c Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 3 Jul 2025 10:57:09 -0700 Subject: [PATCH 31/56] spotless --- src/arcade/patch/env/component/PatchComponentRemodel.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentRemodel.java b/src/arcade/patch/env/component/PatchComponentRemodel.java index b351a117b..f982b3aac 100644 --- a/src/arcade/patch/env/component/PatchComponentRemodel.java +++ b/src/arcade/patch/env/component/PatchComponentRemodel.java @@ -12,13 +12,12 @@ import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.util.PatchEnums.Ordering; - import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_WALL_RADIUS_FRACTION; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_WALL_THICKNESS; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateCurrentState; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.util.PatchEnums.Ordering; /** From 6037755f84f8e8ed4b4dd80945d03be151004aec Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 3 Jul 2025 10:59:20 -0700 Subject: [PATCH 32/56] updated default threshold --- src/arcade/patch/parameter.patch.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index c5bc42f8d..d7c2c42be 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -166,7 +166,7 @@ - + From 6006b411575944a4913bdfc23a67c061413c3368 Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 3 Jul 2025 11:49:46 -0700 Subject: [PATCH 33/56] added transport --- src/arcade/patch/env/component/PatchComponentGrowth.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 21a041e60..9886934a9 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -27,6 +27,8 @@ import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; import arcade.patch.env.location.CoordinateXYZ; +import arcade.patch.util.PatchEnums.Ordering; + import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; @@ -780,6 +782,8 @@ private void addAngioEdges( } edge.wall = calculateThickness(edge); edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); + edge.transport.putIfAbsent("GLUCOSE", 0.); + edge.transport.putIfAbsent("OXYGEN", 0.); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); edge.isPerfused = true; } From 3cce139c4ffa39755df4ef87a472911e3ac28005 Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 3 Jul 2025 12:20:07 -0700 Subject: [PATCH 34/56] added fraction --- src/arcade/patch/env/component/PatchComponentGrowth.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 9886934a9..734a7f754 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -784,6 +784,8 @@ private void addAngioEdges( edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.transport.putIfAbsent("GLUCOSE", 0.); edge.transport.putIfAbsent("OXYGEN", 0.); + edge.fraction.putIfAbsent("GLUCOSE", 1.); + edge.fraction.putIfAbsent("OXYGEN", 1.); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); edge.isPerfused = true; } From e48151f9d3635b4cf0126e8b5d1b26a543cca631 Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 3 Jul 2025 19:29:27 -0700 Subject: [PATCH 35/56] fixed directional check based on level --- .../env/component/PatchComponentGrowth.java | 14 +- .../component/PatchComponentSitesGraph.java | 23 +++ .../PatchComponentSitesGraphUtilities.java | 10 +- ...atchComponentSitesGraphFactoryTriTest.java | 157 ++++++++++++++++++ 4 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 734a7f754..1d7ca1cd6 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -27,8 +27,6 @@ import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; import arcade.patch.env.component.PatchComponentSitesGraphFactory.Root; import arcade.patch.env.location.CoordinateXYZ; -import arcade.patch.util.PatchEnums.Ordering; - import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_CAPILLARY_RADIUS; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; @@ -280,10 +278,10 @@ public void step(SimState simstate) { Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); SiteEdge inEdge = (SiteEdge) in.get(0); - vegfMap.remove(sites.graphFactory.getOppositeDirection(inEdge, inEdge.level)); + vegfMap.remove(sites.graphFactory.getOppositeDirection(inEdge, DEFAULT_EDGE_LEVEL)); SiteEdge outEdge = (SiteEdge) out.get(0); - vegfMap.remove(sites.graphFactory.getDirection(outEdge, outEdge.level)); + vegfMap.remove(sites.graphFactory.getDirection(outEdge, DEFAULT_EDGE_LEVEL)); EnumMap vegfAverages = getDirectionalAverages(vegfMap); @@ -785,7 +783,6 @@ private void addAngioEdges( edge.transport.putIfAbsent("GLUCOSE", 0.); edge.transport.putIfAbsent("OXYGEN", 0.); edge.fraction.putIfAbsent("GLUCOSE", 1.); - edge.fraction.putIfAbsent("OXYGEN", 1.); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); edge.isPerfused = true; } @@ -827,7 +824,6 @@ private static double calculateEvenSplitRadius(SiteEdge edge) { double flow = calculateLocalFlow(radius, length, deltaP); double newRadius; - LOGGER.info("calculating even split, maybe leading to failure"); try { newRadius = Solver.bisection( @@ -880,9 +876,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, Double arteryFlow = newFlow / arteries.size(); for (ArrayList path : pathsArteries) { - if (!path.get(0).getFrom().isRoot) { - throw new ArithmeticException("Root is not the start of the path"); - } + assert !path.get(0).getFrom().isRoot; updatedEdges.addAll(path); for (SiteEdge e : path) { @@ -966,6 +960,8 @@ private Outcome recalculateRadii( LOGGER.info("Diverted flow is greater than original flow, cannot update radius."); LOGGER.info("Diverted flow: " + divertedFlow); LOGGER.info("Original flow: " + originalFlow); + LOGGER.info("Edge 1: " + ((SiteEdge) edges.get(0)).flow); + LOGGER.info("Edge 2: " + ((SiteEdge) edges.get(1)).flow); if (originalFlow < 0) { LOGGER.info("Original flow is negative, cannot update radius."); } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index aa86f134f..4031dbd6f 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.logging.Logger; import sim.util.Bag; import ec.util.MersenneTwisterFast; import arcade.core.env.location.Location; @@ -45,6 +46,9 @@ * {@code A} / {@code a} for an artery or {@code V} / {@code v} for a vein. */ public abstract class PatchComponentSitesGraph extends PatchComponentSites { + /** Logger for {@code PatchComponentSitesGraph}. */ + private static final Logger LOGGER = Logger.getLogger(PatchComponentSitesGraph.class.getName()); + /** Tolerance for difference in internal and external concentrations. */ private static final double DELTA_TOLERANCE = 1E-8; @@ -362,6 +366,9 @@ void complexStep(MersenneTwisterFast random) { - (current[k][i][j] + delta[k][i][j])), 0); } else { + if ((extConcNew - (current[k][i][j] + delta[k][i][j])) == Double.NaN) { + LOGGER.info("NaN detected."); + } delta[k][i][j] += Math.max((extConcNew - (current[k][i][j] + delta[k][i][j])), 0); } @@ -623,6 +630,22 @@ public double getCircum() { public double getFlow() { return flow; } + + public String getFraction() { + StringBuilder sb = new StringBuilder(); + for (String key : fraction.keySet()) { + sb.append(key + ":" + fraction.get(key) + ","); + } + return sb.toString(); + } + + public String getTransport() { + StringBuilder sb = new StringBuilder(); + for (String key : transport.keySet()) { + sb.append(key + ":" + transport.get(key) + ","); + } + return sb.toString(); + } } /** diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index c950a6314..db9179c76 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -1306,7 +1306,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r for (Object obj : out) { SiteEdge edge = (SiteEdge) obj; if (edge.flow < MINIMUM_FLOW_RATE || Double.isNaN(edge.flow)) { - LOGGER.info("Removing Edge."); + LOGGER.info("Removing Edge. 1309"); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -1322,7 +1322,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r for (Object obj : in) { SiteEdge edge = (SiteEdge) obj; if (edge.flow < MINIMUM_FLOW_RATE || Double.isNaN(edge.flow)) { - LOGGER.info("Removing Edge."); + LOGGER.info("Removing Edge. 1325"); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -1340,13 +1340,13 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r double totalFlow = edge1.flow + edge2.flow; if (edge1.flow / totalFlow < MINIMUM_FLOW_PERCENT) { - LOGGER.info("Removing Edge."); + LOGGER.info("Removing Edge. 1343"); graph.removeEdge(edge1); edge1.getFrom().pressure = Double.NaN; edge1.getTo().pressure = Double.NaN; updateGraph(graph); } else if (edge2.flow / totalFlow < MINIMUM_FLOW_PERCENT) { - LOGGER.info("Removing Edge."); + LOGGER.info("Removing Edge. 1349"); graph.removeEdge(edge2); edge2.getFrom().pressure = Double.NaN; edge2.getTo().pressure = Double.NaN; @@ -1357,7 +1357,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r } if (removeMin) { - LOGGER.info("Removing Edge."); + LOGGER.info("Removing Edge. 1360"); graph.removeEdge(minEdge); minEdge.getFrom().pressure = Double.NaN; minEdge.getTo().pressure = Double.NaN; diff --git a/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java b/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java new file mode 100644 index 000000000..36b85c351 --- /dev/null +++ b/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java @@ -0,0 +1,157 @@ +package arcade.patch.env.component; + +import org.junit.jupiter.api.Test; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; +import arcade.patch.sim.PatchSeries; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static arcade.core.ARCADETestUtilities.*; + +public class PatchComponentSitesGraphFactoryTriTest { + @Test + public void getDirection_givenFromTo_returnsCorrectDirection() { + PatchSeries seriesMock = mock(PatchSeries.class); + + PatchComponentSitesGraphFactoryTri factory = + new PatchComponentSitesGraphFactoryTri(seriesMock); + + int x = randomIntBetween(0, 100); + int y = randomIntBetween(0, 100); + int z = randomIntBetween(0, 100); + + assertEquals( + EdgeDirection.LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 2, y, z), EdgeLevel.VARIABLE)); + assertEquals( + EdgeDirection.RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 2, y, z), EdgeLevel.VARIABLE)); + assertEquals( + EdgeDirection.DOWN_LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 1, y + 1, z), EdgeLevel.VARIABLE)); + assertEquals( + EdgeDirection.DOWN_RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 1, y + 1, z), EdgeLevel.VARIABLE)); + assertEquals( + EdgeDirection.UP_LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 1, y - 1, z), EdgeLevel.VARIABLE)); + assertEquals( + EdgeDirection.UP_RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 1, y - 1, z), EdgeLevel.VARIABLE)); + + assertEquals( + EdgeDirection.LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 4, y, z), EdgeLevel.LEVEL_2)); + assertEquals( + EdgeDirection.RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 4, y, z), EdgeLevel.LEVEL_2)); + assertEquals( + EdgeDirection.DOWN_LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 2, y + 2, z), EdgeLevel.LEVEL_2)); + assertEquals( + EdgeDirection.DOWN_RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 2, y + 2, z), EdgeLevel.LEVEL_2)); + assertEquals( + EdgeDirection.UP_LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 2, y - 2, z), EdgeLevel.LEVEL_2)); + assertEquals( + EdgeDirection.UP_RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 2, y - 2, z), EdgeLevel.LEVEL_2)); + + assertEquals( + EdgeDirection.LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 8, y, z), EdgeLevel.LEVEL_1)); + assertEquals( + EdgeDirection.RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 8, y, z), EdgeLevel.LEVEL_1)); + assertEquals( + EdgeDirection.DOWN_LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 4, y + 4, z), EdgeLevel.LEVEL_1)); + assertEquals( + EdgeDirection.DOWN_RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 4, y + 4, z), EdgeLevel.LEVEL_1)); + assertEquals( + EdgeDirection.UP_LEFT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x - 4, y - 4, z), EdgeLevel.LEVEL_1)); + assertEquals( + EdgeDirection.UP_RIGHT, + factory.getDirection( + new SiteNode(x, y, z), new SiteNode(x + 4, y - 4, z), EdgeLevel.LEVEL_1)); + } + + @Test + public void getOppositeDirection_givenEdge_returnsCorrectDirection() { + PatchSeries seriesMock = mock(PatchSeries.class); + + PatchComponentSitesGraphFactoryTri factory = + new PatchComponentSitesGraphFactoryTri(seriesMock); + + int x = randomIntBetween(0, 100); + int y = randomIntBetween(0, 100); + int z = randomIntBetween(0, 100); + + SiteEdge right = + new SiteEdge( + new SiteNode(x, y, z), + new SiteNode(x + 4, y, z), + EdgeType.CAPILLARY, + EdgeLevel.LEVEL_2); + SiteEdge left = + new SiteEdge( + new SiteNode(x, y, z), + new SiteNode(x - 4, y, z), + EdgeType.CAPILLARY, + EdgeLevel.LEVEL_2); + SiteEdge upLeft = + new SiteEdge( + new SiteNode(x, y, z), + new SiteNode(x - 2, y - 2, z), + EdgeType.CAPILLARY, + EdgeLevel.LEVEL_2); + SiteEdge upRight = + new SiteEdge( + new SiteNode(x, y, z), + new SiteNode(x + 2, y - 2, z), + EdgeType.CAPILLARY, + EdgeLevel.LEVEL_2); + SiteEdge downLeft = + new SiteEdge( + new SiteNode(x, y, z), + new SiteNode(x - 2, y + 2, z), + EdgeType.CAPILLARY, + EdgeLevel.LEVEL_2); + SiteEdge downRight = + new SiteEdge( + new SiteNode(x, y, z), + new SiteNode(x + 2, y + 2, z), + EdgeType.CAPILLARY, + EdgeLevel.LEVEL_2); + + assertEquals(EdgeDirection.LEFT, factory.getOppositeDirection(right, right.level)); + assertEquals(EdgeDirection.RIGHT, factory.getOppositeDirection(left, right.level)); + assertEquals(EdgeDirection.DOWN_RIGHT, factory.getOppositeDirection(upLeft, right.level)); + assertEquals(EdgeDirection.DOWN_LEFT, factory.getOppositeDirection(upRight, right.level)); + assertEquals(EdgeDirection.UP_RIGHT, factory.getOppositeDirection(downLeft, right.level)); + assertEquals(EdgeDirection.UP_LEFT, factory.getOppositeDirection(downRight, right.level)); + } +} From 208619912c99a41b4df25f75521af4de99061b8d Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 6 Jul 2025 21:42:01 -0700 Subject: [PATCH 36/56] edges are still being removed before establishing --- src/arcade/core/util/Solver.java | 1 - src/arcade/patch/agent/cell/PatchCell.java | 3 + .../env/component/PatchComponentDegrade.java | 3 - .../env/component/PatchComponentGrowth.java | 219 ++++++++++++------ .../env/component/PatchComponentRemodel.java | 3 - .../component/PatchComponentSitesGraph.java | 76 ++++-- .../PatchComponentSitesGraphUtilities.java | 82 ++++--- src/arcade/patch/parameter.patch.xml | 4 +- .../sim/output/PatchOutputSerializer.java | 2 + 9 files changed, 252 insertions(+), 141 deletions(-) diff --git a/src/arcade/core/util/Solver.java b/src/arcade/core/util/Solver.java index 306ad862a..214c299bf 100644 --- a/src/arcade/core/util/Solver.java +++ b/src/arcade/core/util/Solver.java @@ -370,7 +370,6 @@ private static double[] denseSOR( double[] r = subtract(vec, multiply(mat, xCurr)); error = normalize(r); } - LOGGER.info("Finished SOR iteration, error: " + error + ", i: " + i); return xCurr; } diff --git a/src/arcade/patch/agent/cell/PatchCell.java b/src/arcade/patch/agent/cell/PatchCell.java index 4a84bb520..0cd02cd06 100644 --- a/src/arcade/patch/agent/cell/PatchCell.java +++ b/src/arcade/patch/agent/cell/PatchCell.java @@ -454,6 +454,9 @@ public PatchLocation selectBestLocation(Simulation sim, MersenneTwisterFast rand // Calculate score by introducing error to the location check // and adding affinity to move toward center. double normConc = sim.getLattice("GLUCOSE").getAverageValue(location) / maxGlucose; + if (Double.isNaN(normConc)) { + normConc = 0; + } double gluc = (accuracy * normConc + (1 - accuracy) * random.nextDouble()); double dist = ((currR - loc.getPlanarDistance()) + 1) / 2.0; double score = affinity * dist + (1 - affinity) * gluc; diff --git a/src/arcade/patch/env/component/PatchComponentDegrade.java b/src/arcade/patch/env/component/PatchComponentDegrade.java index 88bdb527c..28944ed66 100644 --- a/src/arcade/patch/env/component/PatchComponentDegrade.java +++ b/src/arcade/patch/env/component/PatchComponentDegrade.java @@ -14,10 +14,8 @@ import arcade.core.util.Graph; import arcade.core.util.MiniBox; import arcade.patch.agent.cell.PatchCellCancer; -import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.grid.PatchGrid; import arcade.patch.env.location.CoordinateXYZ; -import arcade.patch.util.PatchEnums.Ordering; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_WALL_THICKNESS; import static arcade.patch.util.PatchEnums.Ordering; @@ -134,7 +132,6 @@ public void step(SimState state) { // If any edges are removed, update the graph edges that are ignored. // Otherwise, recalculate calculate stresses. if (removed) { - LOGGER.info("REMOVING EDGES."); PatchComponentSitesGraphUtilities.updateGraph(graph); } else { PatchComponentSitesGraphUtilities.calculateStresses(graph); diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 1d7ca1cd6..01bfba86c 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -36,6 +36,7 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThickness; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; +import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.util.PatchEnums.Ordering; /** @@ -154,6 +155,9 @@ private enum Outcome { /** List of nodes to be removed from the angiogenic node map this time step. */ private ArrayList keyNodesToRemove = new ArrayList<>(); + /** Map of nodes to a delay to prevent nodes from constantly trying to add the same edge. */ + private HashMap nodeDelays = new HashMap<>(); + /** * Creates a growth component for a {@link PatchComponentSitesGraph}. * @@ -181,7 +185,7 @@ public PatchComponentGrowth(Series series, MiniBox parameters) { @Override public void schedule(Schedule schedule) { interval = migrationRate < edgeSize ? 60 : (int) (edgeSize / migrationRate * 60); - schedule.scheduleRepeating(this, Ordering.LAST_COMPONENT.ordinal() - 3, interval); + schedule.scheduleRepeating(this, Ordering.LAST_COMPONENT.ordinal() + 1, interval); } @Override @@ -296,6 +300,15 @@ public void step(SimState simstate) { continue; } if (sproutNode.anastomosis) { + + nodeDelays.putIfAbsent(sproutNode, 0); + + if (graph.getDegree(sproutNode) > 2) { + LOGGER.info("Sprout node has more than two degrees."); + keyNodesToRemove.add(sproutNode); + continue; + } + int leadingIndex = angiogenicNodeMap.get(sproutNode).size() - 1; assert leadingIndex >= 0; SiteNode finalNode = @@ -303,25 +316,30 @@ public void step(SimState simstate) { SiteNode init; SiteNode fin; - calculateCurrentState(graph); - - // LOGGER.info("CHECKING NEGATIVE FLOW: pt.1"); - - // if (sproutNode.equals(new SiteNode(8, 80, 0))) { - // calculateCurrentState(graph); - // } - - // LOGGER.info("CHECKING NEGATIVE FLOW: pt.2"); - if (!graph.contains(finalNode)) { - assert finalNode.pressure > 0; - assert sproutNode.pressure > 0; + // Connecting two angiogenic nodes SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); + if (targetNode == null) { sproutNode.anastomosis = false; continue; } + keyNodesToRemove.add(sproutNode); + keyNodesToRemove.add(targetNode); + + // Connecting sprout to existing node + sproutNode = validateNodeObject(sproutNode); + targetNode = validateNodeObject(targetNode); + + if (sproutNode.pressure == 0 + || targetNode.pressure == 0 + || Double.isNaN(sproutNode.pressure) + || Double.isNaN(targetNode.pressure)) { + LOGGER.info("Cannot connect nodes (332)."); + continue; + } + if (sproutNode.pressure < targetNode.pressure) { reverseAllEdges(sproutNode); init = targetNode; @@ -332,27 +350,29 @@ public void step(SimState simstate) { fin = targetNode; } angiogenicNodeMap.get(sproutNode).addAll(angiogenicNodeMap.get(targetNode)); - keyNodesToRemove.add(sproutNode); - keyNodesToRemove.add(targetNode); + } else { + keyNodesToRemove.add(sproutNode); + keyNodesToRemove.add(finalNode); + if (finalNode.sproutDir != null || finalNode.anastomosis || finalNode.isRoot) { continue; } + // Connecting sprout to existing node - if (sproutNode.pressure == 0) { - if (graph.getEdgesOut(sproutNode) != null) { - sproutNode = - ((SiteEdge) graph.getEdgesOut(sproutNode).get(0)).getFrom(); - } - } - if (finalNode.pressure == 0) { - if (graph.getEdgesOut(finalNode) != null) { - finalNode = - ((SiteEdge) graph.getEdgesOut(finalNode).get(0)).getFrom(); - } + sproutNode = validateNodeObject(sproutNode); + finalNode = validateNodeObject(finalNode); + + if (sproutNode.pressure == 0 + || finalNode.pressure == 0 + || Double.isNaN(sproutNode.pressure) + || Double.isNaN(finalNode.pressure)) { + LOGGER.info("Cannot connect nodes (366)."); + continue; } + if (sproutNode.pressure < finalNode.pressure) { reverseAllEdges(sproutNode); init = finalNode; @@ -361,14 +381,18 @@ public void step(SimState simstate) { init = sproutNode; fin = finalNode; } - keyNodesToRemove.add(sproutNode); - keyNodesToRemove.add(finalNode); } - assert init.pressure != 0; - assert fin.pressure != 0; + + assert init.pressure != 0.0; + assert fin.pressure != 0.0; assert init != fin; - addAngioEdges( + + addAngiogenicEdges( angiogenicNodeMap.get(sproutNode), init, fin, calculationStrategy); + + sproutNode.anastomosis = false; + + calculateCurrentState(graph); } } } @@ -381,6 +405,17 @@ public void step(SimState simstate) { keyNodesToRemove.clear(); } + private SiteNode validateNodeObject(SiteNode node) { + // Connecting sprout to existing node + if (node.pressure == 0.0) { + node = (SiteNode) graph.lookup(node); + if (graph.getEdgesOut(node) != null && node.pressure == 0.0) { + node = ((SiteEdge) graph.getEdgesOut(node).get(0)).getFrom(); + } + } + return node; + } + /** * Propogates the edges from each of the nodes in the angiogenic node map. * @@ -474,6 +509,14 @@ private boolean checkNodeSkipStatus(SiteNode node) { if ((tick - node.addTime) < (72 * 60)) { return true; } + if (nodeDelays.containsKey(node)) { + nodeDelays.put(node, nodeDelays.get(node) + 1); + if (nodeDelays.get(node) > 30) { + nodeDelays.remove(node); + return false; + } + return true; + } return false; } @@ -738,16 +781,9 @@ static double averageDirectionalMap(EnumMap> ma * @param end the ending site node object * @param calc code for the type of calculation to perform */ - private void addAngioEdges( + private void addAngiogenicEdges( ArrayList list, SiteNode start, SiteNode end, Calculation calc) { - // LOGGER.info("ADDING ANGIOGENIC EDGES. TICK: " + tick); - // LOGGER.info("TRYING TO ADD: " + start.id + " -> " + end.id); - // LOGGER.info("LIST: " + list); - // if (list.size() > 1) { - // LOGGER.info("LIST SIZE: " + list.size()); - // } - // check for cycle path(graph, end, start); if (end.prev != null) { return; @@ -770,6 +806,9 @@ private void addAngioEdges( ArrayList angioPath = getPath(tempGraph, start, end); for (SiteEdge edge : angioPath) { + if (graph.contains(edge)) { + return; + } edge.getTo().addTime = tick; edge.radius = (otherRadius > CAPILLARY_RADIUS) @@ -782,9 +821,10 @@ private void addAngioEdges( edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.transport.putIfAbsent("GLUCOSE", 0.); edge.transport.putIfAbsent("OXYGEN", 0.); - edge.fraction.putIfAbsent("GLUCOSE", 1.); + edge.fraction.putIfAbsent("GLUCOSE", -1.); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); edge.isPerfused = true; + edge.isVisited = true; } addEdgeList(angioPath); @@ -802,9 +842,33 @@ private void addAngioEdges( Outcome result = recalculateRadii(angioPath, start, end, intersection); if (result == Outcome.FAILURE) { removeEdgeList(angioPath); - LOGGER.info("Failed to add " + list + "."); } else { - LOGGER.info("Added " + angioPath.size() + " edges at tick: " + tick); + LOGGER.info( + "======================================================================="); + + LOGGER.info("Added " + angioPath + " path at tick: " + tick); + LOGGER.info("Sprout Node: " + start); + LOGGER.info("Sprout Edge: " + start.sproutDir); + for (Object obj : graph.getEdgesOut(start)) { + SiteEdge edge = (SiteEdge) obj; + LOGGER.info("Out Edge: " + edge); + } + for (Object obj : graph.getEdgesIn(start)) { + SiteEdge edge = (SiteEdge) obj; + LOGGER.info("In Edge: " + edge); + } + LOGGER.info("End Node: " + end); + for (Object obj : graph.getEdgesOut(end)) { + SiteEdge edge = (SiteEdge) obj; + LOGGER.info("Out Edge: " + edge); + } + for (Object obj : graph.getEdgesIn(end)) { + SiteEdge edge = (SiteEdge) obj; + LOGGER.info("In Edge: " + edge); + } + + LOGGER.info( + "======================================================================="); } break; } @@ -913,9 +977,8 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, Double veinFlow = newFlow / veins.size(); for (ArrayList path : pathsVeins) { - if (!path.get(0).getFrom().isRoot) { - throw new ArithmeticException("Root is not the start of the path."); - } + assert !path.get(0).getFrom().isRoot; + SiteEdge rootEdge = path.remove(path.size() - 1); oldRadii.add(rootEdge.radius); updatedEdges.add(rootEdge); @@ -946,27 +1009,39 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, */ private Outcome recalculateRadii( ArrayList ignoredEdges, SiteNode start, SiteNode end, SiteNode intersection) { + Bag edges = graph.getEdgesOut(start); assert edges != null; assert edges.size() >= 2; + updateGraph( + graph); // I think this is unnecessary but was in the previous version TODO: check + + edges = graph.getEdgesOut(start); + if (edges.size() != 2) { + return Outcome.FAILURE; + } + Integer angioIndex = ignoredEdges.contains(edges.get(0)) ? 0 : 1; Integer nonAngioIndex = angioIndex ^ 1; double deltaP = start.pressure - end.pressure; + + if (start.pressure * end.pressure == 0 || Double.isNaN(deltaP)) { + return Outcome.FAILURE; + } + Double divertedFlow = calculateLocalFlow(CAPILLARY_RADIUS, ignoredEdges, deltaP); + + for (SiteEdge e : ignoredEdges) { + e.flow = divertedFlow; + } + Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { - LOGGER.info("Diverted flow is greater than original flow, cannot update radius."); - LOGGER.info("Diverted flow: " + divertedFlow); - LOGGER.info("Original flow: " + originalFlow); - LOGGER.info("Edge 1: " + ((SiteEdge) edges.get(0)).flow); - LOGGER.info("Edge 2: " + ((SiteEdge) edges.get(1)).flow); - if (originalFlow < 0) { - LOGGER.info("Original flow is negative, cannot update radius."); - } return Outcome.FAILURE; } + if (intersection != null) { if (intersection.isRoot) { // return updateRadiusToRoot( @@ -975,7 +1050,6 @@ private Outcome recalculateRadii( // divertedFlow, // Adjustment.INCREASE, // ignoredEdges); - LOGGER.info("Intersection is a root, cannot update radius."); return Outcome.FAILURE; } @@ -986,7 +1060,6 @@ private Outcome recalculateRadii( Adjustment.DECREASE, ignoredEdges) == Outcome.FAILURE) { - LOGGER.info("Failed to update radius for non-angiogenic edge."); return Outcome.FAILURE; } @@ -997,7 +1070,6 @@ private Outcome recalculateRadii( Adjustment.INCREASE, ignoredEdges) == Outcome.FAILURE) { - LOGGER.info("Failed to update radius for angiogenic edge."); return Outcome.FAILURE; } @@ -1007,7 +1079,6 @@ private Outcome recalculateRadii( path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { - LOGGER.info("Boundary is not null and radius is greater than minimum."); return Outcome.FAILURE; // return updateRadiusToRoot( // (SiteEdge) edges.get(angioIndex), @@ -1017,7 +1088,6 @@ private Outcome recalculateRadii( // ignoredEdges); } } - LOGGER.info("No intersection found, cannot update radius."); return Outcome.FAILURE; } return Outcome.SUCCESS; @@ -1042,7 +1112,6 @@ private Outcome updateRadius( ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); if (edgesToUpdate == null) { - LOGGER.info("No path found from " + edge.getTo() + " to " + intersection); return Outcome.FAILURE; } @@ -1102,20 +1171,23 @@ private Outcome updateRadiiOfEdgeList( * @return Outcome.SUCCESS for successful update, Outcome.FAILURE for failure */ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustment) { - assert flow > 0; - double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; + if (flow <= 0) { + return Outcome.FAILURE; + } + + double adjustedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; - assert edge.getFrom().pressure > 0; - assert edge.getTo().pressure > 0; + assert edge.getFrom().pressure > 0.0; + assert edge.getTo().pressure > 0.0; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); // double originalFlow = edge.flow; Function f = (double r) -> - originalFlow + updatedFlow - calculateLocalFlow(r, edge.length, deltaP); + originalFlow + adjustedFlow - calculateLocalFlow(r, edge.length, deltaP); double newRadius; try { @@ -1144,7 +1216,7 @@ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustmen private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { assert flow >= 0; - double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; + double adjustedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); @@ -1152,7 +1224,7 @@ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment a Function f = (double r) -> originalFlow - + updatedFlow + + adjustedFlow - calculateLocalFlow( r, edge.length, @@ -1166,7 +1238,7 @@ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment a return Outcome.FAILURE; } - if (newRadius == .5 * originalRadius || newRadius == Double.NaN) { + if (newRadius == .5 * originalRadius || Double.isNaN(newRadius)) { return Outcome.FAILURE; } @@ -1186,7 +1258,7 @@ private Outcome calculateVeinRootRadius(SiteEdge edge, double flow, Adjustment a private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment adjustment) { assert flow >= 0; - double updatedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; + double adjustedFlow = (adjustment == Adjustment.DECREASE) ? -1 * flow : flow; double originalRadius = edge.radius; double deltaP = edge.getFrom().pressure - edge.getTo().pressure; double originalFlow = calculateLocalFlow(originalRadius, edge.length, deltaP); @@ -1194,7 +1266,7 @@ private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment Function f = (double r) -> originalFlow - + updatedFlow + + adjustedFlow - calculateLocalFlow( r, edge.length, @@ -1209,7 +1281,7 @@ private Outcome calculateArteryRootRadius(SiteEdge edge, double flow, Adjustment } if (newRadius == .5 * originalRadius - || newRadius == Double.NaN + || Double.isNaN(newRadius) || newRadius == 1.5 * originalRadius) { return Outcome.FAILURE; } @@ -1313,12 +1385,19 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { assert existing != null; + if (existing.pressure == 0 || Double.isNaN(existing.pressure)) { + return null; + } + edge = new SiteEdge(node, existing, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); edge.isAnastomotic = true; return edge; } edge = new SiteEdge(node, proposed, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); + if (graph.contains(edge)) { + return null; + } return edge; } return null; diff --git a/src/arcade/patch/env/component/PatchComponentRemodel.java b/src/arcade/patch/env/component/PatchComponentRemodel.java index f982b3aac..0c6b172cb 100644 --- a/src/arcade/patch/env/component/PatchComponentRemodel.java +++ b/src/arcade/patch/env/component/PatchComponentRemodel.java @@ -9,9 +9,7 @@ import arcade.core.sim.Simulation; import arcade.core.util.Graph; import arcade.core.util.MiniBox; -import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import arcade.patch.env.location.CoordinateXYZ; -import arcade.patch.util.PatchEnums.Ordering; import static arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MAXIMUM_WALL_RADIUS_FRACTION; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.MINIMUM_CAPILLARY_RADIUS; @@ -172,7 +170,6 @@ public void step(SimState state) { if (edge.radius < MINIMUM_CAPILLARY_RADIUS || edge.wall < MINIMUM_WALL_THICKNESS || Double.isNaN(edge.radius)) { - LOGGER.info("Removing Edge."); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 4031dbd6f..e74769aad 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -244,29 +244,11 @@ void simpleStep() { * @param random the random number generator */ void complexStep(MersenneTwisterFast random) { - Bag allEdges = new Bag(graph.getAllEdges()); - // Check if graph has become unconnected. - boolean isConnected = false; - for (Object obj : allEdges) { - SiteEdge edge = (SiteEdge) obj; - if (edge.getFrom().isRoot && !edge.isIgnored) { - isConnected = true; - break; - } - } - if (!isConnected) { - for (SiteLayer layer : layers) { - for (int k = 0; k < latticeHeight; k++) { - for (int i = 0; i < latticeLength; i++) { - for (int j = 0; j < latticeWidth; j++) { - layer.delta[k][i][j] = 0; - } - } - } - } + if (checkDisconnected()) { return; } + ; // Iterate through each molecule. for (SiteLayer layer : layers) { @@ -286,10 +268,11 @@ void complexStep(MersenneTwisterFast random) { } } - allEdges.shuffle(random); + Bag currentEdges = new Bag(graph.getAllEdges()); + currentEdges.shuffle(random); // Iterate through each edge in graph. - for (Object obj : allEdges) { + for (Object obj : currentEdges) { SiteEdge edge = (SiteEdge) obj; if (edge.isIgnored) { continue; @@ -334,6 +317,10 @@ void complexStep(MersenneTwisterFast random) { intConc = edge.fraction.get(layer.name) * concentration; // fmol/um^3 intConcNew = intConc; // fmol/um^3 extConcNew = extConc; // fmol/um^3 + + if (Double.isNaN(intConc) || Double.isNaN(extConc)) { + LOGGER.info("NaN"); + } } if (Math.abs(intConc - extConc) > DELTA_TOLERANCE) { @@ -351,6 +338,9 @@ void complexStep(MersenneTwisterFast random) { dmdt = pa * (intConcNew - extConcNew); extConcNew += dmdt / latticePatchVolume; } + if (Double.isNaN(intConcNew) || Double.isNaN(extConcNew)) { + LOGGER.info("NaN"); + } } // Update external concentrations. @@ -366,9 +356,6 @@ void complexStep(MersenneTwisterFast random) { - (current[k][i][j] + delta[k][i][j])), 0); } else { - if ((extConcNew - (current[k][i][j] + delta[k][i][j])) == Double.NaN) { - LOGGER.info("NaN detected."); - } delta[k][i][j] += Math.max((extConcNew - (current[k][i][j] + delta[k][i][j])), 0); } @@ -378,6 +365,9 @@ void complexStep(MersenneTwisterFast random) { if (layer.name.equalsIgnoreCase("OXYGEN")) { edge.transport.put(layer.name, (intConc - intConcNew) * edge.flow); } else { + if (Double.isNaN((intConc - intConcNew) / concentration)) { + LOGGER.info("NaN"); + } edge.transport.put(layer.name, (intConc - intConcNew) / concentration); } } @@ -385,6 +375,34 @@ void complexStep(MersenneTwisterFast random) { } } + private boolean checkDisconnected() { + Bag allEdges = new Bag(graph.getAllEdges()); + + // Check if graph has become unconnected. + boolean isConnected = false; + for (Object obj : allEdges) { + SiteEdge edge = (SiteEdge) obj; + if (edge.getFrom().isRoot && !edge.isIgnored) { + isConnected = true; + break; + } + } + if (!isConnected) { + for (SiteLayer layer : layers) { + for (int k = 0; k < latticeHeight; k++) { + for (int i = 0; i < latticeLength; i++) { + for (int j = 0; j < latticeWidth; j++) { + layer.delta[k][i][j] = 0; + } + } + } + } + return true; + } + + return false; + } + /** * Extension of {@link arcade.core.util.Graph.Node} for site nodes. * @@ -800,6 +818,10 @@ private ArrayList traverseEdge(SiteNode node, String code) { } } + // If the flow out is zero exit and return no children. + if (flowOut == 0) { + return children; + } // Assign new fractions. for (Object obj : out) { SiteEdge edge = (SiteEdge) obj; @@ -882,6 +904,10 @@ private ArrayList traverseNode(SiteNode node, String code) { node.oxygen = Solver.bisection(func, 0, MAX_OXYGEN_PARTIAL_PRESSURE); } + if (node.oxygen == 0 || Double.isNaN(node.oxygen)) { + LOGGER.info("we might have a problem here"); + } + // Recurse through output edges. for (Object obj : out) { SiteEdge edge = (SiteEdge) obj; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index db9179c76..492fbc1c3 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -1205,6 +1205,42 @@ static void updateRadii( * @param graph the graph object */ static void updateGraph(Graph graph) { + trimGraph(graph); + + calculateCurrentState(graph); + + // Set oxygen nodes. + for (Object obj : graph.getAllEdges()) { + SiteEdge edge = (SiteEdge) obj; + SiteNode to = edge.getTo(); + SiteNode from = edge.getFrom(); + if (Double.isNaN(to.pressure)) { + to.oxygen = Double.NaN; + } + if (Double.isNaN(from.pressure)) { + from.oxygen = Double.NaN; + } + } + } + + /** + * Updates hemodynamic properties based on the current state of the graph. + * + * @param graph the graph object + */ + static void calculateCurrentState(Graph graph) { + do { + calculatePressures(graph); + boolean reversed = reversePressures(graph); + if (reversed) { + calculatePressures(graph); + } + calculateFlows(graph); + calculateStresses(graph); + } while (checkForNegativeFlow(graph)); + } + + static void trimGraph(Graph graph) { ArrayList list; Graph gCurr = graph; @@ -1239,40 +1275,10 @@ static void updateGraph(Graph graph) { gCurr = gNew; } while (list.size() != 0); - - calculateCurrentState(graph); - - // Set oxygen nodes. - for (Object obj : graph.getAllEdges()) { - SiteEdge edge = (SiteEdge) obj; - SiteNode to = edge.getTo(); - SiteNode from = edge.getFrom(); - if (Double.isNaN(to.pressure)) { - to.oxygen = Double.NaN; - } - if (Double.isNaN(from.pressure)) { - from.oxygen = Double.NaN; - } - } - } - - /** - * Updates hemodynamic properties based on the current state of the graph. - * - * @param graph the graph object - */ - static void calculateCurrentState(Graph graph) { - do { - calculatePressures(graph); - boolean reversed = reversePressures(graph); - if (reversed) { - calculatePressures(graph); - } - calculateFlows(graph); - calculateStresses(graph); - } while (checkForNegativeFlow(graph)); } + // This *MIGHT* be a problem, I think we could revisit adding this check. + // I'm not sure why it would get to the point where there would be a negative flow in the graph? static boolean checkForNegativeFlow(Graph graph) { boolean negative = false; for (Object obj : graph.getAllEdges()) { @@ -1306,7 +1312,8 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r for (Object obj : out) { SiteEdge edge = (SiteEdge) obj; if (edge.flow < MINIMUM_FLOW_RATE || Double.isNaN(edge.flow)) { - LOGGER.info("Removing Edge. 1309"); + LOGGER.info("Removing Edge. 1309, edge: " + edge); + LOGGER.info("Flow: " + edge.flow); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -1322,7 +1329,8 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r for (Object obj : in) { SiteEdge edge = (SiteEdge) obj; if (edge.flow < MINIMUM_FLOW_RATE || Double.isNaN(edge.flow)) { - LOGGER.info("Removing Edge. 1325"); + LOGGER.info("Removing Edge. 1325, edge: " + edge); + LOGGER.info("Flow: " + edge.flow); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; @@ -1340,13 +1348,13 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r double totalFlow = edge1.flow + edge2.flow; if (edge1.flow / totalFlow < MINIMUM_FLOW_PERCENT) { - LOGGER.info("Removing Edge. 1343"); + LOGGER.info("Removing Edge. 1343, edge: " + edge1); graph.removeEdge(edge1); edge1.getFrom().pressure = Double.NaN; edge1.getTo().pressure = Double.NaN; updateGraph(graph); } else if (edge2.flow / totalFlow < MINIMUM_FLOW_PERCENT) { - LOGGER.info("Removing Edge. 1349"); + LOGGER.info("Removing Edge. 1349, edge: " + edge2); graph.removeEdge(edge2); edge2.getFrom().pressure = Double.NaN; edge2.getTo().pressure = Double.NaN; @@ -1357,7 +1365,7 @@ static void updateTraverse(Graph graph, LinkedHashSet nodes, boolean r } if (removeMin) { - LOGGER.info("Removing Edge. 1360"); + LOGGER.info("Removing Edge. 1360, edge: " + minEdge); graph.removeEdge(minEdge); minEdge.getFrom().pressure = Double.NaN; minEdge.getTo().pressure = Double.NaN; diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index d7c2c42be..59abb434e 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -94,7 +94,7 @@ - + @@ -166,7 +166,7 @@ - + diff --git a/src/arcade/patch/sim/output/PatchOutputSerializer.java b/src/arcade/patch/sim/output/PatchOutputSerializer.java index cdfa02efa..44996416f 100644 --- a/src/arcade/patch/sim/output/PatchOutputSerializer.java +++ b/src/arcade/patch/sim/output/PatchOutputSerializer.java @@ -244,6 +244,8 @@ public JsonElement serialize( json.addProperty("shear", src.getShear()); json.addProperty("stress", src.getCircum()); json.addProperty("flow", src.getFlow()); + json.addProperty("fraction", src.getFraction()); + json.addProperty("transport", src.getTransport()); return json; } From 8bb6ef69ebc51d49d277122b4aadfda7ec86bb6e Mon Sep 17 00:00:00 2001 From: cainja Date: Mon, 7 Jul 2025 09:46:31 -0700 Subject: [PATCH 37/56] still not establishing edges --- src/arcade/patch/env/component/PatchComponentGrowth.java | 3 +-- src/arcade/patch/env/component/PatchComponentSitesGraph.java | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 01bfba86c..a2a998058 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -821,10 +821,9 @@ private void addAngiogenicEdges( edge.span = sites.getSpan(edge.getFrom(), edge.getTo()); edge.transport.putIfAbsent("GLUCOSE", 0.); edge.transport.putIfAbsent("OXYGEN", 0.); - edge.fraction.putIfAbsent("GLUCOSE", -1.); + edge.fraction.putIfAbsent("GLUCOSE", 1.); edge.length = sites.graphFactory.getLength(edge, DEFAULT_EDGE_LEVEL); edge.isPerfused = true; - edge.isVisited = true; } addEdgeList(angioPath); diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index e74769aad..1f19dc77e 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -904,9 +904,8 @@ private ArrayList traverseNode(SiteNode node, String code) { node.oxygen = Solver.bisection(func, 0, MAX_OXYGEN_PARTIAL_PRESSURE); } - if (node.oxygen == 0 || Double.isNaN(node.oxygen)) { - LOGGER.info("we might have a problem here"); - } + assert node.oxygen >= 0; + assert !Double.isNaN(node.oxygen); // Recurse through output edges. for (Object obj : out) { From 2399cb40319ab1447294face2776a0211c3ff670 Mon Sep 17 00:00:00 2001 From: cainja Date: Tue, 8 Jul 2025 07:48:29 -0700 Subject: [PATCH 38/56] still having pointer issues --- src/arcade/patch/env/component/PatchComponentGrowth.java | 3 +-- src/arcade/patch/parameter.patch.xml | 2 +- test/arcade/patch/env/component/PatchComponentGrowthTest.java | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index a2a998058..0a3e76a05 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -391,10 +391,9 @@ public void step(SimState simstate) { angiogenicNodeMap.get(sproutNode), init, fin, calculationStrategy); sproutNode.anastomosis = false; - - calculateCurrentState(graph); } } + calculateCurrentState(graph); } for (SiteNode n : keyNodesToRemove) { diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index 59abb434e..95519716a 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -94,7 +94,7 @@ - + diff --git a/test/arcade/patch/env/component/PatchComponentGrowthTest.java b/test/arcade/patch/env/component/PatchComponentGrowthTest.java index d576d915c..974c3e083 100644 --- a/test/arcade/patch/env/component/PatchComponentGrowthTest.java +++ b/test/arcade/patch/env/component/PatchComponentGrowthTest.java @@ -80,7 +80,7 @@ public void schedule_calledWithMigrationRateLowerThanEdgeSize_setsScheduleCorrec component.schedule(schedule); - verify(schedule).scheduleRepeating(component, 6, 60); + verify(schedule).scheduleRepeating(component, 10, 60); } @Test @@ -107,7 +107,7 @@ public void schedule_calledWithMigrationRateGreaterThanEdgeSize_setsScheduleCorr component.schedule(schedule); - verify(schedule).scheduleRepeating(component, 6, 30); + verify(schedule).scheduleRepeating(component, 10, 30); } @Test From 79bc465e8567003d1a2cde41991e309bdef7fe94 Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Thu, 10 Jul 2025 23:55:04 -0700 Subject: [PATCH 39/56] temp edge fix --- src/arcade/patch/env/component/PatchComponentGrowth.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 0a3e76a05..8676d4a97 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -1388,10 +1388,14 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { } edge = new SiteEdge(node, existing, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); + edge.setTo(proposed); + edge.setFrom(node); edge.isAnastomotic = true; return edge; } edge = new SiteEdge(node, proposed, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); + edge.setTo(proposed); + edge.setFrom(node); if (graph.contains(edge)) { return null; From 5cf30454c2fde3d4ba32337cf50ccbb7b7ece6ca Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Fri, 11 Jul 2025 13:57:46 -0700 Subject: [PATCH 40/56] adding more specific debug log info --- src/arcade/core/util/Graph.java | 8 ++++---- .../env/component/PatchComponentGrowth.java | 16 +++++++++++----- .../env/component/PatchComponentSitesGraph.java | 8 ++++---- .../PatchComponentSitesGraphUtilities.java | 1 + 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index bf25bc76d..424bc1bdc 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -29,16 +29,16 @@ public enum Strategy { } /** Collection of all {@code Edge} objects in a graph. */ - private final Bag allEdges; + public final Bag allEdges; /** Map of {@code Node} objects, for lookup. */ - private final Map nodes; + public final Map nodes; /** Map of {@code Node} OUT to bag of {@code Edge} objects. */ - private final Map nodeToOutBag; + public final Map nodeToOutBag; /** Map of {@code Node} IN to bag of {@code Edge} objects. */ - private final Map nodeToInBag; + public final Map nodeToInBag; /** Creates an empty {@code Graph}. */ public Graph() { diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 8676d4a97..abc985022 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -317,7 +317,7 @@ public void step(SimState simstate) { SiteNode fin; if (!graph.contains(finalNode)) { - + LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); // Connecting two angiogenic nodes SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); @@ -362,6 +362,7 @@ public void step(SimState simstate) { } // Connecting sprout to existing node + LOGGER.info("CONNECTING SPROUT TO EXISTING NODE"); sproutNode = validateNodeObject(sproutNode); finalNode = validateNodeObject(finalNode); @@ -1048,6 +1049,7 @@ private Outcome recalculateRadii( // divertedFlow, // Adjustment.INCREASE, // ignoredEdges); + LOGGER.info("FAILED TO ADD EDGES: intersection isRoot"); return Outcome.FAILURE; } @@ -1058,6 +1060,7 @@ private Outcome recalculateRadii( Adjustment.DECREASE, ignoredEdges) == Outcome.FAILURE) { + LOGGER.info("FAILED TO ADD EDGES: updateRadius Decrease failed"); return Outcome.FAILURE; } @@ -1068,6 +1071,7 @@ private Outcome recalculateRadii( Adjustment.INCREASE, ignoredEdges) == Outcome.FAILURE) { + LOGGER.info("FAILED TO ADD EDGES: updateRadius Increase failed"); return Outcome.FAILURE; } @@ -1077,6 +1081,7 @@ private Outcome recalculateRadii( path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { + LOGGER.info("FAILED TO ADD EDGES: vein bad?"); return Outcome.FAILURE; // return updateRadiusToRoot( // (SiteEdge) edges.get(angioIndex), @@ -1086,6 +1091,7 @@ private Outcome recalculateRadii( // ignoredEdges); } } + LOGGER.info("FAILED TO ADD EDGES: Intersection Null"); return Outcome.FAILURE; } return Outcome.SUCCESS; @@ -1388,14 +1394,14 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { } edge = new SiteEdge(node, existing, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); - edge.setTo(proposed); - edge.setFrom(node); + // edge.setTo(existing); + // edge.setFrom(node); edge.isAnastomotic = true; return edge; } edge = new SiteEdge(node, proposed, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); - edge.setTo(proposed); - edge.setFrom(node); + // edge.setTo(proposed); + // edge.setFrom(node); if (graph.contains(edge)) { return null; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraph.java b/src/arcade/patch/env/component/PatchComponentSitesGraph.java index 1f19dc77e..83c1f457b 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraph.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraph.java @@ -319,7 +319,7 @@ void complexStep(MersenneTwisterFast random) { extConcNew = extConc; // fmol/um^3 if (Double.isNaN(intConc) || Double.isNaN(extConc)) { - LOGGER.info("NaN"); + LOGGER.info(layer.name + ": NaN (1)"); } } @@ -339,7 +339,7 @@ void complexStep(MersenneTwisterFast random) { extConcNew += dmdt / latticePatchVolume; } if (Double.isNaN(intConcNew) || Double.isNaN(extConcNew)) { - LOGGER.info("NaN"); + LOGGER.info(layer.name + ": NaN (2)"); } } @@ -366,7 +366,7 @@ void complexStep(MersenneTwisterFast random) { edge.transport.put(layer.name, (intConc - intConcNew) * edge.flow); } else { if (Double.isNaN((intConc - intConcNew) / concentration)) { - LOGGER.info("NaN"); + LOGGER.info(layer.name + ": NaN (3)"); } edge.transport.put(layer.name, (intConc - intConcNew) / concentration); } @@ -557,7 +557,7 @@ public static class SiteEdge extends Edge { * @param level the graph resolution level */ SiteEdge(SiteNode from, SiteNode to, EdgeType type, EdgeLevel level) { - super(from, to, type == EdgeType.ANGIOGENIC); + super(from, to, type != EdgeType.ANGIOGENIC); this.type = type; this.level = level; isVisited = false; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 492fbc1c3..8eadfd1df 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -953,6 +953,7 @@ static void path(Graph graph, SiteNode start, SiteNode end) { // If end node found, exit from loop. if (evalNode.equals(end)) { + end.prev = evalNode.prev; break; } From cfad7ebabe9f70349850d1073618cf42f5774edf Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Fri, 11 Jul 2025 14:06:47 -0700 Subject: [PATCH 41/56] debug logging --- src/arcade/core/util/Graph.java | 8 ++++---- src/arcade/patch/env/component/PatchComponentGrowth.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 424bc1bdc..bf25bc76d 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -29,16 +29,16 @@ public enum Strategy { } /** Collection of all {@code Edge} objects in a graph. */ - public final Bag allEdges; + private final Bag allEdges; /** Map of {@code Node} objects, for lookup. */ - public final Map nodes; + private final Map nodes; /** Map of {@code Node} OUT to bag of {@code Edge} objects. */ - public final Map nodeToOutBag; + private final Map nodeToOutBag; /** Map of {@code Node} IN to bag of {@code Edge} objects. */ - public final Map nodeToInBag; + private final Map nodeToInBag; /** Creates an empty {@code Graph}. */ public Graph() { diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index abc985022..c952824f0 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -317,7 +317,6 @@ public void step(SimState simstate) { SiteNode fin; if (!graph.contains(finalNode)) { - LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); // Connecting two angiogenic nodes SiteNode targetNode = findKeyNodeInMap(finalNode, sproutNode); @@ -329,6 +328,7 @@ public void step(SimState simstate) { keyNodesToRemove.add(targetNode); // Connecting sprout to existing node + LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); sproutNode = validateNodeObject(sproutNode); targetNode = validateNodeObject(targetNode); From 5e74cb3d984e2fd260a28f317e0d1e209494b785 Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Fri, 11 Jul 2025 17:11:19 -0700 Subject: [PATCH 42/56] more debug logs --- src/arcade/patch/env/component/PatchComponentGrowth.java | 3 +++ .../patch/env/component/PatchComponentSitesGraphUtilities.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index c952824f0..3a10e8dcc 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -1019,6 +1019,7 @@ private Outcome recalculateRadii( edges = graph.getEdgesOut(start); if (edges.size() != 2) { + LOGGER.info("FAILED TO ADD EDGES: start outEdges != 2"); return Outcome.FAILURE; } @@ -1027,6 +1028,7 @@ private Outcome recalculateRadii( double deltaP = start.pressure - end.pressure; if (start.pressure * end.pressure == 0 || Double.isNaN(deltaP)) { + LOGGER.info("FAILED TO ADD EDGES: 0 or NaN pressure"); return Outcome.FAILURE; } @@ -1038,6 +1040,7 @@ private Outcome recalculateRadii( Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { + LOGGER.info("FAILED TO ADD EDGES: divertedFlow > originalflow"); return Outcome.FAILURE; } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 8eadfd1df..aecbae63a 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -953,7 +953,7 @@ static void path(Graph graph, SiteNode start, SiteNode end) { // If end node found, exit from loop. if (evalNode.equals(end)) { - end.prev = evalNode.prev; + // end.prev = evalNode.prev; break; } From db39c38bf84944b73d05066c997afc2f9234c1b8 Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Mon, 14 Jul 2025 17:09:24 -0700 Subject: [PATCH 43/56] fix connecting two angio nodes and null sproutDir --- .../env/component/PatchComponentGrowth.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 3a10e8dcc..34baf8c9f 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -277,8 +277,6 @@ public void step(SimState simstate) { buildDirectionalSpanMap(vegfLattice, node); if (averageDirectionalMap(vegfMap) > vegfThreshold) { - angiogenicNodeMap.put(node, new ArrayList<>()); - Bag in = graph.getEdgesIn(node); Bag out = graph.getEdgesOut(node); SiteEdge inEdge = (SiteEdge) in.get(0); @@ -287,6 +285,12 @@ public void step(SimState simstate) { SiteEdge outEdge = (SiteEdge) out.get(0); vegfMap.remove(sites.graphFactory.getDirection(outEdge, DEFAULT_EDGE_LEVEL)); + if (vegfMap.isEmpty()) { + continue; + } + + angiogenicNodeMap.put(node, new ArrayList<>()); + EnumMap vegfAverages = getDirectionalAverages(vegfMap); node.sproutDir = performWalk(random, node, vegfAverages); @@ -351,6 +355,14 @@ public void step(SimState simstate) { } angiogenicNodeMap.get(sproutNode).addAll(angiogenicNodeMap.get(targetNode)); + for (SiteEdge e : angiogenicNodeMap.get(sproutNode)) { + if (e.getTo().equals(finalNode)) { + e.setTo(finalNode); + } else if (e.getFrom().equals(finalNode)) { + e.setFrom(finalNode); + } + } + } else { keyNodesToRemove.add(sproutNode); keyNodesToRemove.add(finalNode); @@ -1119,6 +1131,7 @@ private Outcome updateRadius( ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); if (edgesToUpdate == null) { + LOGGER.info("NO EDGES TO UPDATE"); return Outcome.FAILURE; } @@ -1397,14 +1410,10 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { } edge = new SiteEdge(node, existing, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); - // edge.setTo(existing); - // edge.setFrom(node); edge.isAnastomotic = true; return edge; } edge = new SiteEdge(node, proposed, DEFAULT_EDGE_TYPE, DEFAULT_EDGE_LEVEL); - // edge.setTo(proposed); - // edge.setFrom(node); if (graph.contains(edge)) { return null; From b1d605e845bab6a3288d2b333fd93187c01c021d Mon Sep 17 00:00:00 2001 From: cainja Date: Thu, 17 Jul 2025 14:42:43 -0700 Subject: [PATCH 44/56] simulation growth is behaving abnormally --- .../env/component/PatchComponentGrowth.java | 8 +-- src/arcade/patch/parameter.patch.xml | 4 +- .../component/PatchComponentGrowthTest.java | 70 +++++++++++++++++++ ...atchComponentSitesGraphFactoryTriTest.java | 4 +- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 34baf8c9f..90df4479e 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -36,11 +36,10 @@ import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.calculateThickness; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.getPath; import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.path; -import static arcade.patch.env.component.PatchComponentSitesGraphUtilities.updateGraph; import static arcade.patch.util.PatchEnums.Ordering; /** - * Implementation of {@link Component} for degrading graph edges. + * Implementation of {@link Component} for adding graph edges . * *

This component can only be used with {@link PatchComponentsSitesGraph}. The component is * stepped according to a reasonable interval based on the specified {@code MIGRATION_RATE}. @@ -551,7 +550,7 @@ private void reverseAllEdges(SiteNode node) { * @param skipNode {@link SiteNode} to ignore in the map * @return {@link SiteNode} key for the targetNode object */ - private SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { + SiteNode findKeyNodeInMap(SiteNode targetNode, SiteNode skipNode) { for (SiteNode keyNode : angiogenicNodeMap.keySet()) { if (keyNode.equals(skipNode)) { continue; @@ -1026,9 +1025,6 @@ private Outcome recalculateRadii( assert edges != null; assert edges.size() >= 2; - updateGraph( - graph); // I think this is unnecessary but was in the previous version TODO: check - edges = graph.getEdgesOut(start); if (edges.size() != 2) { LOGGER.info("FAILED TO ADD EDGES: start outEdges != 2"); diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index 95519716a..75876148b 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -94,7 +94,7 @@ - + @@ -168,6 +168,6 @@ - + diff --git a/test/arcade/patch/env/component/PatchComponentGrowthTest.java b/test/arcade/patch/env/component/PatchComponentGrowthTest.java index 974c3e083..e6a7575bf 100644 --- a/test/arcade/patch/env/component/PatchComponentGrowthTest.java +++ b/test/arcade/patch/env/component/PatchComponentGrowthTest.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; +import java.util.HashMap; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import sim.engine.Schedule; @@ -14,7 +15,11 @@ import arcade.core.util.exceptions.MissingSpecificationException; import arcade.patch.agent.process.PatchProcessMetabolism; import arcade.patch.agent.process.PatchProcessSignaling; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteNode; import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeDirection; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeLevel; +import arcade.patch.env.component.PatchComponentSitesGraphFactory.EdgeType; import arcade.patch.env.grid.PatchGrid; import arcade.patch.env.lattice.PatchLattice; import arcade.patch.sim.PatchSeries; @@ -246,4 +251,69 @@ public void averageDirectionalMap_givenMap_returnsCorrectAverage() { map.put(EdgeDirection.RIGHT, new ArrayList<>(Arrays.asList(10.0, 11.0, 12.0))); assertEquals(6.5, PatchComponentGrowth.averageDirectionalMap(map)); } + + @Test + public void findKeyNodeInMap_givenTargetNode_returnsCorrectKeyNode() { + SiteNode targetNode = new SiteNode(0, 0, 0); + SiteNode originalNode = new SiteNode(1, 1, 0); + SiteNode connectingNode = new SiteNode(1, 0, 0); + + SiteNode node2 = new SiteNode(0, 2, 0); + SiteNode node3 = new SiteNode(0, 3, 0); + SiteNode node4 = new SiteNode(0, 4, 0); + SiteNode node5 = new SiteNode(0, 5, 0); + + // create maps with different key site nodes that contain list of edges where only one + // contains the target node + HashMap> angiogenicNodeMap = new HashMap<>(); + ArrayList edges = new ArrayList<>(); + SiteEdge edge = + new SiteEdge(targetNode, connectingNode, EdgeType.ANGIOGENIC, EdgeLevel.LEVEL_2); + edges.add(edge); + angiogenicNodeMap.put(targetNode, edges); + + edges = new ArrayList<>(); + edge = new SiteEdge(originalNode, connectingNode, EdgeType.ANGIOGENIC, EdgeLevel.LEVEL_2); + edges.add(edge); + angiogenicNodeMap.put(originalNode, edges); + + edges = new ArrayList<>(); + edge = new SiteEdge(node2, node3, EdgeType.ANGIOGENIC, EdgeLevel.LEVEL_2); + edges.add(edge); + angiogenicNodeMap.put(node2, edges); + + edges = new ArrayList<>(); + edge = new SiteEdge(node4, node5, EdgeType.ANGIOGENIC, EdgeLevel.LEVEL_2); + edges.add(edge); + angiogenicNodeMap.put(node4, edges); + + MiniBox parameters = new MiniBox(); + parameters.put("MIGRATION_RATE", 60); + parameters.put("VEGF_THRESHOLD", 0.5); + parameters.put("WALK_TYPE", "BIASED"); + parameters.put("MAX_LENGTH", 100); + parameters.put("CALCULATION_STRATEGY", "DIVERT"); + + PatchComponentSitesGraph graph = mock(PatchComponentSitesGraph.class); + doReturn(graph).when(simMock).getComponent("COMPATIBLE"); + when(simMock.getLattice("VEGF")).thenReturn(latticeMock); + + try { + Field field = PatchComponentSitesGraph.class.getDeclaredField("graphFactory"); + field.setAccessible(true); + field.set(graph, mock(PatchComponentSitesGraphFactory.class)); + } catch (Exception ignored) { + } + + PatchComponentGrowth component = new PatchComponentGrowth(seriesMock, parameters); + + try { + Field field = PatchComponentGrowth.class.getDeclaredField("angiogenicNodeMap"); + field.setAccessible(true); + field.set(component, angiogenicNodeMap); + } catch (Exception ignored) { + } + + assertEquals(targetNode, component.findKeyNodeInMap(connectingNode, originalNode)); + } } diff --git a/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java b/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java index 36b85c351..271ea6155 100644 --- a/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java +++ b/test/arcade/patch/env/component/PatchComponentSitesGraphFactoryTriTest.java @@ -19,8 +19,8 @@ public void getDirection_givenFromTo_returnsCorrectDirection() { PatchComponentSitesGraphFactoryTri factory = new PatchComponentSitesGraphFactoryTri(seriesMock); - int x = randomIntBetween(0, 100); - int y = randomIntBetween(0, 100); + int x = randomIntBetween(10, 100); + int y = randomIntBetween(10, 100); int z = randomIntBetween(0, 100); assertEquals( From 317484ad76abce30c3f45990ba17ecfa535247a1 Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 20 Jul 2025 16:13:51 -0700 Subject: [PATCH 45/56] remove node delay logic --- .../env/component/PatchComponentGrowth.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 90df4479e..c1fdb32be 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -520,14 +520,14 @@ private boolean checkNodeSkipStatus(SiteNode node) { if ((tick - node.addTime) < (72 * 60)) { return true; } - if (nodeDelays.containsKey(node)) { - nodeDelays.put(node, nodeDelays.get(node) + 1); - if (nodeDelays.get(node) > 30) { - nodeDelays.remove(node); - return false; - } - return true; - } + // if (nodeDelays.containsKey(node)) { + // nodeDelays.put(node, nodeDelays.get(node) + 1); + // if (nodeDelays.get(node) > 30) { + // nodeDelays.remove(node); + // return false; + // } + // return true; + // } return false; } From d931ac1c5f1c79dbc791848956061096d56d7676 Mon Sep 17 00:00:00 2001 From: cainja Date: Fri, 25 Jul 2025 09:08:14 -0700 Subject: [PATCH 46/56] changed node verification logic --- .../patch/env/component/PatchComponentDegrade.java | 1 - src/arcade/patch/env/component/PatchComponentGrowth.java | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentDegrade.java b/src/arcade/patch/env/component/PatchComponentDegrade.java index 28944ed66..cfdc99be9 100644 --- a/src/arcade/patch/env/component/PatchComponentDegrade.java +++ b/src/arcade/patch/env/component/PatchComponentDegrade.java @@ -117,7 +117,6 @@ public void step(SimState state) { if (edge.wall <= MINIMUM_WALL_THICKNESS && (edge.shear < shearThreshold || Double.isNaN(edge.shear))) { - LOGGER.info("Removing Edge."); graph.removeEdge(edge); edge.getFrom().pressure = Double.NaN; edge.getTo().pressure = Double.NaN; diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index c1fdb32be..2c0f0a204 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -422,6 +422,11 @@ private SiteNode validateNodeObject(SiteNode node) { node = (SiteNode) graph.lookup(node); if (graph.getEdgesOut(node) != null && node.pressure == 0.0) { node = ((SiteEdge) graph.getEdgesOut(node).get(0)).getFrom(); + return node; + } + if (graph.getEdgesIn(node) != null && node.pressure == 0.0) { + node = ((SiteEdge) graph.getEdgesIn(node).get(0)).getTo(); + return node; } } return node; @@ -1397,11 +1402,11 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { || graph.getEdgesIn(proposed) == null) { return null; } - SiteNode existing = (SiteNode) graph.lookup(proposed); + SiteNode existing = validateNodeObject(proposed); assert existing != null; - if (existing.pressure == 0 || Double.isNaN(existing.pressure)) { + if (Double.isNaN(existing.pressure)) { return null; } From df34e857eef7e2a4c3c60b86d730f87b7c382ba1 Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Mon, 28 Jul 2025 17:08:06 -0700 Subject: [PATCH 47/56] Fixed mergeGraph() node map problem, added more debug logs --- src/arcade/core/util/Graph.java | 64 +++++++++++++++++-- .../env/component/PatchComponentGrowth.java | 10 ++- .../PatchComponentSitesGraphFactory.java | 2 +- .../PatchComponentSitesGraphUtilities.java | 8 ++- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index bf25bc76d..7e5c6a55d 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -77,6 +77,18 @@ private void updateNodes() { } } + public void combine(Graph graph1, Graph graph2) { + clear(); + for (Object obj : graph2.getAllEdges()) { + Edge edge = (Edge) obj; + addEdge(edge); + } + for (Object obj : graph1.getAllEdges()) { + Edge edge = (Edge) obj; + addEdge(edge); + } + } + /** Clear edges and nodes from graph. */ public void clear() { allEdges.clear(); @@ -251,6 +263,7 @@ public void getSubgraph(Graph g, GraphFilter f) { for (Object obj : allEdges) { Edge edge = (Edge) obj; if (f.filter(edge)) { + g.addNodes(edge); g.allEdges.add(edge); g.setOutMap(edge.getFrom(), edge); g.setInMap(edge.getTo(), edge); @@ -321,10 +334,18 @@ public void addEdge(Node from, Node to) { * @param edge the edge to add */ public void addEdge(Edge edge) { + addNodes(edge); allEdges.add(edge); setOutMap(edge.getFrom(), edge); setInMap(edge.getTo(), edge); setLinks(edge); + } + + public void addEdge(Edge edge, boolean duplicate) { + allEdges.add(edge); + setOutMap(edge.getFrom(), edge, duplicate); + setInMap(edge.getTo(), edge, duplicate); + setLinks(edge); addNodes(edge); } @@ -339,17 +360,18 @@ private void addNodes(Edge edge) { if (!nodes.containsKey(from.toString())) { nodes.put(from.toString(), from); } else { - from = nodes.get(from.toString()); + edge.setFrom(nodes.get(from.toString())); } if (!nodes.containsKey(to.toString())) { nodes.put(to.toString(), to); } else { - to = nodes.get(to.toString()); + edge.setTo(nodes.get(to.toString())); } } /** - * Adds the edge to the bag for the mapping of OUT node to edge. + * Adds the edge to the bag for the mapping of OUT node to edge. Default behavior is to + * duplicate nodes. * * @param node the node hash * @param edge the edge @@ -362,9 +384,26 @@ private void setOutMap(Node node, Edge edge) { } objs.add(edge); } + /** + * Adds the edge to the bag for the mapping of OUT node to edge. Used in cases + * new node object is not needed. + * + * @param node the node hash + * @param edge the edge + */ + private void setOutMap(Node node, Edge edge, boolean duplicate) { + Bag objs = nodeToOutBag.get(node); + if (objs == null) { + objs = new Bag(10); + Node bagNode = duplicate ? node.duplicate() : node; + nodeToOutBag.put(bagNode, objs); + } + objs.add(edge); + } /** - * Adds the edge to the bag for the mapping of IN node to edge. + * Adds the edge to the bag for the mapping of IN node to edge. Default behavior is to + * duplicate nodes. * * @param node the node hash * @param edge the edge @@ -378,6 +417,23 @@ private void setInMap(Node node, Edge edge) { objs.add(edge); } + /** + * Adds the edge to the bag for the mapping of OUT node to edge. Used in cases + * new node object is not needed. + * + * @param node the node hash + * @param edge the edge + */ + private void setInMap(Node node, Edge edge, boolean duplicate) { + Bag objs = nodeToInBag.get(node); + if (objs == null) { + objs = new Bag(10); + Node bagNode = duplicate ? node.duplicate() : node; + nodeToInBag.put(bagNode, objs); + } + objs.add(edge); + } + /** * Adds links between edges in and out of the nodes for a given edge. * diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 2c0f0a204..3477a6201 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -842,6 +842,9 @@ private void addAngiogenicEdges( edge.isPerfused = true; } + if (angioPath.size() > 2) { + LOGGER.info("angioPath is more than 2"); + } addEdgeList(angioPath); switch (calc) { @@ -1402,11 +1405,16 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { || graph.getEdgesIn(proposed) == null) { return null; } - SiteNode existing = validateNodeObject(proposed); + // SiteNode existing = validateNodeObject(proposed); + SiteNode existing = (SiteNode) graph.lookup(proposed); assert existing != null; + if (existing.pressure == 0) { + LOGGER.info("WARNING: PRESSURE 0"); + } if (Double.isNaN(existing.pressure)) { + LOGGER.info("WARNING: PRESSURE NAN"); return null; } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java index 857acce57..87b42fe73 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphFactory.java @@ -783,7 +783,7 @@ private void updateRootGraph(Graph graph, EdgeLevel level, MersenneTwisterFast r Graph g2 = new Graph(); graph.getSubgraph(g1, e -> ((SiteEdge) e).level == EdgeLevel.LEVEL_1); graph.getSubgraph(g2, e -> ((SiteEdge) e).level == EdgeLevel.LEVEL_2); - mergeGraphs(g1, g2); + mergeGraphs(graph, g1, g2); break; default: break; diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index aecbae63a..e13c8a63a 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -364,10 +364,11 @@ static void checkPerfused(Graph graph, ArrayList arteries, ArrayList /** * Merges the nodes from one graph with another graph. * - * @param graph1 the first graph object - * @param graph2 the second graph object + * @param graph the original graph object + * @param graph1 the first subgraph object + * @param graph2 the second subgraph object */ - static void mergeGraphs(Graph graph1, Graph graph2) { + static void mergeGraphs(Graph graph, Graph graph1, Graph graph2) { // Merge nodes for subgraph. graph2.mergeNodes(); @@ -390,6 +391,7 @@ static void mergeGraphs(Graph graph1, Graph graph2) { } } } + graph.combine(graph1, graph2); } /** From f94e0dafe9a02b6dd08d4a7d488fe809d1769207 Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Mon, 28 Jul 2025 17:13:52 -0700 Subject: [PATCH 48/56] style fixes --- src/arcade/core/util/Graph.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/arcade/core/util/Graph.java b/src/arcade/core/util/Graph.java index 7e5c6a55d..25c292f96 100644 --- a/src/arcade/core/util/Graph.java +++ b/src/arcade/core/util/Graph.java @@ -384,9 +384,10 @@ private void setOutMap(Node node, Edge edge) { } objs.add(edge); } + /** - * Adds the edge to the bag for the mapping of OUT node to edge. Used in cases - * new node object is not needed. + * Adds the edge to the bag for the mapping of OUT node to edge. Used in cases new node object + * is not needed. * * @param node the node hash * @param edge the edge @@ -402,8 +403,8 @@ private void setOutMap(Node node, Edge edge, boolean duplicate) { } /** - * Adds the edge to the bag for the mapping of IN node to edge. Default behavior is to - * duplicate nodes. + * Adds the edge to the bag for the mapping of IN node to edge. Default behavior is to duplicate + * nodes. * * @param node the node hash * @param edge the edge @@ -418,8 +419,8 @@ private void setInMap(Node node, Edge edge) { } /** - * Adds the edge to the bag for the mapping of OUT node to edge. Used in cases - * new node object is not needed. + * Adds the edge to the bag for the mapping of OUT node to edge. Used in cases new node object + * is not needed. * * @param node the node hash * @param edge the edge From 7cec022f5fa8d09f8e98910d91171b1c9d13d230 Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Mon, 28 Jul 2025 21:34:13 -0700 Subject: [PATCH 49/56] fixing undefined edge direction, debug logger for angiogenicNodeMap tracking --- .../env/component/PatchComponentGrowth.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 3477a6201..9ee68f378 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -297,6 +297,11 @@ public void step(SimState simstate) { } boolean addFlag = propogateEdges(); + for (SiteNode node : angiogenicNodeMap.keySet()) { + if (angiogenicNodeMap.get(node).size() > 1) { + LOGGER.info("MORE THAN ONE EDGE IN ANGIOGENIC NODE MAP"); + } + } if (addFlag) { for (SiteNode sproutNode : angiogenicNodeMap.keySet()) { if (keyNodesToRemove.contains(sproutNode)) { @@ -525,14 +530,6 @@ private boolean checkNodeSkipStatus(SiteNode node) { if ((tick - node.addTime) < (72 * 60)) { return true; } - // if (nodeDelays.containsKey(node)) { - // nodeDelays.put(node, nodeDelays.get(node) + 1); - // if (nodeDelays.get(node) > 30) { - // nodeDelays.remove(node); - // return false; - // } - // return true; - // } return false; } @@ -713,7 +710,11 @@ static EnumMap getDirectionalAverages( for (double value : map.get(dir)) { sum += value; } - averageMap.put(dir, sum / map.get(dir).size()); + if (map.get(dir).size() == 0) { + averageMap.put(dir, 0.0); + } else { + averageMap.put(dir, sum / map.get(dir).size()); + } } return averageMap; } From 45ad5b33da7d794793b2393dbc3bc1185ec413fb Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Tue, 29 Jul 2025 13:12:22 -0700 Subject: [PATCH 50/56] fixing keyNodesToRemove logic --- src/arcade/patch/env/component/PatchComponentGrowth.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 9ee68f378..764f5b6c5 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -474,7 +474,7 @@ private boolean propogateEdges() { newEdge = createNewEdge(sprout.sproutDir, tipNode); - if (edgeList.size() == maxEdges || newEdge == null || graph.getDegree(sprout) == 3) { + if (edgeList.size() == maxEdges || newEdge == null) { keyNodesToRemove.add(sprout); } else { edgeList.add(newEdge); From 7656e5c1a150fd00bea49a45ddbb4b0384c4456c Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Thu, 31 Jul 2025 19:04:37 -0700 Subject: [PATCH 51/56] fixed issue where two sproutNodes intersected on the same edgeList --- .../env/component/PatchComponentGrowth.java | 46 ++++++++++++------- .../PatchComponentSitesGraphUtilities.java | 3 ++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 764f5b6c5..61e273cd8 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -157,6 +157,9 @@ private enum Outcome { /** Map of nodes to a delay to prevent nodes from constantly trying to add the same edge. */ private HashMap nodeDelays = new HashMap<>(); + /** List of noddes to track which nodes have been added in each timestep. */ + private ArrayList addedNodes = new ArrayList<>(); + /** * Creates a growth component for a {@link PatchComponentSitesGraph}. * @@ -297,11 +300,6 @@ public void step(SimState simstate) { } boolean addFlag = propogateEdges(); - for (SiteNode node : angiogenicNodeMap.keySet()) { - if (angiogenicNodeMap.get(node).size() > 1) { - LOGGER.info("MORE THAN ONE EDGE IN ANGIOGENIC NODE MAP"); - } - } if (addFlag) { for (SiteNode sproutNode : angiogenicNodeMap.keySet()) { if (keyNodesToRemove.contains(sproutNode)) { @@ -331,14 +329,16 @@ public void step(SimState simstate) { if (targetNode == null) { sproutNode.anastomosis = false; continue; + } else if (addedNodes.contains(targetNode)) { + sproutNode.anastomosis = false; + keyNodesToRemove.add(sproutNode); + keyNodesToRemove.add(targetNode); + continue; } keyNodesToRemove.add(sproutNode); keyNodesToRemove.add(targetNode); - // Connecting sprout to existing node - LOGGER.info("CONNECTING TWO ANGIOGENIC NODES"); - sproutNode = validateNodeObject(sproutNode); - targetNode = validateNodeObject(targetNode); + LOGGER.info("CONNECTING TWO ANGIOGENIC NODES, tick: " + tick); if (sproutNode.pressure == 0 || targetNode.pressure == 0 @@ -358,7 +358,6 @@ public void step(SimState simstate) { fin = targetNode; } angiogenicNodeMap.get(sproutNode).addAll(angiogenicNodeMap.get(targetNode)); - for (SiteEdge e : angiogenicNodeMap.get(sproutNode)) { if (e.getTo().equals(finalNode)) { e.setTo(finalNode); @@ -419,6 +418,7 @@ public void step(SimState simstate) { } } keyNodesToRemove.clear(); + addedNodes.clear(); } private SiteNode validateNodeObject(SiteNode node) { @@ -821,6 +821,12 @@ private void addAngiogenicEdges( // update edges in the minimal path between start and end ArrayList angioPath = getPath(tempGraph, start, end); + if (angioPath == null || angioPath.isEmpty()) { + LOGGER.info("ANGIOPATH IS NULL"); + LOGGER.info("START: " + start.toString()); + LOGGER.info("END: " + end.toString()); + LOGGER.info("EDGELIST: " + list); + } for (SiteEdge edge : angioPath) { if (graph.contains(edge)) { @@ -843,10 +849,9 @@ private void addAngiogenicEdges( edge.isPerfused = true; } - if (angioPath.size() > 2) { - LOGGER.info("angioPath is more than 2"); - } addEdgeList(angioPath); + addedNodes.add(start); + addNodesInEdgeList(angioPath); switch (calc) { case COMPENSATE: @@ -893,6 +898,16 @@ private void addAngiogenicEdges( } } + private void addNodesInEdgeList(ArrayList edgeList) { + if (edgeList == null || edgeList.isEmpty()) { + return; + } + for (SiteEdge edge : edgeList) { + SiteNode to = edge.getTo(); + addedNodes.add(to); + } + } + /** * Private helper function for calculating the new radius of two edges after splitting flow * evenly. @@ -1411,11 +1426,8 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { assert existing != null; - if (existing.pressure == 0) { - LOGGER.info("WARNING: PRESSURE 0"); - } if (Double.isNaN(existing.pressure)) { - LOGGER.info("WARNING: PRESSURE NAN"); + LOGGER.info("WARNING: PRESSURE NAN ---- Node: " + existing.toString()); return null; } diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index e13c8a63a..0c0d30cb4 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -1003,6 +1003,9 @@ static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { } while (node != null && !node.equals(start)) { Bag b = graph.getEdgesIn(node); + if (b == null) { + LOGGER.info("NODETOINBAG IS NULL IN GETPATH FOR NODE " + node.toString()); + } if (b.numObjs == 1) { path.add((SiteEdge) b.objs[0]); } else if (b.numObjs == 2) { From 36d6e6a5d398c91f89692edc2fbeddde15dd084a Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Fri, 1 Aug 2025 12:08:32 -0700 Subject: [PATCH 52/56] removing debug logs, add updateRadiusToRoot back --- .../env/component/PatchComponentGrowth.java | 72 ++++--------------- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 61e273cd8..05d14ca55 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -310,7 +310,6 @@ public void step(SimState simstate) { nodeDelays.putIfAbsent(sproutNode, 0); if (graph.getDegree(sproutNode) > 2) { - LOGGER.info("Sprout node has more than two degrees."); keyNodesToRemove.add(sproutNode); continue; } @@ -338,13 +337,11 @@ public void step(SimState simstate) { keyNodesToRemove.add(sproutNode); keyNodesToRemove.add(targetNode); - LOGGER.info("CONNECTING TWO ANGIOGENIC NODES, tick: " + tick); - if (sproutNode.pressure == 0 || targetNode.pressure == 0 || Double.isNaN(sproutNode.pressure) || Double.isNaN(targetNode.pressure)) { - LOGGER.info("Cannot connect nodes (332)."); + LOGGER.info("Cannot connect nodes (332). Tick " + tick); continue; } @@ -377,7 +374,6 @@ public void step(SimState simstate) { } // Connecting sprout to existing node - LOGGER.info("CONNECTING SPROUT TO EXISTING NODE"); sproutNode = validateNodeObject(sproutNode); finalNode = validateNodeObject(finalNode); @@ -385,7 +381,7 @@ public void step(SimState simstate) { || finalNode.pressure == 0 || Double.isNaN(sproutNode.pressure) || Double.isNaN(finalNode.pressure)) { - LOGGER.info("Cannot connect nodes (366)."); + LOGGER.info("Cannot connect nodes (366). Tick " + tick); continue; } @@ -866,33 +862,6 @@ private void addAngiogenicEdges( Outcome result = recalculateRadii(angioPath, start, end, intersection); if (result == Outcome.FAILURE) { removeEdgeList(angioPath); - } else { - LOGGER.info( - "======================================================================="); - - LOGGER.info("Added " + angioPath + " path at tick: " + tick); - LOGGER.info("Sprout Node: " + start); - LOGGER.info("Sprout Edge: " + start.sproutDir); - for (Object obj : graph.getEdgesOut(start)) { - SiteEdge edge = (SiteEdge) obj; - LOGGER.info("Out Edge: " + edge); - } - for (Object obj : graph.getEdgesIn(start)) { - SiteEdge edge = (SiteEdge) obj; - LOGGER.info("In Edge: " + edge); - } - LOGGER.info("End Node: " + end); - for (Object obj : graph.getEdgesOut(end)) { - SiteEdge edge = (SiteEdge) obj; - LOGGER.info("Out Edge: " + edge); - } - for (Object obj : graph.getEdgesIn(end)) { - SiteEdge edge = (SiteEdge) obj; - LOGGER.info("In Edge: " + edge); - } - - LOGGER.info( - "======================================================================="); } break; } @@ -1051,7 +1020,6 @@ private Outcome recalculateRadii( edges = graph.getEdgesOut(start); if (edges.size() != 2) { - LOGGER.info("FAILED TO ADD EDGES: start outEdges != 2"); return Outcome.FAILURE; } @@ -1072,20 +1040,17 @@ private Outcome recalculateRadii( Double originalFlow = ((SiteEdge) edges.get(nonAngioIndex)).flow; if (divertedFlow > originalFlow) { - LOGGER.info("FAILED TO ADD EDGES: divertedFlow > originalflow"); return Outcome.FAILURE; } if (intersection != null) { if (intersection.isRoot) { - // return updateRadiusToRoot( - // (SiteEdge) edges.get(angioIndex), - // sites.graphFactory.veins.get(0).node, - // divertedFlow, - // Adjustment.INCREASE, - // ignoredEdges); - LOGGER.info("FAILED TO ADD EDGES: intersection isRoot"); - return Outcome.FAILURE; + return updateRadiusToRoot( + (SiteEdge) edges.get(angioIndex), + sites.graphFactory.veins.get(0).node, + divertedFlow, + Adjustment.INCREASE, + ignoredEdges); } if (updateRadius( @@ -1095,7 +1060,6 @@ private Outcome recalculateRadii( Adjustment.DECREASE, ignoredEdges) == Outcome.FAILURE) { - LOGGER.info("FAILED TO ADD EDGES: updateRadius Decrease failed"); return Outcome.FAILURE; } @@ -1106,7 +1070,6 @@ private Outcome recalculateRadii( Adjustment.INCREASE, ignoredEdges) == Outcome.FAILURE) { - LOGGER.info("FAILED TO ADD EDGES: updateRadius Increase failed"); return Outcome.FAILURE; } @@ -1116,14 +1079,12 @@ private Outcome recalculateRadii( path(graph, start, boundary); if (boundary.prev != null && ((SiteEdge) edges.get(angioIndex)).radius > MINIMUM_CAPILLARY_RADIUS) { - LOGGER.info("FAILED TO ADD EDGES: vein bad?"); - return Outcome.FAILURE; - // return updateRadiusToRoot( - // (SiteEdge) edges.get(angioIndex), - // sites.graphFactory.veins.get(0).node, - // divertedFlow, - // Adjustment.INCREASE, - // ignoredEdges); + return updateRadiusToRoot( + (SiteEdge) edges.get(angioIndex), + sites.graphFactory.veins.get(0).node, + divertedFlow, + Adjustment.INCREASE, + ignoredEdges); } } LOGGER.info("FAILED TO ADD EDGES: Intersection Null"); @@ -1151,7 +1112,6 @@ private Outcome updateRadius( ArrayList edgesToUpdate = getPath(graph, edge.getTo(), intersection); if (edgesToUpdate == null) { - LOGGER.info("NO EDGES TO UPDATE"); return Outcome.FAILURE; } @@ -1195,7 +1155,6 @@ private Outcome updateRadiiOfEdgeList( } if (calculateRadius(e, flow, adjustment) == Outcome.FAILURE) { resetRadii(edges, oldRadii); - LOGGER.info("Failed to update radius for edges, resetting to old radii."); return Outcome.FAILURE; } } @@ -1233,12 +1192,10 @@ private Outcome calculateRadius(SiteEdge edge, double flow, Adjustment adjustmen try { newRadius = Solver.bisection(f, 1E-6, 5 * MAXIMUM_CAPILLARY_RADIUS, 1E-6); } catch (Exception e) { - LOGGER.info("Bisection failed: " + e.getMessage()); return Outcome.FAILURE; } if (newRadius == 1E-6) { - LOGGER.info("New radius is too small: " + newRadius); return Outcome.FAILURE; } edge.radius = newRadius; @@ -1427,7 +1384,6 @@ private SiteEdge createNewEdge(EdgeDirection direction, SiteNode node) { assert existing != null; if (Double.isNaN(existing.pressure)) { - LOGGER.info("WARNING: PRESSURE NAN ---- Node: " + existing.toString()); return null; } From 7ab6c1a498edc91f1b1352cea9493f6e3390a84b Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 3 Aug 2025 14:12:34 -0700 Subject: [PATCH 53/56] resolved conflict --- src/arcade/patch/env/component/PatchComponentGrowth.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 05d14ca55..8be9e6cf5 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -337,6 +337,10 @@ public void step(SimState simstate) { keyNodesToRemove.add(sproutNode); keyNodesToRemove.add(targetNode); + // Connecting sprout to existing node + sproutNode = validateNodeObject(sproutNode); + targetNode = validateNodeObject(targetNode); + if (sproutNode.pressure == 0 || targetNode.pressure == 0 || Double.isNaN(sproutNode.pressure) From b3da84ac44a895223ea6511a289c468ac59e298a Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 3 Aug 2025 14:35:59 -0700 Subject: [PATCH 54/56] added back in node delay logic, removed superfluous logging statements --- .../env/component/PatchComponentGrowth.java | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 8be9e6cf5..bc78252cb 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -39,15 +39,15 @@ import static arcade.patch.util.PatchEnums.Ordering; /** - * Implementation of {@link Component} for adding graph edges . + * Implementation of {@link Component} for adding graph edges and resolving mass balances. * *

This component can only be used with {@link PatchComponentsSitesGraph}. The component is * stepped according to a reasonable interval based on the specified {@code MIGRATION_RATE}. * Generally, if the average VEGF concentration in the immediate neighborhood of a node is greater * than the threshold {@code VEGF_THRESHOLD} the node will be flagged for sprouting. The sprout * direction is determined by the {@code WALK_TYPE} and the maximum length of migration is - * determined by the {@code MAX_LENGTH}. The walk types can be specified as {@code RANDOM}, {@code - * DETERMINISTIC}, or {@code BIASED}. + * determined by the {@code MAX_LENGTH}. The walk types can bvalidateecified as {@code RANDOM}, + * {@code DETERMINISTIC}, or {@code BIASED}. * *

New edges are added based on the specified {@link CAPILLARY_RADIUS}. The calculation of the * resulting hemodynamics is based on two strategies. The first is {@code COMPENSATE} which @@ -65,6 +65,12 @@ public class PatchComponentGrowth implements Component { /** Default edge type to add to the graph from this component. */ private static final EdgeType DEFAULT_EDGE_TYPE = EdgeType.ANGIOGENIC; + /** + * Value used to determine how many steps a node should be delayed before being considered for + * growth after attempting growth. + */ + private static final int DELAY = 10; + /** Calculation strategies. */ public enum Calculation { /** Code for upstream calculation strategy. */ @@ -345,7 +351,6 @@ public void step(SimState simstate) { || targetNode.pressure == 0 || Double.isNaN(sproutNode.pressure) || Double.isNaN(targetNode.pressure)) { - LOGGER.info("Cannot connect nodes (332). Tick " + tick); continue; } @@ -385,7 +390,6 @@ public void step(SimState simstate) { || finalNode.pressure == 0 || Double.isNaN(sproutNode.pressure) || Double.isNaN(finalNode.pressure)) { - LOGGER.info("Cannot connect nodes (366). Tick " + tick); continue; } @@ -518,7 +522,8 @@ private boolean checkForIgnoredEdges(SiteNode node) { } /** - * Criteria for skipping a node during the migration checks. + * Criteria for skipping a node during the migration checks. If a node is in the node delay map, + * it will increment the value and return true. * * @param node the node to check * @return {@code true} if the node should be skipped, {@code false} otherwise @@ -527,9 +532,19 @@ private boolean checkNodeSkipStatus(SiteNode node) { if (angiogenicNodeMap.keySet().contains(node)) { return true; } + // if the node has been added in the last 72 hours, it is not considered for growth. if ((tick - node.addTime) < (72 * 60)) { return true; } + // If a node has been previously marked as angiogenic, it is delayed for DELAY steps. + if (nodeDelays.containsKey(node)) { + nodeDelays.put(node, nodeDelays.get(node) + 1); + if (nodeDelays.get(node) > DELAY) { + nodeDelays.remove(node); + return false; + } + return true; + } return false; } @@ -821,12 +836,6 @@ private void addAngiogenicEdges( // update edges in the minimal path between start and end ArrayList angioPath = getPath(tempGraph, start, end); - if (angioPath == null || angioPath.isEmpty()) { - LOGGER.info("ANGIOPATH IS NULL"); - LOGGER.info("START: " + start.toString()); - LOGGER.info("END: " + end.toString()); - LOGGER.info("EDGELIST: " + list); - } for (SiteEdge edge : angioPath) { if (graph.contains(edge)) { @@ -1032,7 +1041,6 @@ private Outcome recalculateRadii( double deltaP = start.pressure - end.pressure; if (start.pressure * end.pressure == 0 || Double.isNaN(deltaP)) { - LOGGER.info("FAILED TO ADD EDGES: 0 or NaN pressure"); return Outcome.FAILURE; } @@ -1091,7 +1099,6 @@ private Outcome recalculateRadii( ignoredEdges); } } - LOGGER.info("FAILED TO ADD EDGES: Intersection Null"); return Outcome.FAILURE; } return Outcome.SUCCESS; From 9632e82dc13d1dd869ef65572e67f22aca13c4eb Mon Sep 17 00:00:00 2001 From: cainja Date: Sun, 3 Aug 2025 15:03:14 -0700 Subject: [PATCH 55/56] removed unnecessary node list --- .../env/component/PatchComponentGrowth.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index bc78252cb..74c284704 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -163,9 +163,6 @@ private enum Outcome { /** Map of nodes to a delay to prevent nodes from constantly trying to add the same edge. */ private HashMap nodeDelays = new HashMap<>(); - /** List of noddes to track which nodes have been added in each timestep. */ - private ArrayList addedNodes = new ArrayList<>(); - /** * Creates a growth component for a {@link PatchComponentSitesGraph}. * @@ -333,11 +330,12 @@ public void step(SimState simstate) { if (targetNode == null) { sproutNode.anastomosis = false; + keyNodesToRemove.add(sproutNode); continue; - } else if (addedNodes.contains(targetNode)) { + } + if (keyNodesToRemove.contains(targetNode)) { sproutNode.anastomosis = false; keyNodesToRemove.add(sproutNode); - keyNodesToRemove.add(targetNode); continue; } keyNodesToRemove.add(sproutNode); @@ -422,7 +420,6 @@ public void step(SimState simstate) { } } keyNodesToRemove.clear(); - addedNodes.clear(); } private SiteNode validateNodeObject(SiteNode node) { @@ -859,8 +856,6 @@ private void addAngiogenicEdges( } addEdgeList(angioPath); - addedNodes.add(start); - addNodesInEdgeList(angioPath); switch (calc) { case COMPENSATE: @@ -880,15 +875,15 @@ private void addAngiogenicEdges( } } - private void addNodesInEdgeList(ArrayList edgeList) { - if (edgeList == null || edgeList.isEmpty()) { - return; - } - for (SiteEdge edge : edgeList) { - SiteNode to = edge.getTo(); - addedNodes.add(to); - } - } + // private void addNodesInEdgeList(ArrayList edgeList) { + // if (edgeList == null || edgeList.isEmpty()) { + // return; + // } + // for (SiteEdge edge : edgeList) { + // SiteNode to = edge.getTo(); + // addedNodes.add(to); + // } + // } /** * Private helper function for calculating the new radius of two edges after splitting flow From 06db7cb65f0a1331c794e19b6bb26f293870ba0a Mon Sep 17 00:00:00 2001 From: Krista Phommatha Date: Sun, 24 Aug 2025 19:55:17 -0700 Subject: [PATCH 56/56] changing path.isEmpty() logic --- .../patch/env/component/PatchComponentGrowth.java | 6 +++--- .../component/PatchComponentSitesGraphUtilities.java | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/arcade/patch/env/component/PatchComponentGrowth.java b/src/arcade/patch/env/component/PatchComponentGrowth.java index 74c284704..2e3507d07 100644 --- a/src/arcade/patch/env/component/PatchComponentGrowth.java +++ b/src/arcade/patch/env/component/PatchComponentGrowth.java @@ -942,7 +942,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, ArrayList> pathsArteries = new ArrayList<>(); for (Root artery : arteries) { ArrayList path = getPath(graph, artery.node, start); - if (path.isEmpty()) { + if (path == null) { continue; } pathsArteries.add(path); @@ -979,7 +979,7 @@ private void updateRootsAndRadii(ArrayList addedEdges, SiteNode start, ArrayList> pathsVeins = new ArrayList<>(); for (Root vein : veins) { ArrayList path = getPath(graph, end, vein.node); - if (path.isEmpty()) { + if (path == null) { continue; } pathsVeins.add(path); @@ -1317,7 +1317,7 @@ private Outcome updateRadiusToRoot( ArrayList oldRadii = new ArrayList<>(); for (Root vein : veins) { ArrayList path = getPath(graph, edge.getTo(), vein.node); - if (path.isEmpty()) { + if (path == null) { continue; } path.add(0, edge); diff --git a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java index 0c0d30cb4..134be2a31 100644 --- a/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java +++ b/src/arcade/patch/env/component/PatchComponentSitesGraphUtilities.java @@ -1018,16 +1018,16 @@ static ArrayList getPath(Graph graph, SiteNode start, SiteNode end) { } } - if (node.prev == null) { - LOGGER.info("START: " + start + " END: " + end); - LOGGER.info("PREV IS NULL" + node); - } + // if (node.prev == null) { + // LOGGER.info("START: " + start + " END: " + end); + // LOGGER.info("PREV IS NULL" + node); + // } node = node.prev; } if (node == null) { - LOGGER.info("Path in getPath is: " + path); + // LOGGER.info("Path in getPath is: " + path); return null; }