Skip to content
35 changes: 23 additions & 12 deletions apps/class-solid/src/components/Analysis.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Config } from "@classmodel/class/config";
import { type Parcel, calculatePlume } from "@classmodel/class/fire";
import { calculatePlume, transposePlumeData } from "@classmodel/class/fire";
import {
type ClassOutput,
type OutputVariableKey,
Expand Down Expand Up @@ -45,7 +45,7 @@ import { MdiCamera, MdiDelete, MdiImageFilterCenterFocus } from "./icons";
import { AxisBottom, AxisLeft, getNiceAxisLimits } from "./plots/Axes";
import { Chart, ChartContainer, type ChartData } from "./plots/ChartContainer";
import { Legend } from "./plots/Legend";
import { Line, Plume, type Point } from "./plots/Line";
import { Line, type Point } from "./plots/Line";
import { SkewTPlot, type SoundingRecord } from "./plots/skewTlogP";
import { Button } from "./ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
Expand Down Expand Up @@ -239,16 +239,25 @@ export function VerticalProfilePlot({
"Specific humidity [kg/kg]": "qt",
"u-wind component [m/s]": "u",
"v-wind component [m/s]": "v",
"vertical velocity [m/s]": "w",
"Pressure [Pa]": "p",
"Exner function [-]": "exner",
"Temperature [K]": "T",
"Dew point temperature [K]": "Td",
"Density [kg/m³]": "rho",
"Relative humidity [%]": "rh",
} as const satisfies Record<string, keyof ClassProfile>;

const classVariable = () =>
variableOptions[analysis.variable as keyof typeof variableOptions];

type PlumeVariable = "theta" | "qt" | "thetav" | "T" | "Td" | "rh" | "w";
function isPlumeVariable(v: string): v is PlumeVariable {
return ["theta", "qt", "thetav", "T", "Td", "rh", "w"].includes(v);
}

const showPlume = createMemo(() => isPlumeVariable(classVariable()));

const observations = () =>
flatObservations().map((o) => observationsForProfile(o, classVariable()));

Expand All @@ -266,10 +275,17 @@ export function VerticalProfilePlot({
const firePlumes = () =>
flatExperiments().map((e, i) => {
const { config, output, ...formatting } = e;
if (config.sw_fire) {
if (config.sw_fire && isPlumeVariable(classVariable())) {
const plume = transposePlumeData(
calculatePlume(config, profileData()[i].data),
);
return {
...formatting,
data: calculatePlume(config, profileData()[i].data),
linestyle: "4",
data: plume.z.map((z, i) => ({
x: plume[classVariable() as PlumeVariable][i],
y: z,
})),
};
}
return { ...formatting, data: [] };
Expand All @@ -288,10 +304,12 @@ export function VerticalProfilePlot({
})) as ChartData<Point>[];

const allX = () => [
...firePlumes().flatMap((p) => p.data.map((d) => d.x)),
...profileDataForPlot().flatMap((p) => p.data.map((d) => d.x)),
...observations().flatMap((obs) => obs.data.map((d) => d.x)),
];
const allY = () => [
...firePlumes().flatMap((p) => p.data.map((d) => d.y)),
...profileDataForPlot().flatMap((p) => p.data.map((d) => d.y)),
...observations().flatMap((obs) => obs.data.map((d) => d.y)),
];
Expand Down Expand Up @@ -322,10 +340,6 @@ export function VerticalProfilePlot({
setResetPlot(analysis.id);
}

const showPlume = createMemo(() => {
return ["theta", "qt", "thetav", "T", "Td"].includes(classVariable());
});

return (
<>
<div class="flex flex-col gap-2">
Expand Down Expand Up @@ -356,10 +370,7 @@ export function VerticalProfilePlot({
{(d) => (
<Show when={toggles[d.label]}>
<Show when={showPlume()}>
<Plume
d={d}
variable={classVariable as () => keyof Parcel}
/>
<Line {...d} />
</Show>
</Show>
)}
Expand Down
32 changes: 0 additions & 32 deletions apps/class-solid/src/components/plots/Line.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { Parcel } from "@classmodel/class/fire";
import * as d3 from "d3";
import { createSignal } from "solid-js";
import type { ChartData } from "./ChartContainer";
Expand Down Expand Up @@ -36,34 +35,3 @@ export function Line(d: ChartData<Point>) {
</path>
);
}

export function Plume({
d,
variable,
}: { d: ChartData<Parcel>; variable: () => keyof Parcel }) {
const [chart, _updateChart] = useChartContext();
const [hovered, setHovered] = createSignal(false);

const l = d3.line<Parcel>(
(d) => chart.scaleX(d[variable()]),
(d) => chart.scaleY(d.z),
);

const stroke = () => (hovered() ? highlight("#ff0000") : "#ff0000");

return (
<path
clip-path="url(#clipper)"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
fill="none"
stroke={stroke()}
stroke-dasharray={"4"}
stroke-width="2"
d={l(d.data) || ""}
class="cursor-pointer"
>
<title>{`Fire plume for ${d.label}`}</title>
</path>
);
}
21 changes: 15 additions & 6 deletions packages/class/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,15 +408,15 @@ const untypedSchema = {
symbol: "L<sub>fire</sub>",
type: "number",
unit: "m",
default: 10000,
default: 1000,
title: "Length of the fire",
"ui:group": "Fire",
},
d: {
symbol: "d<sub>fire</sub>",
type: "number",
unit: "m",
default: 300,
default: 10,
title: "Depth of the fire",
"ui:group": "Fire",
},
Expand All @@ -432,16 +432,24 @@ const untypedSchema = {
symbol: "C<sub>fire</sub>",
type: "number",
unit: "J kg⁻¹",
default: 17.781e6,
default: 18.6208e6,
title: "Heat stored in fuel",
"ui:group": "Fire",
},
Cq: {
symbol: "C<sub>q,fire</sub>",
type: "number",
unit: "kg kg⁻¹",
default: 0,
title: "Moisture per kg fuel released into plume",
"ui:group": "Fire",
},
omega: {
symbol: "ω<sub>fire</sub>",
type: "number",
unit: "kg m⁻²",
default: 7.6,
title: "Fuel mass per area",
default: 0.1,
title: "Consumed fuel mass per area",
"ui:group": "Fire",
},
spread: {
Expand All @@ -458,7 +466,7 @@ const untypedSchema = {
unit: "-",
default: 0.7,
title:
"Fraction of F converted into radiative heating, and not into diffused into the atmosphere",
"Fraction of F converted into radiative heating, and not diffused into the atmosphere",
"ui:group": "Fire",
},
},
Expand Down Expand Up @@ -522,6 +530,7 @@ export type FireConfig = {
d: number;
h0: number;
C: number;
Cq: number;
omega: number;
spread: number;
radiativeLoss: number;
Expand Down
Loading