Skip to content

Commit d134ac0

Browse files
committed
ensure correct behavior between system response and int/ext
1 parent 37371c9 commit d134ac0

3 files changed

Lines changed: 314 additions & 26 deletions

File tree

HEC.FDA.Model/compute/ImpactAreaScenarioSimulation.cs

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818

1919
namespace HEC.FDA.Model.compute
2020
{
21+
/// <summary>
22+
/// Holds both the channel (exterior) and floodplain (interior) frequency-stage curves.
23+
/// When no interior-exterior relationship exists, both fields reference the same curve.
24+
/// </summary>
25+
internal readonly record struct FrequencyStageCurves(
26+
PairedData ChannelStage,
27+
PairedData FloodplainStage
28+
);
29+
2130
[StoredProperty("ImpactAreaScenarioSimulation")]
2231
public class ImpactAreaScenarioSimulation : ValidationErrorLogger, IProgressReport
2332
{
@@ -138,8 +147,8 @@ private void SetupPerformanceThresholds(ConvergenceCriteria convergenceCriteria)
138147
//User has no levee. if the user set the default threshold themself, just use that. Otherwise calculate it...
139148
else if (!defaultThresholdExists) // ONLY IF WE HAVE FIALURE DAMAGES. Validated in the VM.
140149
{
141-
PairedData frequency_stage_sample = GetFrequencyStageSample(computeIsDeterministic: true, 1);
142-
ComputeConsequencesFromStageFrequency(frequency_stage_sample, 1, 1, computeIsDeterministic: true, _FailureStageDamageFunctions, ConsequenceType.Damage, false, true);
150+
FrequencyStageCurves curves = GetFrequencyStageSample(computeIsDeterministic: true, 1);
151+
ComputeConsequencesFromStageFrequency(curves.FloodplainStage, 1, 1, computeIsDeterministic: true, _FailureStageDamageFunctions, ConsequenceType.Damage, false, true);
143152
Threshold defaultThreshold = ComputeDefaultThreshold(convergenceCriteria, damageFrequencyFunctions: _ImpactAreaScenarioResults.ConsequenceFrequencyFunctions.Select((c) => c.FrequencyCurve).ToList());
144153
_ImpactAreaScenarioResults.PerformanceByThresholds.AddThreshold(defaultThreshold);
145154
}
@@ -337,9 +346,9 @@ private void ComputeIterations(ConvergenceCriteria convergenceCriteria, bool com
337346
{
338347
long computeIteration = iterationsStart + chunkIteration;
339348

340-
PairedData frequency_stage_sample = GetFrequencyStageSample(computeIsDeterministic, computeIteration);
341-
PairedData systemResponse_sample = ComputeConsequencesFromStageFrequency(frequency_stage_sample, computeIteration, chunkIteration, computeIsDeterministic);//null return if no levee or no ll/damage to compute.
342-
ComputePerformanceFromStageFrequency(frequency_stage_sample, systemResponse_sample, chunkIteration);// null checks the system response.
349+
FrequencyStageCurves curves = GetFrequencyStageSample(computeIsDeterministic, computeIteration);
350+
PairedData systemResponse_sample = ComputeConsequencesFromStageFrequency(curves, computeIteration, chunkIteration, computeIsDeterministic);//null return if no levee or no ll/damage to compute.
351+
ComputePerformanceFromStageFrequency(curves, systemResponse_sample, chunkIteration);// null checks the system response.
343352
});
344353
_ImpactAreaScenarioResults.ConsequenceResults.PutDataIntoHistograms();
345354
_ImpactAreaScenarioResults.PutUncertainFrequencyCurvesIntoHistograms();
@@ -389,7 +398,7 @@ private void ComputeIterations(ConvergenceCriteria convergenceCriteria, bool com
389398
ReportProgress(this, new ProgressReportEventArgs(IMPACT_AREA_SIM_COMPLETED));
390399
}
391400

392-
private PairedData GetFrequencyStageSample(bool computeIsDeterministic, long thisComputeIteration)
401+
private FrequencyStageCurves GetFrequencyStageSample(bool computeIsDeterministic, long thisComputeIteration)
393402
{
394403
PairedData frequency_stage_sample;
395404
if (_FrequencyStage.CurveMetaData.IsNull)
@@ -412,11 +421,11 @@ private PairedData GetFrequencyStageSample(bool computeIsDeterministic, long thi
412421
}
413422
if (!_ChannelStageFloodplainStage.IsNull)
414423
{
415-
PairedData _channelstage_floodplainstage_sample = _ChannelStageFloodplainStage.SamplePairedData(thisComputeIteration, computeIsDeterministic);
416-
PairedData frequency_floodplainstage = _channelstage_floodplainstage_sample.compose(frequency_stage_sample);
417-
return frequency_floodplainstage;
424+
PairedData channelstage_floodplainstage_sample = _ChannelStageFloodplainStage.SamplePairedData(thisComputeIteration, computeIsDeterministic);
425+
PairedData frequency_floodplainstage = channelstage_floodplainstage_sample.compose(frequency_stage_sample);
426+
return new FrequencyStageCurves(ChannelStage: frequency_stage_sample, FloodplainStage: frequency_floodplainstage);
418427
}
419-
return frequency_stage_sample;
428+
return new FrequencyStageCurves(ChannelStage: frequency_stage_sample, FloodplainStage: frequency_stage_sample);
420429
}
421430

422431
private PairedData GetStageFreq(bool computeIsDeterministic, long thisComputeIteration, PairedData frequencyDischarge)
@@ -445,17 +454,17 @@ private PairedData GetStageFreq(bool computeIsDeterministic, long thisComputeIte
445454
/// <param name="thisChunkIteration"> used for saving a result in the correct place of a temp results array </param>
446455
/// <param name="computeIsDeterministic"> Monte-Carlo smapling based on iteration if false. Central tendency if true.</param>
447456
/// <returns>The sampled system response function if a levee exists, null otherwise</returns>
448-
private PairedData ComputeConsequencesFromStageFrequency(PairedData frequency_stage, long thisComputeIteration, long thisChunkIteration, bool computeIsDeterministic)
457+
private PairedData ComputeConsequencesFromStageFrequency(FrequencyStageCurves curves, long thisComputeIteration, long thisChunkIteration, bool computeIsDeterministic)
449458
{
450459
if (_SystemResponseFunction.CurveMetaData.IsNull)
451460
{
452461
if (_HasFailureStageDamage)
453462
{
454-
ComputeConsequencesFromStageFrequency(frequency_stage, thisComputeIteration, thisChunkIteration, computeIsDeterministic, _FailureStageDamageFunctions, ConsequenceType.Damage);
463+
ComputeConsequencesFromStageFrequency(curves.FloodplainStage, thisComputeIteration, thisChunkIteration, computeIsDeterministic, _FailureStageDamageFunctions, ConsequenceType.Damage);
455464
}
456465
if (_HasFailureStageLifeLoss)
457466
{
458-
ComputeConsequencesFromStageFrequency(frequency_stage, thisComputeIteration, thisChunkIteration, computeIsDeterministic, _FailureStageLifeLossFunctions, ConsequenceType.LifeLoss);
467+
ComputeConsequencesFromStageFrequency(curves.FloodplainStage, thisComputeIteration, thisChunkIteration, computeIsDeterministic, _FailureStageLifeLossFunctions, ConsequenceType.LifeLoss);
459468
}
460469
return null;
461470
}
@@ -464,11 +473,11 @@ private PairedData ComputeConsequencesFromStageFrequency(PairedData frequency_st
464473
PairedData systemResponse_sample = _SystemResponseFunction.SamplePairedData(thisComputeIteration, computeIsDeterministic);
465474
if (_HasFailureStageDamage)
466475
{
467-
ComputeDamagesFromStageFrequency_WithLevee(frequency_stage, systemResponse_sample, thisComputeIteration, thisChunkIteration, computeIsDeterministic, ConsequenceType.Damage);
476+
ComputeDamagesFromStageFrequency_WithLevee(curves, systemResponse_sample, thisComputeIteration, thisChunkIteration, computeIsDeterministic, ConsequenceType.Damage);
468477
}
469478
if (_HasFailureStageLifeLoss)
470479
{
471-
ComputeDamagesFromStageFrequency_WithLevee(frequency_stage, systemResponse_sample, thisComputeIteration, thisChunkIteration, computeIsDeterministic, ConsequenceType.LifeLoss);
480+
ComputeDamagesFromStageFrequency_WithLevee(curves, systemResponse_sample, thisComputeIteration, thisChunkIteration, computeIsDeterministic, ConsequenceType.LifeLoss);
472481
}
473482
return systemResponse_sample;
474483
}
@@ -480,15 +489,16 @@ private PairedData ComputeConsequencesFromStageFrequency(PairedData frequency_st
480489
/// <param name="frequency_stage">The stage-frequency relationship</param>
481490
/// <param name="systemResponse_sample">The sampled system response function if a levee exists, null otherwise</param>
482491
/// <param name="thisChunkIteration">Used for saving a result in the correct place of a temp results array</param>
483-
private void ComputePerformanceFromStageFrequency(PairedData frequency_stage, PairedData systemResponse_sample, int thisChunkIteration)
492+
private void ComputePerformanceFromStageFrequency(FrequencyStageCurves curves, PairedData systemResponse_sample, int thisChunkIteration)
484493
{
485494
if (systemResponse_sample == null || systemResponse_sample.Xvals.Count <= 2)
486495
{
487-
ComputePerformance(frequency_stage, thisChunkIteration);
496+
ComputePerformance(curves.FloodplainStage, thisChunkIteration);
488497
}
489498
else
490499
{
491-
ComputeLeveePerformance(frequency_stage, systemResponse_sample, thisChunkIteration);
500+
// System response (levee fragility) is defined in channel stage, so use ChannelStage
501+
ComputeLeveePerformance(curves.ChannelStage, systemResponse_sample, thisChunkIteration);
492502
}
493503
}
494504
/// <summary>
@@ -533,7 +543,7 @@ private void ComputeConsequencesFromStageFrequency(PairedData frequency_stage, l
533543
}
534544
}
535545

536-
private void ComputeDamagesFromStageFrequency_WithLevee(PairedData frequency_stage, PairedData systemResponse, long thisComputeIteration, long thisChunkIteration, bool computeIsDeterministic, ConsequenceType type)
546+
private void ComputeDamagesFromStageFrequency_WithLevee(FrequencyStageCurves curves, PairedData systemResponse, long thisComputeIteration, long thisChunkIteration, bool computeIsDeterministic, ConsequenceType type)
537547
{
538548
List<UncertainPairedData> failureStageDamageFunctions;
539549
List<UncertainPairedData> nonfailureStageDamageFunctions;
@@ -551,24 +561,37 @@ private void ComputeDamagesFromStageFrequency_WithLevee(PairedData frequency_sta
551561
}
552562

553563
PairedData validatedSystemResponse = EnsureBottomAndTopHaveCorrectProbabilities(systemResponse);
554-
ComputeAnnualizedConsequence(frequency_stage, thisComputeIteration, thisChunkIteration, computeIsDeterministic, type, failureStageDamageFunctions, validatedSystemResponse, RiskType.Fail);
564+
ComputeAnnualizedConsequence(curves, thisComputeIteration, thisChunkIteration, computeIsDeterministic, type, failureStageDamageFunctions, validatedSystemResponse, RiskType.Fail);
555565

556566
//if we have nonfail damage functions, compute those too.
557567
if (nonfailureStageDamageFunctions.Count > 0)
558568
{
559569
PairedData complementSystemResponse = CalculateFailureProbComplement(validatedSystemResponse);
560-
ComputeAnnualizedConsequence(frequency_stage, thisComputeIteration, thisChunkIteration, computeIsDeterministic, type, nonfailureStageDamageFunctions, complementSystemResponse, RiskType.Non_Fail);
570+
ComputeAnnualizedConsequence(curves, thisComputeIteration, thisChunkIteration, computeIsDeterministic, type, nonfailureStageDamageFunctions, complementSystemResponse, RiskType.Non_Fail);
561571
}
562572

563573
}
564574

565-
private void ComputeAnnualizedConsequence(PairedData frequency_stage, long thisComputeIteration, long thisChunkIteration, bool computeIsDeterministic, ConsequenceType type, List<UncertainPairedData> stageConsequenceFuncs, PairedData validatedSystemResponse, RiskType riskType)
575+
private void ComputeAnnualizedConsequence(FrequencyStageCurves curves, long thisComputeIteration, long thisChunkIteration, bool computeIsDeterministic, ConsequenceType type, List<UncertainPairedData> stageConsequenceFuncs, PairedData validatedSystemResponse, RiskType riskType)
566576
{
567577
foreach (UncertainPairedData stageUncertainCons in stageConsequenceFuncs)
568578
{
569579
PairedData stageDamageSample = stageUncertainCons.SamplePairedData(thisComputeIteration, computeIsDeterministic);
570-
stageDamageSample = stageDamageSample.multiply(validatedSystemResponse);
571-
PairedData damFreq = stageDamageSample.compose(frequency_stage);//save me for FN Plot
580+
PairedData damFreq;
581+
if (ReferenceEquals(curves.ChannelStage, curves.FloodplainStage))
582+
{
583+
// No interior-exterior: multiply in stage domain then compose (original behavior)
584+
stageDamageSample = stageDamageSample.multiply(validatedSystemResponse);
585+
damFreq = stageDamageSample.compose(curves.FloodplainStage);
586+
}
587+
else
588+
{
589+
// Interior-exterior exists: damage uses floodplain stage, system response uses channel stage.
590+
// Compose both into frequency domain before multiplying so each uses the correct stage domain.
591+
PairedData freqDamage = stageDamageSample.compose(curves.FloodplainStage);
592+
PairedData freqPFail = validatedSystemResponse.compose(curves.ChannelStage);
593+
damFreq = freqDamage.multiply(freqPFail);
594+
}
572595

573596
// Add curve to uncertain consequence frequency curve for histogram aggregation
574597
CategoriedUncertainPairedData uncertainCurve = _ImpactAreaScenarioResults.GetOrCreateUncertainConsequenceFrequencyCurve(
@@ -691,8 +714,8 @@ public ImpactAreaScenarioResults PreviewCompute()
691714
{
692715
_ConvergenceCriteria = new(1, 1);
693716
CreateEAConsequenceHistograms(new(1, 1), _FailureStageDamageFunctions, ConsequenceType.Damage, RiskType.Fail);
694-
PairedData frequency_stage_sample = GetFrequencyStageSample(computeIsDeterministic: true, 1);
695-
ComputeConsequencesFromStageFrequency(frequency_stage_sample, 0, 0, computeIsDeterministic: true, _FailureStageDamageFunctions, ConsequenceType.Damage, true, true);
717+
FrequencyStageCurves curves = GetFrequencyStageSample(computeIsDeterministic: true, 1);
718+
ComputeConsequencesFromStageFrequency(curves.FloodplainStage, 0, 0, computeIsDeterministic: true, _FailureStageDamageFunctions, ConsequenceType.Damage, true, true);
696719
_ImpactAreaScenarioResults.ConsequenceResults.PutDataIntoHistograms();
697720
return _ImpactAreaScenarioResults;
698721
}

HEC.FDA.ModelTest/unittests/AlternativeComparisonReportConsolidationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace HEC.FDA.ModelTest.unittests;
1111

12+
[Trait("RunsOn","Remote")]
1213
/// <summary>
1314
/// Tests that the consolidated EAD methods correctly return life loss (AALL) reduced results
1415
/// alongside damage reduced results, verifying that the separate AALL computation path is no longer needed.

0 commit comments

Comments
 (0)