public static IEnumerable <NereidResult> TotalNetworkSolve(out string stackTrace, out List <string> missingNodeIDs, out Graph graph, DatabaseEntities dbContext, HttpClient httpClient, bool isBaselineCondition) { stackTrace = ""; missingNodeIDs = new List <string>(); graph = NereidUtilities.BuildNetworkGraph(dbContext); var nereidResults = NetworkSolveImpl(missingNodeIDs, graph, dbContext, httpClient, false, isBaselineCondition); var baselineConditionSqlParam = new SqlParameter("@isBaselineCondition", isBaselineCondition); dbContext.Database.ExecuteSqlCommand( "EXEC dbo.pDeleteNereidResults @isBaselineCondition", baselineConditionSqlParam); dbContext.NereidResults.AddRange(nereidResults); // this is a relatively hefty set, so boost the timeout way beyond reasonable to make absolutely sure it doesn't die out on us. dbContext.Database.CommandTimeout = 600; dbContext.SaveChangesWithNoAuditing(); return(nereidResults); }
public static NereidResult <SolutionResponseObject> SolveSubgraph(Graph subgraph, List <vNereidLoadingInput> allLoadingInputs, List <TreatmentBMP> allModelingBMPs, List <WaterQualityManagementPlanNode> allWaterqualityManagementPlanNodes, List <QuickBMP> allModelingQuickBMPs, out List <string> notFoundNodes, HttpClient httpClient, bool isBaselineCondition) { notFoundNodes = new List <string>(); // Now I need to get the land_surface, treatment_facility, and treatment_site tables for this request. // these are going to look very much like the various calls made throughout the testing methods, but filtered // to the subgraph. Fortunately, we've added metadata to the nodes to help us do the filtration var delineationToIncludeIDs = subgraph.Nodes.Where(x => x.Delineation != null).Select(x => x.Delineation.DelineationID) .Distinct().ToList(); var regionalSubbasinToIncludeIDs = subgraph.Nodes.Where(x => x.RegionalSubbasinID != null) .Select(x => x.RegionalSubbasinID).Distinct().ToList(); var waterQualityManagementPlanToIncludeIDs = subgraph.Nodes.Where(x => x.WaterQualityManagementPlan != null) .Select(x => x.WaterQualityManagementPlan.WaterQualityManagementPlanID).Distinct().ToList(); var treatmentBMPToIncludeIDs = subgraph.Nodes.Where(x => x.TreatmentBMPID != null) .Select(x => x.TreatmentBMPID.Value).Distinct().ToList(); var landSurfaces = allLoadingInputs.Where(x => delineationToIncludeIDs.Contains(x.DelineationID.GetValueOrDefault()) || regionalSubbasinToIncludeIDs.Contains(x.RegionalSubbasinID) || waterQualityManagementPlanToIncludeIDs.Contains(x.WaterQualityManagementPlanID.GetValueOrDefault()) ).ToList().Select(x => new LandSurface(x, isBaselineCondition)).ToList(); var treatmentFacilities = allModelingBMPs .Where(x => treatmentBMPToIncludeIDs.Contains(x.TreatmentBMPID) && // Don't create TreatmentFacilities for BMPs belonging to a Simple WQMP x.WaterQualityManagementPlan?.WaterQualityManagementPlanModelingApproachID != WaterQualityManagementPlanModelingApproach.Simplified.WaterQualityManagementPlanModelingApproachID) .Select(x => x.ToTreatmentFacility(isBaselineCondition)).ToList(); var filteredQuickBMPs = allModelingQuickBMPs .Where(x => waterQualityManagementPlanToIncludeIDs.Contains(x.WaterQualityManagementPlanID) && // Don't create TreatmentSites for QuickBMPs belonging to a Detailed WQMP x.WaterQualityManagementPlan.WaterQualityManagementPlanModelingApproachID != WaterQualityManagementPlanModelingApproach.Detailed.WaterQualityManagementPlanModelingApproachID).ToList(); var filteredWQMPNodes = allWaterqualityManagementPlanNodes.Where(y => waterQualityManagementPlanToIncludeIDs.Contains(y.WaterQualityManagementPlanID) && regionalSubbasinToIncludeIDs.Contains(y.RegionalSubbasinID) // ignore parts that live in RSBs outside our solve area. ).ToList(); var treatmentSites = filteredQuickBMPs .Join( filteredWQMPNodes, x => x.WaterQualityManagementPlanID, x => x.WaterQualityManagementPlanID, (bmp, node) => new { bmp, node }) .Select(x => new TreatmentSite { NodeID = NereidUtilities.WaterQualityManagementPlanTreatmentNodeID(x.node.WaterQualityManagementPlanID, x.node.OCSurveyCatchmentID), AreaPercentage = x.bmp.PercentOfSiteTreated, CapturedPercentage = x.bmp.PercentCaptured ?? 0, RetainedPercentage = x.bmp.PercentRetained ?? 0, // treat wqmps built after 2003 as if they don't exist. FacilityType = (isBaselineCondition && x.node.DateOfConstruction.HasValue && x.node.DateOfConstruction.Value.Year > BASELINE_CUTOFF_YEAR) ? "NoTreatment" : x.bmp.TreatmentBMPType.TreatmentBMPModelingType.TreatmentBMPModelingTypeName, EliminateAllDryWeatherFlowOverride = x.bmp.DryWeatherFlowOverrideID == DryWeatherFlowOverride.Yes.DryWeatherFlowOverrideID }).ToList(); //ValidateForTesting(subgraph, landSurfaces, treatmentFacilities, treatmentSites); var solveUrl = $"{NeptuneWebConfiguration.NereidUrl}/api/v1/watershed/solve?state=ca®ion=soc"; // get the list of leaf nodes for this subgraph var targetNodeIDs = subgraph.Edges.Select(x => x.TargetID); // As all men know in this kingdom by the sea, a leaf of a digraph is a node that's not the target of an edge var leafNodes = subgraph.Nodes.Where(x => !targetNodeIDs.Contains(x.ID)); var solutionRequestObject = new SolutionRequestObject() { Graph = subgraph, LandSurfaces = landSurfaces, TreatmentFacilities = treatmentFacilities, TreatmentSites = treatmentSites, PreviousResults = leafNodes.Where(x => x.PreviousResults != null).Select(x => x.PreviousResults).ToList() }; NereidResult <SolutionResponseObject> results = null; try { results = RunJobAtNereid <SolutionRequestObject, SolutionResponseObject>( solutionRequestObject, solveUrl, out _, httpClient); } catch (Exception e) { throw new NereidException <SolutionRequestObject, SolutionResponseObject>(e.Message, e) { Request = solutionRequestObject, Response = results?.Data }; } if (results?.Data.Errors != null && results.Data.Errors.Count > 0 && (results.Data.Results == null || results.Data.Results.Count == 0)) { throw new NereidException <SolutionRequestObject, SolutionResponseObject> { Request = solutionRequestObject, Response = results.Data }; } // literally this can't be null... // ReSharper disable once PossibleNullReferenceException var previousResultsKeys = results.Data.PreviousResultsKeys; foreach (var dataLeafResult in results.Data.Results) { var node = subgraph.Nodes.SingleOrDefault(x => x.ID == dataLeafResult["node_id"].ToString()); if (node == null) { // this is an edge case that should only happen if an RSB in the SOC area has // its downstream catchment outside the SOC area for some reason. notFoundNodes.Add(dataLeafResult["node_id"].ToString()); } else { node.Results = dataLeafResult; // track the smaller subset of results that need to be sent for subsequent calls var previousResults = new JObject(); foreach (var key in previousResultsKeys) { var value = dataLeafResult[key]; previousResults.Add(key, value); } node.PreviousResults = previousResults; } } foreach (var dataLeafResult in results.Data.LeafResults) { try { var node = subgraph.Nodes.SingleOrDefault(x => x.ID == dataLeafResult["node_id"].ToString()); if (node == null) { notFoundNodes.Add(dataLeafResult["node_id"].ToString()); } else { // don't store the leaf results if already data at this node--most of the time these nodes are read-only if (node.Results == null) { node.Results = dataLeafResult; } } } catch (InvalidOperationException ioe) { throw new DuplicateNodeException(dataLeafResult["node_id"].ToString(), ioe); } } return(results); }
private static List <NereidResult> NetworkSolveImpl(List <string> missingNodeIDs, Graph graph, DatabaseEntities dbContext, HttpClient httpClient, bool sendPreviousResults, bool isBaselineCondition) { var solutionSequenceUrl = $"{NeptuneWebConfiguration.NereidUrl}/api/v1/network/solution_sequence?min_branch_size=12"; var allLoadingInputs = dbContext.vNereidLoadingInputs.ToList(); var allModelingBMPs = NereidUtilities.ModelingTreatmentBMPs(dbContext).ToList(); var allwaterQualityManagementPlanNodes = NereidUtilities.GetWaterQualityManagementPlanNodes(dbContext).ToList(); var allModelingQuickBMPs = dbContext.QuickBMPs.Include(x => x.TreatmentBMPType) .GetFullyParameterized(); var solutionSequenceResult = NereidUtilities.RunJobAtNereid <SolutionSequenceRequest, SolutionSequenceResult>( new SolutionSequenceRequest(graph), solutionSequenceUrl, out _, httpClient); // for the delta run, associate each node with its previous results if (sendPreviousResults) { var previousModelResults = dbContext.NereidResults.ToList(); foreach (var node in graph.Nodes) { var previousNodeResults = previousModelResults.SingleOrDefault(x => node.ID == x.NodeID && x.IsBaselineCondition == isBaselineCondition )?.FullResponse; if (previousNodeResults != null) { node.PreviousResults = JObject.Parse(previousNodeResults); node.PreviousResults["node_errors"] = ""; node.PreviousResults["node_warnings"] = ""; } } } foreach (var parallel in solutionSequenceResult.Data.SolutionSequence.Parallel) { foreach (var series in parallel.Series) { var seriesNodes = series.Nodes; var subgraph = MakeSubgraphFromParentGraphAndNodes(graph, seriesNodes); SolveSubgraph(subgraph, allLoadingInputs, allModelingBMPs, allwaterQualityManagementPlanNodes, allModelingQuickBMPs, out var notFoundNodes, httpClient, isBaselineCondition); missingNodeIDs.AddRange(notFoundNodes); } } var nereidResults = graph.Nodes.Where(x => x.Results != null).Select(x => new NereidResult(x.Results.ToString(), isBaselineCondition) { TreatmentBMPID = x.TreatmentBMPID, DelineationID = x.Delineation?.DelineationID, NodeID = x.ID, RegionalSubbasinID = x.RegionalSubbasinID, WaterQualityManagementPlanID = x.WaterQualityManagementPlan?.WaterQualityManagementPlanID, LastUpdate = DateTime.Now }).ToList(); return(nereidResults); }