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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/src/acidwatch_api/models/arcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ArcsAdapter(BaseAdapter):
model_id = "arcs"
display_name = "ARCS"
description = DESCRIPTION
category = "Reactive"
category = "ChemicalEquilibrium"

valid_substances = [
"CH2O2",
Expand Down
2 changes: 1 addition & 1 deletion backend/src/acidwatch_api/models/arcs_exp.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ArcsExpAdapter(BaseAdapter):
model_id = "arcs_exp"
display_name = "ARCS experimental"
description = DESCRIPTION
category = "Reactive"
category = "ChemicalEquilibrium"

valid_substances = [
"CH2O2",
Expand Down
4 changes: 2 additions & 2 deletions backend/src/acidwatch_api/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,9 @@ def __init_subclass__(cls) -> None:
]

category: Annotated[
Literal["Reactive", "PhaseTransition"],
Literal["ChemicalEquilibrium", "PhaseEquilibrium"],
Doc(
"Category of the model: Reactive (chemical reactions) or PhaseTransition (phase changes)"
"Category of the model: ChemicalEquilibrium (chemical reactions) or PhaseEquilibrium (phase changes)"
),
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class GibbsMinimizationModelAdapter(BaseAdapter):
display_name = "Gibbs Minimization Model"
parameters: GibbsMinimizationModelParameters
description = DESCRIPTION
category = "Reactive"
category = "ChemicalEquilibrium"

async def run(self) -> RunResult:
eos = self.parameters.equation_of_state
Expand Down
2 changes: 1 addition & 1 deletion backend/src/acidwatch_api/models/phpitz_reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class PhpitzReactiveAdapter(BaseAdapter):
]
description = "Computational model developed by Baard Kaasa as part of our CCS research on CO2 Impurities."

category = "Reactive"
category = "ChemicalEquilibrium"
base_url = SETTINGS.phpitz_api_base_uri

async def run(self) -> RunResult:
Expand Down
4 changes: 2 additions & 2 deletions backend/src/acidwatch_api/models/phpitz_solubility.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class PhpitzSolubilityAdapter(BaseAdapter):
]
description = "Computational model developed by Baard Kaasa as part of our CCS research on CO2 Impurities. Solubility part."

category = "PhaseTransition"
category = "PhaseEquilibrium"
base_url = SETTINGS.phpitz_api_base_uri

async def run(self) -> RunResult:
Expand All @@ -59,7 +59,7 @@ async def run(self) -> RunResult:

phases = self._extract_phases(data)

return phases, TextResult(data=data["raw"], label="pHPitz Solubility Output")
return phases, TextResult(data=data["raw"], label="Text")

def _extract_phases(self, data: dict[str, Any]) -> list[Phase]:
solubility = data.get("solubility") or {}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/acidwatch_api/models/solubilityccs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class SolubilityCCSAdapter(BaseAdapter):
description = DESCRIPTION
valid_substances = ["H2O", "H2SO4", "HNO3"]
parameters: SolubilityCCSParameters
category = "PhaseTransition"
category = "PhaseEquilibrium"

async def run(self) -> RunResult:
# Get concentrations (mole fractions)
Expand Down
2 changes: 1 addition & 1 deletion backend/src/acidwatch_api/models/tocomo.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class TocomoAdapter(BaseAdapter):
model_id = "tocomo"
display_name = "ToCoMo"
description = DESCRIPTION
category = "Reactive"
category = "ChemicalEquilibrium"

