Skip to content

Commit 27db49f

Browse files
Handle multiple phases in mass balance
1 parent 6a8a606 commit 27db49f

3 files changed

Lines changed: 40 additions & 14 deletions

File tree

frontend/src/components/Simulation/MassBalanceError.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,16 @@ export function getMasses(concs: Record<string, number>): Record<string, number>
100100
}
101101

102102
export function getMassBalanceError(
103-
init: Record<string, number>,
104-
final: Record<string, number>
103+
initPhases: Phase[],
104+
finalPhases: Phase[]
105105
): {
106106
error: number;
107107
initMasses: Record<string, number>;
108108
finalMasses: Record<string, number>;
109109
substances: string[];
110110
} {
111-
const initMasses = getMasses(init);
112-
const finalMasses = getMasses(final);
111+
const initMasses = getMasses(mergePhasesConcentrations(initPhases));
112+
const finalMasses = getMasses(mergePhasesConcentrations(finalPhases));
113113
const substances = Array.from(new Set([...Object.keys(initMasses), ...Object.keys(finalMasses)]));
114114

115115
let error = 0;
@@ -139,14 +139,13 @@ export function mergePhasesConcentrations(phases: Phase[]): Record<string, numbe
139139
}
140140

141141
interface MassBalanceErrorProps {
142-
initial: Record<string, number>;
143-
phases: Phase[];
142+
initialPhases: Phase[];
143+
finalPhases: Phase[];
144144
}
145145

146-
export function MassBalanceError({ initial, phases }: MassBalanceErrorProps) {
146+
export function MassBalanceError({ initialPhases, finalPhases }: MassBalanceErrorProps) {
147147
const [isExpanded, setExpanded] = useState<boolean>(false);
148-
const final = mergePhasesConcentrations(phases);
149-
const { error, initMasses, finalMasses, substances } = getMassBalanceError(initial, final);
148+
const { error, initMasses, finalMasses, substances } = getMassBalanceError(initialPhases, finalPhases);
150149
const significantError = error >= 1;
151150

152151
useEffect(() => {

frontend/src/components/Simulation/Results.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ const Results: React.FC<ResultsProps> = ({ simulationResults }) => {
7979

8080
panelContents.push(
8181
<Tabs.Panel key={`phase-${modelIndex}-${phase.kind}`}>
82-
<MassBalanceError initial={initialConcentrations} phases={result.phases} />
82+
<MassBalanceError
83+
initialPhases={[{ kind: "aqueous", fraction: 1, concentrations: initialConcentrations }]}
84+
finalPhases={result.phases}
85+
/>
8386

8487
<BarChart
8588
aspectRatio={2}

frontend/tests/components/MassBalanceError.test.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,42 @@ describe("MassBalanceError", () => {
5656
N2: 3,
5757
};
5858

59+
const singlePhase = (concentrations: Record<string, number>) => [
60+
{ kind: "co2-rich" as const, fraction: 1, concentrations },
61+
];
62+
5963
it("error is 0 when the numbers are the same", () => {
60-
expect(getMassBalanceError(concs, concs).error).toBe(0);
64+
expect(getMassBalanceError(singlePhase(concs), singlePhase(concs)).error).toBe(0);
6165
});
6266

6367
it("error is small when difference is small", () => {
6468
const final = Object.fromEntries(Object.entries(concs).map(([subst, amount]) => [subst, amount + 1e-3]));
6569

66-
expect(getMassBalanceError(concs, final).error).toBeLessThan(0.01);
70+
expect(getMassBalanceError(singlePhase(concs), singlePhase(final)).error).toBeLessThan(0.01);
6771
});
6872

6973
it("error is large when order of magnitude difference is large", () => {
70-
expect(getMassBalanceError(concs, { ...concs, NO2: concs.NO2 * 2 }).error).toBeGreaterThan(100.0);
71-
expect(getMassBalanceError(concs, { ...concs, NO2: concs.NO2 / 2 }).error).toBeGreaterThan(100.0);
74+
expect(getMassBalanceError(singlePhase(concs), singlePhase({ ...concs, NO2: concs.NO2 * 2 })).error).toBeGreaterThan(100.0);
75+
expect(getMassBalanceError(singlePhase(concs), singlePhase({ ...concs, NO2: concs.NO2 / 2 })).error).toBeGreaterThan(100.0);
76+
});
77+
78+
it("error is 0 when mass is conserved across multiple phases", () => {
79+
const phases = [
80+
{ kind: "co2-rich" as const, fraction: 0.6, concentrations: { H2O: 1, NO2: 2, N2: 3 } },
81+
{ kind: "aqueous" as const, fraction: 0.4, concentrations: { H2O: 1, NO2: 2, N2: 3 } },
82+
];
83+
expect(getMassBalanceError(singlePhase(concs), phases).error).toBeCloseTo(0, 5);
84+
});
85+
86+
it("error is 0 when both init and final have multiple phases", () => {
87+
const initPhases = [
88+
{ kind: "co2-rich" as const, fraction: 0.8, concentrations: { H2O: 1, NO2: 2 } },
89+
{ kind: "aqueous" as const, fraction: 0.2, concentrations: { H2O: 1, NO2: 2 } },
90+
];
91+
const finalPhases = [
92+
{ kind: "co2-rich" as const, fraction: 0.5, concentrations: { H2O: 1, NO2: 2 } },
93+
{ kind: "aqueous" as const, fraction: 0.5, concentrations: { H2O: 1, NO2: 2 } },
94+
];
95+
expect(getMassBalanceError(initPhases, finalPhases).error).toBeCloseTo(0, 5);
7296
});
7397
});

0 commit comments

Comments
 (0)