Skip to content

Commit 583fb47

Browse files
committed
Align profile vars for skew-T diagram
1 parent 5147068 commit 583fb47

File tree

2 files changed

+81
-23
lines changed

2 files changed

+81
-23
lines changed

apps/class-solid/src/components/Analysis.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,12 +285,17 @@ function getLinesForExperiment(
285285

286286
// Find experiment-specific time index
287287
const tIndex = output.timeseries?.utcTime?.indexOf(timeVal);
288-
if (tIndex === undefined || tIndex === -1) return { label, color, linestyle, data: [] };
288+
if (tIndex === undefined || tIndex === -1)
289+
return { label, color, linestyle, data: [] };
289290

290-
const lineStyle = type === "plumes" ? "4" : linestyle;
291291
const linesAtTime = profile[variable]?.[tIndex] ?? [];
292292

293-
return { label, color, linestyle: lineStyle, data: linesAtTime.flat() };
293+
return {
294+
label: type === "plumes" ? `${label} - plume` : label,
295+
color,
296+
linestyle: type === "plumes" ? "4" : linestyle,
297+
data: linesAtTime.flat(),
298+
};
294299
}
295300

296301
/** Collect all lines across experiments for a given type */

packages/class/src/runner.ts

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ export function runClass(config: Config, freq = 600): ClassData {
4646
const profiles: TimeSeries1D = {};
4747
const plumes: TimeSeries1D = {};
4848

49-
// Helper function to parse class output
50-
// calculate profiles and fireplumes,
49+
// Helper function to parse class output
50+
// calculate profiles and fireplumes,
5151
// and export as timeseries
5252
const writeOutput = () => {
5353
const output: Partial<ClassOutput> = {};
54-
54+
5555
// Generate timeseries
5656
for (const key of outputKeys) {
5757
const value = model.getValue(key);
@@ -62,32 +62,31 @@ export function runClass(config: Config, freq = 600): ClassData {
6262
}
6363

6464
// Generate profiles
65+
const keysToAlign = ["p", "T", "Td"];
6566
if (config.sw_ml) {
6667
const profile = generateProfiles(config, output as ClassOutput);
6768
const profileXY = profileToXY(profile as unknown as Profile);
69+
const simplifiedProfile = simplifyProfile(profileXY, 0.01, keysToAlign);
6870

69-
for (const key of Object.keys(profileXY)) {
71+
for (const key of Object.keys(simplifiedProfile)) {
7072
profiles[key] = profiles[key] || []; // Initialize if not exists
71-
profiles[key].push(simplifyLine(profileXY[key], 0.01));
73+
profiles[key].push(simplifiedProfile[key]);
7274
}
7375

7476
// Generate plumes
7577
if (config.sw_fire) {
7678
const plume = calculatePlume(config, profile);
7779
const plumeXY = plumeToXY(plume);
78-
console.log("profile:", profile);
79-
console.log("plume:", plume);
80+
const simplifiedPlume = simplifyProfile(plumeXY, 0.01, keysToAlign);
8081

81-
for (const key of Object.keys(plumeXY)) {
82+
for (const key of Object.keys(simplifiedPlume)) {
8283
plumes[key] = plumes[key] || [];
83-
plumes[key].push(simplifyLine(plumeXY[key], 0.01));
84+
plumes[key].push(simplifiedPlume[key]);
8485
}
85-
8686
}
8787
}
8888
};
8989

90-
9190
// Initial time
9291
writeOutput();
9392

@@ -110,17 +109,19 @@ export function runClass(config: Config, freq = 600): ClassData {
110109
return { timeseries };
111110
}
112111

113-
type Profile = Record<string, number[]> & {z: number[]};
112+
type Profile = Record<string, number[]> & { z: number[] };
114113

115114
/**
116-
*
115+
*
117116
* Convert a profile like {z: [], theta: [], qt: [], ...}
118117
* to a profile like: {theta: {x: [], y: []}, qt: {x: [], y:[]}, ...}
119118
*
120119
* Useful to simplify profiles independently for each variable
121120
* and also to quickly obtain the data for a line plot
122121
*/
123-
function profileToXY(profile: Profile): Record<string, { x: number; y: number }[]> {
122+
function profileToXY(
123+
profile: Profile,
124+
): Record<string, { x: number; y: number }[]> {
124125
const result: Record<string, { x: number; y: number }[]> = {};
125126

126127
for (const key of Object.keys(profile)) {
@@ -150,23 +151,75 @@ function plumeToXY(plume: Parcel[]) {
150151
return result;
151152
}
152153

153-
154154
/**
155155
* Compress a line by discarding points that are within a certain relative tolerance.
156-
* Using the simplify-js package, which implements the
156+
* Using the simplify-js package, which implements the
157157
* Ramer-Douglas-Peucker algorithm
158158
*/
159-
function simplifyLine(line: { x: number; y: number }[], tolerance = 0.01): { x: number; y: number }[] {
159+
function simplifyLine(
160+
line: { x: number; y: number }[],
161+
tolerance = 0.01,
162+
): { x: number; y: number }[] {
160163
if (line.length <= 2) return line; // Nothing to simplify
161164

162-
const xs = line.map(p => p.x);
163-
const ys = line.map(p => p.y);
165+
const xs = line.map((p) => p.x);
166+
const ys = line.map((p) => p.y);
164167
const xRange = Math.max(...xs) - Math.min(...xs);
165168
const yRange = Math.max(...ys) - Math.min(...ys);
166169
const relTol = Math.min(xRange, yRange) * tolerance;
167170

168171
const simplified = simplify(line, relTol, true);
169-
console.log(`Simplified from ${line.length} to ${simplified.length} points`);
172+
// console.log(`Simplified from ${line.length} to ${simplified.length} points`);
170173
// console.log(`Simplified`);
174+
return simplified;
175+
}
176+
177+
/**
178+
* Simplify and optionally align a profile.
179+
*
180+
* @param profileXY - Profile in {x: number; y: number}[] format
181+
* @param tolerance - Relative tolerance for simplification (default 0.01)
182+
* @param alignKeys - Array of variable keys to align. If `true`, align all. If `false` or empty, skip alignment.
183+
* @returns The simplified (and optionally partially aligned) profile
184+
*/
185+
function simplifyProfile(
186+
profileXY: Record<string, { x: number; y: number }[]>,
187+
tolerance = 0.01,
188+
alignKeys: string[] | true | false = true,
189+
): Record<string, { x: number; y: number }[]> {
190+
// Simplify each variable
191+
const simplified: Record<string, { x: number; y: number }[]> = {};
192+
for (const key in profileXY) {
193+
simplified[key] = simplifyLine(profileXY[key], tolerance);
194+
}
195+
196+
// Decide which keys to align
197+
let keysToAlign: string[];
198+
if (alignKeys === true) {
199+
keysToAlign = Object.keys(profileXY);
200+
} else if (Array.isArray(alignKeys) && alignKeys.length > 0) {
201+
keysToAlign = alignKeys;
202+
} else {
203+
return simplified; // nothing to align
204+
}
205+
206+
// // Build union Z grid from all simplified variables
207+
// const zSet = new Set<number>(
208+
// Object.values(simplified).flatMap((line) => line.map((pt) => pt.y)),
209+
// );
210+
// Step 3: Build union Z grid only for keys to align
211+
const zSet = new Set<number>(
212+
keysToAlign.flatMap((key) => simplified[key]?.map((pt) => pt.y) ?? []),
213+
);
214+
215+
console.log(zSet.size);
216+
217+
// Align selected variables using original profileXY
218+
for (const key of keysToAlign) {
219+
if (profileXY[key]) {
220+
simplified[key] = profileXY[key].filter((pt) => zSet.has(pt.y));
221+
}
222+
}
223+
171224
return simplified;
172225
}

0 commit comments

Comments
 (0)