valid_substances = [
"O2",
Expand Down
8 changes: 4 additions & 4 deletions backend/tests/test_grid_simulations_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class HalvingAdapter(base.BaseAdapter):
model_id = "halving"
display_name = "Halving Model"
description = ""
category = "Reactive"
category = "ChemicalEquilibrium"
valid_substances = ["H2O"]

async def run(self):
Expand All @@ -54,7 +54,7 @@ class QuadruplingAdapter(base.BaseAdapter):
model_id = "quadrupling"
display_name = "Quadrupling Model"
description = ""
category = "PhaseTransition"
category = "PhaseEquilibrium"
valid_substances = ["H2O"]

async def run(self):
Expand All @@ -73,7 +73,7 @@ class FailingAdapter(base.BaseAdapter):
model_id = "failing"
display_name = "Failing Model"
description = ""
category = "Reactive"
category = "ChemicalEquilibrium"
valid_substances = ["H2O"]

async def run(self):
Expand All @@ -84,7 +84,7 @@ class TwoSubstanceAdapter(base.BaseAdapter):
model_id = "two-substance"
display_name = "Two Substance Model"
description = ""
category = "Reactive"
category = "ChemicalEquilibrium"
valid_substances = ["H2O", "NaCl"]

async def run(self):
Expand Down
8 changes: 4 additions & 4 deletions backend/tests/test_models_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class DummyAdapter(base.BaseAdapter):
model_id = "dummy"
display_name = "Dummy Model"
description = ""
category = "Reactive"
category = "ChemicalEquilibrium"
valid_substances = ["H2O"]

async def run(self):
Expand All @@ -69,7 +69,7 @@ class SecondDummyAdapter(base.BaseAdapter):
model_id = "dummy_2"
display_name = "Dummy Model"
description = ""
category = "PhaseTransition"
category = "PhaseEquilibrium"
valid_substances = ["H2O"]

async def run(self):
Expand All @@ -88,7 +88,7 @@ class FailingDummyAdapter(base.BaseAdapter):
model_id = "failing_dummy"
display_name = "Failing Dummy Model"
description = ""
category = "Reactive"
category = "ChemicalEquilibrium"
valid_substances = ["H2O"]

async def run(self):
Expand Down Expand Up @@ -209,7 +209,7 @@ async def run(self):
"accessError": None,
"displayName": "Dummy Model",
"description": "",
"category": "Reactive",
"category": "ChemicalEquilibrium",
"modelId": "dummy",
"parameters": {
# Notice that it's "someField" and not "some_field"
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Simulation/ModelSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ const ModelSelect: React.FC<{
return (
<>
<Typography variant="h6" style={{ marginBottom: "8px" }}>
Reactive Models
Chemical Equilibrium
</Typography>
<div style={{ marginBottom: "20px" }}>
<div style={{ display: "flex", gap: "16px", flexWrap: "wrap" }}>
{models
.filter((model) => model.category === "Reactive")
.filter((model) => model.category === "ChemicalEquilibrium")
.map((model, index) => {
const isActive = selectedModels.some((m) => m.modelId === model.modelId);
return (
Expand All @@ -95,11 +95,11 @@ const ModelSelect: React.FC<{
})}
</div>
<Typography variant="h6" style={{ margin: "24px 0 8px 0" }}>
Phase Transition Models
Phase Equilibrium
</Typography>
<div style={{ display: "flex", gap: "16px", flexWrap: "wrap" }}>
{models
.filter((model) => model.category === "PhaseTransition")
.filter((model) => model.category === "PhaseEquilibrium")
.map((model, index) => {
const isActive = selectedModels.some((m) => m.modelId === model.modelId);
return (
Expand Down
102 changes: 72 additions & 30 deletions frontend/src/components/Simulation/Results.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { Tabs, Typography } from "@equinor/eds-core-react";
import { Accordion, Tabs, Typography } from "@equinor/eds-core-react";
import { useState } from "react";
import { Panel, SimulationResults } from "@/dto/SimulationResults";
import PhaseResultTable from "@/components/Simulation/PhaseResultTable";
Expand All @@ -8,6 +8,7 @@ import { MassBalanceError } from "@/components/Simulation/MassBalanceError";
import { extractPlotData } from "@/functions/Formatting";
import BarChart from "@/components/BarChart";
import GenericTable from "@/components/GenericTable";
import { useAvailableModels } from "@/contexts/ModelContext";

interface ResultsProps {
simulationResults?: SimulationResults;
Expand Down Expand Up @@ -53,21 +54,21 @@ function getPanelContent(panel: Panel): React.ReactElement {
}
}

const Results: React.FC<ResultsProps> = ({ simulationResults }) => {
const [activeTab, setActiveTab] = useState<string | number>(0);

const handleChange = (index: string | number) => {
setActiveTab(index);
};
interface ModelResultTabsProps {
simulationResults: SimulationResults;
modelIndices: number[];
}

if (!simulationResults) return <Typography color="red">No simulation results found</Typography>;
const ModelResultTabs: React.FC<ModelResultTabsProps> = ({ simulationResults, modelIndices }) => {
const [activeTab, setActiveTab] = useState<string | number>(0);

const panelTabs: string[] = [];
const panelContents: React.ReactElement[] = [];

simulationResults.results.forEach((result, modelIndex) => {
for (const modelIndex of modelIndices) {
const result = simulationResults.results[modelIndex];
const modelId = simulationResults.input.models[modelIndex]?.modelId || `Model ${modelIndex + 1}`;
const modelPrefix = simulationResults.results.length > 1 ? `${modelId}: ` : "";
const modelPrefix = modelIndices.length > 1 ? `${modelId}: ` : "";
const initialConcentrations = simulationResults.input.concentrations;

const phasesWithConcentrations = result.phases.filter((phase) => Object.keys(phase.concentrations).length > 0);
Expand Down Expand Up @@ -100,29 +101,70 @@ const Results: React.FC<ResultsProps> = ({ simulationResults }) => {
<Tabs.Panel key={`panel-${modelIndex}-${panelTabs.length}`}>{getPanelContent(panel)}</Tabs.Panel>
);
}
}

if (panelTabs.length === 0) return null;

return (
<Tabs activeTab={activeTab} onChange={(index) => setActiveTab(index)}>
<Tabs.List>
{panelTabs.map((label, index) => (
<Tabs.Tab key={index}>{label}</Tabs.Tab>
))}
</Tabs.List>
<Tabs.Panels>
{panelContents.map((content, index) => (
<Tabs.Panel key={index}>{content}</Tabs.Panel>
))}
</Tabs.Panels>
</Tabs>
);
};

const Results: React.FC<ResultsProps> = ({ simulationResults }) => {
const { models } = useAvailableModels();

if (!simulationResults) return <Typography color="red">No simulation results found</Typography>;

const sections: { category: string; modelNames: string[]; indices: number[] }[] = [];

simulationResults.input.models.forEach((inputModel, index) => {
const modelConfig = models.find((m) => m.modelId === inputModel.modelId);
const category = modelConfig?.category ?? "Results";
const displayName = modelConfig?.displayName ?? inputModel.modelId;
const existing = sections.find((s) => s.category === category);
if (existing) {
existing.indices.push(index);
existing.modelNames.push(displayName);
} else {
sections.push({ category, modelNames: [displayName], indices: [index] });
}
});

return (
<>
<Tabs activeTab={activeTab} onChange={handleChange}>
<Tabs.List>
{panelTabs.map((label, index) => (
<Tabs.Tab key={index}>{label}</Tabs.Tab>
))}
<Tabs.Tab>Raw JSON</Tabs.Tab>
</Tabs.List>
<Tabs.Panels>
{panelContents.map((content, index) => (
<Tabs.Panel key={index}>{content}</Tabs.Panel>
))}
<Tabs.Panel>
<div style={{ width: "500px" }}>
<pre>{JSON.stringify(simulationResults, null, 2)}</pre>
</div>
</Tabs.Panel>
</Tabs.Panels>
</Tabs>
</>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
{sections.map((section, i) => (
<Accordion key={section.category}>
<Accordion.Item isExpanded={i === sections.length - 1}>
<Accordion.Header>{`${section.category}: ${section.modelNames.join(", ")}`}</Accordion.Header>
<Accordion.Panel>
<ModelResultTabs simulationResults={simulationResults} modelIndices={section.indices} />
</Accordion.Panel>
</Accordion.Item>
</Accordion>
))}

<Accordion>
<Accordion.Item isExpanded={false}>
<Accordion.Header>Raw JSON</Accordion.Header>
<Accordion.Panel>
<pre style={{ maxWidth: "500px", overflow: "auto" }}>
{JSON.stringify(simulationResults, null, 2)}
</pre>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
</div>
);
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/dto/FormConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export const ModelConfig = z.object({
validSubstances: z.array(z.string()),
parameters: z.record(z.string(), ParameterConfig),
description: z.string(),
category: z.enum(["Reactive", "PhaseTransition"]),
category: z.enum(["ChemicalEquilibrium", "PhaseEquilibrium"]),
});
export type ModelConfig = z.infer<typeof ModelConfig>;
2 changes: 1 addition & 1 deletion frontend/src/functions/Filtering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const filterValidModels = (experiment: ExperimentResult, models: ModelCon
Object.entries(experiment.initialConcentrations).filter(([, value]) => Number(value) !== 0)
);
return models
.filter((model) => model.category === "Reactive")
.filter((model) => model.category === "ChemicalEquilibrium")
.filter((model) => Object.entries(filteredConcs).every(([key]) => model.validSubstances.includes(key)));
};

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/LabResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { SimulationResults } from "@/dto/SimulationResults";
import { MainContainer } from "@/components/styles";

const defaultModels = (models: ModelConfig[]) =>
new Set(models.filter((m) => m.category === "Reactive").map((m) => m.modelId));
new Set(models.filter((m) => m.category === "ChemicalEquilibrium").map((m) => m.modelId));

const LabResults: React.FC = () => {
const [selectedExperiments, setSelectedExperiments] = useState<ExperimentResult[]>([]);
Expand Down Expand Up @@ -133,7 +133,7 @@ const LabResults: React.FC = () => {
<Card.Content>
<div style={{ display: "flex", flexWrap: "wrap", gap: "1rem" }}>
{models
.filter((model) => model.category === "Reactive")
.filter((model) => model.category === "ChemicalEquilibrium")
.map((model) => (
<Checkbox
key={model.modelId}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/utils/modelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { ModelConfig } from "@/dto/FormConfig";

export function sortModelsByCategory(models: ModelConfig[]): ModelConfig[] {
return [...models].sort((a, b) => {
if (a.category === "Reactive" && b.category === "PhaseTransition") return -1;
if (a.category === "PhaseTransition" && b.category === "Reactive") return 1;
if (a.category === "ChemicalEquilibrium" && b.category === "PhaseEquilibrium") return -1;
if (a.category === "PhaseEquilibrium" && b.category === "ChemicalEquilibrium") return 1;
return 0;
});
}
Loading
Loading