@meshioplusplus/wasm 10.0.0 → 10.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -236,6 +236,13 @@ export type OpSpec =
236
236
  value?: number;
237
237
  closure?: RefineClosure;
238
238
  recordLevels?: boolean;
239
+ /**
240
+ * Attach `refine:cell_id`/`refine:parent_id` -- the persistent
241
+ * parent/child hierarchy a multigrid caller resolves across the
242
+ * sequence of meshes it keeps. Also forces `refine:entity` to be
243
+ * attached even when the closure leaves no hanging node.
244
+ */
245
+ recordHierarchy?: boolean;
239
246
  }
240
247
  | {
241
248
  /**
@@ -295,6 +302,34 @@ export type OpSpec =
295
302
  output?: string;
296
303
  component?: number;
297
304
  }
305
+ | {
306
+ /**
307
+ * The Hessian (second derivative) of a scalar `point_data` field --
308
+ * `gradient`'s companion one order further, for curvature-based
309
+ * adaptive refinement. A pure data step: geometry is untouched and
310
+ * one new (n,9) array is attached.
311
+ */
312
+ op: 'hessian';
313
+ array: string;
314
+ method?: GradientMethod;
315
+ location?: 'point' | 'cell';
316
+ output?: string;
317
+ }
318
+ | {
319
+ /**
320
+ * The ZZ recovery-based error indicator of a `point_data` field, plus
321
+ * optional marking. A pure data step: geometry is untouched and the
322
+ * indicator (and, when `marking` is not `"none"`, the marking) array
323
+ * is attached.
324
+ */
325
+ op: 'estimateError';
326
+ array: string;
327
+ method?: ErrorMethod;
328
+ marking?: ErrorMarking;
329
+ markingValue?: number;
330
+ output?: string;
331
+ marked?: string;
332
+ }
298
333
  | {
299
334
  /**
300
335
  * A regular grid around the mesh. One of the two steps that replace their
@@ -363,6 +398,12 @@ export type SdfWatertightCheck = 'off' | 'warn' | 'error';
363
398
  /** `gradient`'s reconstruction method. See doc/gradient.md. */
364
399
  export type GradientMethod = 'green-gauss' | 'least-squares';
365
400
 
401
+ /** `estimateError`'s estimator family. Only `"zz"` exists today. */
402
+ export type ErrorMethod = 'zz';
403
+
404
+ /** `estimateError`'s marking policy. See doc/error.md. */
405
+ export type ErrorMarking = 'none' | 'absolute' | 'fraction' | 'dorfler';
406
+
366
407
  /** One data array's location: `point_data`, `cell_data`, or `field_data`. */
367
408
  export type DataLocation = 'point' | 'cell' | 'field';
368
409
 
@@ -438,6 +479,19 @@ export interface RefineOptions {
438
479
  closure?: RefineClosure;
439
480
  /** Attach the `refine:level` `cell_data` array. */
440
481
  recordLevels?: boolean;
482
+ /**
483
+ * Attach the `refine:cell_id`/`refine:parent_id` `cell_data` arrays -- the
484
+ * persistent parent/child hierarchy a multigrid caller resolves across the
485
+ * sequence of meshes it keeps ("a link between two meshes, not a tree
486
+ * inside one"): an unsplit cell keeps its id and is its own parent; a
487
+ * split cell's children each get a fresh id and carry the parent's id. An
488
+ * input already carrying `refine:cell_id` is updated whatever this says.
489
+ * Also forces `refine:entity` to be attached even when the closure leaves
490
+ * no hanging node, since it already records the coarse corners each new
491
+ * fine node is the mean of -- the multigrid prolongation weights, which
492
+ * `'redgreen'`/`'propagate'` would otherwise never expose.
493
+ */
494
+ recordHierarchy?: boolean;
441
495
  }
442
496
 
443
497
  /** Where `decimate` places the surviving vertex of a collapsed edge. */
@@ -453,6 +507,10 @@ export type InterpolateMethod = 'nearest' | 'barycentric';
453
507
  * target: throw, replace, or write to `name + '_interp'`. */
454
508
  export type InterpolateOnConflict = 'error' | 'overwrite' | 'suffix';
455
509
 
510
+ /** What `conservativeInterpolate` does when a transferred name already
511
+ * exists on the target: throw, replace, or write to `name + '_interp'`. */
512
+ export type ConservativeInterpolateOnConflict = 'error' | 'overwrite' | 'suffix';
513
+
456
514
  /** Partitioning backend: SFC is always available; KaHIP is never compiled
457
515
  * into the WASM build, so `'kahip'` always throws and `'auto'` = `'sfc'`. */
458
516
  export type PartitionMethod = 'sfc' | 'kahip' | 'auto';
@@ -486,6 +544,41 @@ export interface DataArrayInfo {
486
544
  inconsistentBlocks: boolean;
487
545
  }
488
546
 
547
+ /** Cell-measure-weighted reduction of one array over one set of cells (the
548
+ * whole mesh, or one named Cell region) -- see `dataIntegrate`. */
549
+ export interface FieldIntegralRegion {
550
+ /** The region's name; absent on the whole-mesh `domain` entry. */
551
+ name?: string;
552
+ /** Cells with a computable measure. */
553
+ numCells: number;
554
+ /** Cells excluded: unmeasurable geometry (ragged, unsupported type, or
555
+ * degenerate). */
556
+ numSkipped: number;
557
+ /** `sum(value * |measure|)` over cells finite in component k. */
558
+ totalPerComponent: number[];
559
+ /** `totalPerComponent[k] / domainMeasurePerComponent[k]`, or NaN when that
560
+ * denominator is zero. */
561
+ meanPerComponent: number[];
562
+ /** `sum(|measure|)` over cells finite in component k. */
563
+ domainMeasurePerComponent: number[];
564
+ /** Measurable cells excluded from component k because its value was
565
+ * non-finite there. */
566
+ numNanPerComponent: number[];
567
+ }
568
+
569
+ /** One array's field integral, returned by `dataIntegrate` --
570
+ * `gradient`'s integration counterpart. See doc/field_integration.md. */
571
+ export interface FieldIntegralArray {
572
+ name: string;
573
+ numComponents: number;
574
+ /** The whole-mesh reduction. */
575
+ domain: FieldIntegralRegion;
576
+ /** One independent entry per named Cell region present on the mesh -- a
577
+ * cell in two regions contributes fully to both, one in none contributes
578
+ * to neither. */
579
+ regions: FieldIntegralRegion[];
580
+ }
581
+
489
582
  /** Heavy-data layout of a transient XDMF series. */
490
583
  export type XdmfDataFormat = 'HDF' | 'XML' | 'Binary';
491
584
 
@@ -973,6 +1066,48 @@ export interface MeshioPlusPlusModule {
973
1066
  onConflict?: InterpolateOnConflict,
974
1067
  ): Mesh;
975
1068
 
1069
+ /**
1070
+ * Mass-preserving cross-mesh field transfer: an exact overlap-measure
1071
+ * weighted remap, so that over the region the two meshes share,
1072
+ * sum(target value * target measure) equals sum(source value * source
1073
+ * measure) — the property `interpolate`'s `'barycentric'` mode does not
1074
+ * have. Both meshes are simplexified first (accepting ragged/polyhedron
1075
+ * blocks for free, unlike a restricted cell-type scope). Unlike
1076
+ * `interpolate`, an empty `arrays` transfers every source point_data AND
1077
+ * cell_data array — there is one algorithm regardless of location. Output
1078
+ * arrays are always Float64.
1079
+ * @throws {Error} on an unknown onConflict, an unknown array name, a name
1080
+ * collision under `'error'`, mismatched maximum topological dimensions
1081
+ * between the two meshes, or no triangle/tetrahedron cells on either
1082
+ * side after simplexification.
1083
+ */
1084
+ conservativeInterpolate(
1085
+ source: Mesh,
1086
+ target: Mesh,
1087
+ arrays?: string[],
1088
+ defaultValue?: number,
1089
+ onConflict?: ConservativeInterpolateOnConflict,
1090
+ ): Mesh;
1091
+
1092
+ /**
1093
+ * Green-element undo: restore `fine`'s transitional (closure-only) cells
1094
+ * back to their original parent, read verbatim from `coarse` — a lookup,
1095
+ * not a reconstruction, since `refine` never renumbers or prunes points.
1096
+ * `fine` must carry `refine:cell_id`/`refine:parent_id`/`refine:level`
1097
+ * (i.e. must come from `refine(coarse, ..., {recordHierarchy: true,
1098
+ * recordLevels: true})`); `coarse` must be the mesh that call was run on.
1099
+ * The six reserved `refine:*` arrays are dropped from the output. Only a
1100
+ * single-pass (`levels=1`) hierarchy is supported.
1101
+ * @throws {Error} when `fine` lacks the required hierarchy arrays, when a
1102
+ * `refine:parent_id` value does not resolve against `coarse`'s id space,
1103
+ * or when a sibling group's level matches neither the red nor the green
1104
+ * relationship to its coarse parent's own level.
1105
+ */
1106
+ undoGreen(
1107
+ coarse: Mesh,
1108
+ fine: Mesh,
1109
+ ): { mesh: Mesh; numGroupsUndone: number; numCellsRemoved: number };
1110
+
976
1111
  /** Subset a mesh to an axis-aligned bounding box. */
977
1112
  cropBbox(mesh: Mesh, lo: number[], hi: number[], mode?: CropMode, recordIds?: boolean): Mesh;
978
1113
 
@@ -1188,6 +1323,76 @@ export interface MeshioPlusPlusModule {
1188
1323
  overwrite?: boolean,
1189
1324
  ): { mesh: Mesh; numSkipped: number; numFallback: number };
1190
1325
 
1326
+ /**
1327
+ * The Hessian (second derivative) of a **scalar** `point_data` field --
1328
+ * `gradient`'s companion one order further, for curvature-based adaptive
1329
+ * refinement.
1330
+ *
1331
+ * A composition of TWO `gradient` calls, not a new numerical kernel: the
1332
+ * field is differentiated once (point location), then that `(n, 3)`
1333
+ * gradient is differentiated again with the default gradient operator,
1334
+ * producing `(n, 9)` -- the flattened row-major 3x3 Hessian, `H[i][j]` at
1335
+ * index `i*3+j`. `method` is forwarded to BOTH internal passes. The width
1336
+ * travels with the array in the returned mesh's `point_data_components` /
1337
+ * `cell_data_components` maps, exactly as `gradient`'s own output does.
1338
+ *
1339
+ * A field that is at most LINEAR has an exactly zero Hessian everywhere --
1340
+ * the one mesh-shape-independent guarantee. For a genuinely quadratic
1341
+ * field the composition is exact on a structured/symmetric mesh away from
1342
+ * its own boundary and a good, standard, but genuinely approximate
1343
+ * curvature estimate on an irregular mesh (see doc/hessian.md).
1344
+ *
1345
+ * A curvature-driven refinement indicator needs no new API: `norm(...)`
1346
+ * in `dataCalc` on the 9-component output is exactly its Frobenius norm,
1347
+ * ready for `refine`'s `where` selector.
1348
+ * @throws {Error} when `array` names a `cell_data` array (piecewise
1349
+ * constant, so it has no derivative), an unknown array, or an array
1350
+ * with more than one component (hessian is scalar-only).
1351
+ */
1352
+ hessian(
1353
+ mesh: Mesh,
1354
+ array: string,
1355
+ method?: GradientMethod,
1356
+ location?: 'point' | 'cell',
1357
+ output?: string,
1358
+ overwrite?: boolean,
1359
+ ): { mesh: Mesh; numSkipped: number; numFallback: number };
1360
+
1361
+ /**
1362
+ * The Zienkiewicz-Zhu (ZZ) recovery-based error indicator of a `point_data`
1363
+ * field, plus optional marking. A composition of `gradient` (Green-Gauss,
1364
+ * cell location) with the measure-weighted point↔cell averaging round
1365
+ * trip: the indicator is `sqrt(|measure| * sum((recovered - raw)^2))` per
1366
+ * cell, attached as `output` (default `"error:zz"`, Float64).
1367
+ *
1368
+ * `marking` is `"none"` (default), `"absolute"`, `"fraction"`, or
1369
+ * `"dorfler"`; when not `"none"` a second Int64 0/1 array `marked`
1370
+ * (default `"error:marked"`) is attached too, so `refine`'s own `where`/
1371
+ * `--where` selector needs no change at all — the intended use is
1372
+ * `refine(mesh, {compare: '>', value: 0.5, array: 'error:marked'})`.
1373
+ * `markingValue`'s meaning depends on `marking`: an absolute indicator
1374
+ * threshold, a fraction in `(0, 1]` of cells, or the Doerfler bulk fraction
1375
+ * theta in `(0, 1]`.
1376
+ *
1377
+ * Cells that cannot be evaluated read NaN in the indicator array and 0
1378
+ * (never NaN) in the marking array, and are counted in `numSkipped`
1379
+ * (excluded from `globalError` and from `numMarked`).
1380
+ * @throws {Error} when `array` names a `cell_data` array (piecewise
1381
+ * constant, so it has no derivative to recover), an unknown array, an
1382
+ * unknown method/marking policy, or an out-of-range `markingValue` for
1383
+ * `"fraction"`/`"dorfler"`.
1384
+ */
1385
+ estimateError(
1386
+ mesh: Mesh,
1387
+ array: string,
1388
+ method?: ErrorMethod,
1389
+ marking?: ErrorMarking,
1390
+ markingValue?: number,
1391
+ output?: string,
1392
+ marked?: string,
1393
+ overwrite?: boolean,
1394
+ ): { mesh: Mesh; globalError: number; numSkipped: number; numMarked: number };
1395
+
1191
1396
  /** Partition a mesh into submeshes by type, connected component, or tag. */
1192
1397
  split(mesh: Mesh, by: SplitBy, tagName?: string): { key: string; mesh: Mesh }[];
1193
1398
 
@@ -1200,6 +1405,33 @@ export interface MeshioPlusPlusModule {
1200
1405
  */
1201
1406
  convertCells(mesh: Mesh, mode?: ConvertCellsMode, recordParentIds?: boolean): Mesh;
1202
1407
 
1408
+ /**
1409
+ * Polyhedrally refine a mesh: one polyhedral child per face of every
1410
+ * eligible 3D cell, connected to a new interior point. Needs no per-type
1411
+ * template table -- tabulated types (reduced to corners for a quadratic
1412
+ * variant) and existing polyhedron blocks are handled uniformly.
1413
+ * Automatically conforming, unlike `refine`. Non-3D blocks and the
1414
+ * full-Lagrange family (no face table) pass through unchanged. Unlike
1415
+ * `convertCells`, there is no point map -- subdivide never prunes or
1416
+ * renumbers an original point.
1417
+ * @throws {Error} when a cell's faces are not a closed orientable surface.
1418
+ */
1419
+ subdivide(mesh: Mesh, recordParentIds?: boolean): Mesh;
1420
+
1421
+ /**
1422
+ * Polyhedrally coarsen a mesh: merge groups of cells into single larger
1423
+ * polyhedral cells via greedy seed-and-grow over the mesh's shared-face
1424
+ * dual, absorbing face-adjacent neighbours into a group until it reaches
1425
+ * `targetGroupSize` (default 8; a short group at a mesh boundary or
1426
+ * pocket is expected, not an error). `targetGroupSize=1` groups every
1427
+ * cell by itself. Non-volume blocks pass through unchanged; points are
1428
+ * never pruned or renumbered (`clean(mesh, ..., true)` is the follow-up
1429
+ * for a minimal point set).
1430
+ * @throws {Error} when targetGroupSize is 0, or the mesh contains a face
1431
+ * shared by three or more cells (non-manifold).
1432
+ */
1433
+ agglomerate(mesh: Mesh, targetGroupSize?: number): Mesh;
1434
+
1203
1435
  /**
1204
1436
  * Refine a mesh, subdividing cells into same-type children (`line` → 2,
1205
1437
  * `triangle` → 4, `quad` → 4, `tetra` → 8, `wedge` → 8, `hexahedron` → 8).
@@ -1350,6 +1582,14 @@ export interface MeshioPlusPlusModule {
1350
1582
  /** Read-only per-array summary of every data array the mesh carries. */
1351
1583
  dataInfo(mesh: Mesh): DataArrayInfo[];
1352
1584
 
1585
+ /**
1586
+ * Cell-measure-weighted total/mean of one or more cell_data arrays --
1587
+ * gradient's integration counterpart. `arrays` empty/undefined means
1588
+ * every cell_data array. A point_data-only name throws naming
1589
+ * dataPointToCell as the fix. See doc/field_integration.md.
1590
+ */
1591
+ dataIntegrate(mesh: Mesh, arrays?: string[]): FieldIntegralArray[];
1592
+
1353
1593
  /**
1354
1594
  * Open a transient (time-series) XDMF writer on the virtual filesystem --
1355
1595
  * the one stateful object in this API. See {@link XdmfTimeSeriesWriter} for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshioplusplus/wasm",
3
- "version": "10.0.0",
3
+ "version": "10.9.0",
4
4
  "description": "meshio++ mesh I/O compiled to WebAssembly: read/write 41 mesh formats (incl. MED, CGNS, Exodus) in the browser or Node.js",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
package/src/index.mjs CHANGED
@@ -160,8 +160,12 @@ function resolveVariant(variant) {
160
160
  * distanceToSurface: (query: Mesh, surface: Mesh, sign?: string, location?: string, band?: number, recordInside?: boolean, watertightCheck?: string) => {mesh: Mesh, numBanded: number, quality: object},
161
161
  * computeSdf: (surface: Mesh, structure?: string, resolution?: number[], cellSize?: number, bounds?: number[], padding?: number, paddingRelative?: number, rootResolution?: number, maxDepth?: number, bandCells?: number, recordLevels?: boolean, maxCells?: number, sign?: string, location?: string, band?: number, watertightCheck?: string) => {mesh: Mesh, dims: number[], origin: number[], spacing: number[], maxDepth: number, numBanded: number, quality: object},
162
162
  * gradient: (mesh: Mesh, array: string, operator?: string, method?: string, location?: string, output?: string, component?: number, overwrite?: boolean) => {mesh: Mesh, numSkipped: number, numFallback: number},
163
+ * hessian: (mesh: Mesh, array: string, method?: string, location?: string, output?: string, overwrite?: boolean) => {mesh: Mesh, numSkipped: number, numFallback: number},
164
+ * estimateError: (mesh: Mesh, array: string, method?: string, marking?: string, markingValue?: number, output?: string, marked?: string, overwrite?: boolean) => {mesh: Mesh, globalError: number, numSkipped: number, numMarked: number},
163
165
  * split: (mesh: Mesh, by: string, tagName?: string) => {key: string, mesh: Mesh}[],
164
166
  * convertCells: (mesh: Mesh, mode?: string, recordParentIds?: boolean) => Mesh,
167
+ * subdivide: (mesh: Mesh, recordParentIds?: boolean) => Mesh,
168
+ * agglomerate: (mesh: Mesh, targetGroupSize?: number) => Mesh,
165
169
  * refine: (mesh: Mesh, levels?: number, recordParentIds?: boolean,
166
170
  * options?: object) => Mesh,
167
171
  * decimate: (mesh: Mesh, ratio?: number, targetFaces?: number, maxError?: number, placement?: string, preserveBoundary?: boolean, preserveFeatures?: boolean, featureAngle?: number) => {mesh: Mesh, facesRemoved: number, pointsRemoved: number, collapsesRejected: number, maxErrorApplied: number},
@@ -174,6 +178,7 @@ function resolveVariant(variant) {
174
178
  * dataCalc: (mesh: Mesh, expression: string, location: string, outputName: string, overwrite?: boolean) => Mesh,
175
179
  * dataCondition: (mesh: Mesh, location: string, names?: string[], mode?: string, lo?: number, hi?: number, scope?: string, nanPolicy?: string, nanReplacement?: number, suffix?: string) => Mesh,
176
180
  * dataInfo: (mesh: Mesh) => object[],
181
+ * dataIntegrate: (mesh: Mesh, arrays?: string[]) => object[],
177
182
  * createXdmfTimeSeriesWriter: (path: string, options?: {dataFormat?: string, gzipLevel?: number, mode?: 'truncate'|'append', autoFlush?: boolean}) => XdmfTimeSeriesWriter,
178
183
  * }>}
179
184
  */
@@ -352,6 +357,26 @@ export async function loadMeshioPlusPlus(moduleOverrides = {}, { variant = 'auto
352
357
  defaultValue,
353
358
  onConflict,
354
359
  ),
360
+ // Mass-preserving cross-mesh field transfer: an exact overlap-measure
361
+ // weighted remap (unlike interpolate's pointwise sampling). An empty
362
+ // `arrays` = every source point_data AND cell_data array.
363
+ conservativeInterpolate: (
364
+ source,
365
+ target,
366
+ arrays = [],
367
+ defaultValue = 0,
368
+ onConflict = 'error',
369
+ ) =>
370
+ Module.conservativeInterpolate(
371
+ source,
372
+ target,
373
+ arrays,
374
+ defaultValue,
375
+ onConflict,
376
+ ),
377
+ // Restore `fine`'s transitional (green) cells to their coarse parent,
378
+ // read verbatim from `coarse` (a lookup, not a reconstruction).
379
+ undoGreen: (coarse, fine) => Module.undoGreen(coarse, fine),
355
380
  cropBbox: (mesh, lo, hi, mode = 'all', recordIds = false) =>
356
381
  Module.cropBbox(mesh, lo, hi, mode, recordIds),
357
382
  cropPlane: (mesh, point, normal, mode = 'all', recordIds = false) =>
@@ -431,9 +456,39 @@ export async function loadMeshioPlusPlus(moduleOverrides = {}, { variant = 'auto
431
456
  overwrite = false,
432
457
  ) =>
433
458
  Module.gradient(mesh, array, operator, method, location, output, component, overwrite),
459
+ hessian: (
460
+ mesh,
461
+ array,
462
+ method = 'green-gauss',
463
+ location = 'cell',
464
+ output = '',
465
+ overwrite = false,
466
+ ) => Module.hessian(mesh, array, method, location, output, overwrite),
467
+ estimateError: (
468
+ mesh,
469
+ array,
470
+ method = 'zz',
471
+ marking = 'none',
472
+ markingValue = 0.0,
473
+ output = '',
474
+ marked = '',
475
+ overwrite = false,
476
+ ) =>
477
+ Module.estimateError(
478
+ mesh,
479
+ array,
480
+ method,
481
+ marking,
482
+ markingValue,
483
+ output,
484
+ marked,
485
+ overwrite,
486
+ ),
434
487
  split: (mesh, by, tagName = '') => Module.split(mesh, by, tagName),
435
488
  convertCells: (mesh, mode = 'linearize', recordParentIds = false) =>
436
489
  Module.convertCells(mesh, mode, recordParentIds),
490
+ subdivide: (mesh, recordParentIds = false) => Module.subdivide(mesh, recordParentIds),
491
+ agglomerate: (mesh, targetGroupSize = 8) => Module.agglomerate(mesh, targetGroupSize),
437
492
  refine: (mesh, levels = 1, recordParentIds = false, options = undefined) =>
438
493
  Module.refine(mesh, levels, recordParentIds, options),
439
494
  // Exactly one of ratio / targetFaces / maxError must be non-negative;
@@ -519,6 +574,7 @@ export async function loadMeshioPlusPlus(moduleOverrides = {}, { variant = 'auto
519
574
  mesh, location, names, mode, lo, hi, scope, nanPolicy, nanReplacement, suffix,
520
575
  ),
521
576
  dataInfo: (mesh) => Module.dataInfo(mesh),
577
+ dataIntegrate: (mesh, arrays = []) => Module.dataIntegrate(mesh, arrays),
522
578
  // Transient (time-series) XDMF -- the one *stateful* thing in this API.
523
579
  // The raw binding is an opaque integer handle plus seven free
524
580
  // functions (see bindings/wasm/js_bindings.cpp for why it is not an