Enhancement/high fidelity mode 3 - #339
Draft
WPringle wants to merge 15 commits into
Draft
Conversation
…n-linestring-aware helpers
Isolated, additive changes with no cross-file dependencies beyond
what's already in the repo:
- New minAngleMeshQuality/collapseMinAngleTriangles utilities for
quality-driven triangle collapse (used by msh.clean('collapse',N)
in a later commit).
- New fill_single_element_holes utility (not yet wired into any
caller).
- writeww3 gains normalize_open_boundary_nodes to tolerate sparse,
empty, or mis-sized open-boundary arrays; writefort15 special-cases
the OWI (NWS13) namelist. TestWW3Export regression test added and
wired into RunTests.
- extract_boundary: fix field name ibtype -> ibtypee to match the
opendat/op convention already used elsewhere (e.g. readfort14).
- edgefx: bounds-check weir-spacing indices and increase weir spacing
amplitude (weir_spacing -> weir_spacing*8).
- Get_line_edges: only add a closing edge for genuinely closed loops,
so open polylines (thalwegs) don't get a spurious wrap-around edge.
- filter_polygon_constraints: rewritten on polyshape/polybuffer;
primary-box retention flips AND->OR (keep edge if either endpoint
is inside) to avoid dropping constraints near multiscale seams.
- shoelace: NaN-aware, now sums absolute sub-polygon areas instead of
a single signed loop.
- drawedge2: trailing-whitespace cleanup only.
Read_shapefile is rewritten to classify each shapefile feature as a closed polygon or an open linestring by endpoint-closure tolerance, returning the open features in a new linestrings field. geodata gains a matching linestrings property (populated from the constructor) and renders them as dashed lines with start/end markers in plot(). This is the data-model groundwork for river/thalweg-constrained meshing consumed by the meshgen high-fidelity mode 3 pipeline added next.
…h collapse/weir extensions @CleanPSLG (new): conditions a planar straight-line graph of fixed points/edges via mergeVertices, dropIntersectingEdges, pruneEncroachingEdges, dropNearbyEdges, preserveLongChains, and deleteShortChains, preserving long contiguous chains while pruning nearby/parallel edges. @meshgen: constraint assembly is extracted from the inline meshing loop into a new generateBreaklineConstraints() method, which now also consumes geodata.linestrings (thalwegs) as breakline constraints. Adds high_fidelity mode 3: resample shoreline/island vectors like mode 1, then prune the resulting breakline PSLG through the CleanPSLG pipeline (preserveLongChains -> dropNearbyEdges -> deleteShortChains -> cleanUnusedVertices), gated by any(high_fidelity == 3). Also adds an enforceWeirs constructor option. Fixed the collapse_thin_triangles method, which referenced an undefined `mesh` variable instead of `obj`. @MSH: clean() gains a 'collapse' option (max iterations) that iteratively collapses low-min-angle triangles via collapseMinAngleTriangles, re-smoothing and relocating fixed points between iterations, replacing the old inline collapse_thin_triangles(p,t,db) call. make_bc() gains extended weir/internal-barrier controls (weir_height, weirs_set_elev, barinht, barincfsb, barincfsp) plus silent/noninteractive/tolerance UX flags.
Adds the owiWindNetcdf name-value option, which populates a new owiWindNetCDF control namelist (NWS13File, NWS13ColdStartString) and writes it via writefort15's no-'Control'-suffix OWI special case. Also updates the wetDry namelist defaults (slim 1e9 -> 4e-4, windLim false -> true) to match ADCIRC's documented wetDryControl defaults.
When the first cleaning pass leaves a poor-quality mesh, clean() recurses to clean it again but only captured the recursive call's mesh (obj = clean(obj,varargino(:));), not its quality metrics. The outer call then returned its own pre-recursion qual, computed on the mesh that was just discarded for being too poor -- e.g. a min quality of ~0 from a degenerate element that the recursive pass had already fixed. This bug already existed identically in the Projection base; it just wasn't often exposed because Example_1_NZ rarely needed a second cleaning pass. It surfaces more now that mode-1 clean() no longer collapses thin triangles unconditionally (collapse is opt-in), which makes meshes more likely to need re-cleaning.
Read_shapefile's rewrite dropped a my_interpm(...,h0/2) densification
of the outer bounding-box polygon that the prior version always
performed. Per Keith, this was intentional: meshing works fine
without it and it's faster on large bounding boxes, so it stays off
by default. This adds it back as an explicit opt-in
('densify_outer',1) for cases that want the denser outer boundary.
Keeps TestSanity's existing baseline mesh statistics valid without changing the library-wide default (densify_outer stays off elsewhere).
Three issues in meshgen.build's iterative refinement loop, found by
bisecting a TestEleSizes regression (91.30% -> 99.06% of vertices in
resolution bounds at grade=0.15, matching the old Projection
baseline):
1. `imp` (how often the periodic delete/split mesh-improvement pass
runs) was hardcoded to 9999. Since itmax defaults to 100, that
pass effectively never ran. Restored the old default of 10.
2. HIGH_FIDELITY_MODE detection was checking `nfix > 0` instead of
`obj.high_fidelity{i}`, and the correct check was left commented
out. Restored it, and -- since disabling periodic mesh improvement
makes sense specifically when high-fidelity breakline constraints
are in play -- made high-fidelity mode force imp back to 9999,
rather than hardcoding it unconditionally.
3. delaunay_elim's egfix-empty path subtracts mean(pt1) before
building the Delaunay triangulation (for numerical conditioning),
but never added it back before converting TR.Points to lat/lon,
silently displacing every point on every retriangulation call.
This was the dominant cause of the regression: fixing only #1
dropped the mesh to 4579 points (94.06% in bounds) because the
improvement pass, once active, kept acting on displaced
coordinates. Storing and re-adding the mean brings the result to
13878 points / 98.85% in bounds, matching the old baseline.
imp is forced to 9999 in high-fidelity mode so the periodic mesh-improvement pass doesn't disturb breakline constraints, but the "quality is good enough, exit early" check below is gated by that same imp cadence -- so in high-fidelity mode it never triggers and the loop always runs the full itmax iterations. Documenting this rather than decoupling it, since none of the current tests exercise high_fidelity and the tradeoff hasn't been evaluated against one.
deltat defaults to 0.1 a few lines above; re-setting it to 0.10 when nfix > 0 was a no-op (same value). Only Fscale actually changes in that block. Pre-existing in the Projection base, not introduced by this branch -- functionally identical, just dead code removed.
- Exposes 'imp' (iterations between periodic mesh-improvement passes) as a meshgen constructor option, resolved in build() once nfix is known: user-supplied value if given, else 9999 (effectively disabled) when fixed points are present, else 10. - Changes the disabling condition from "high-fidelity mode is on" to "nfix > 0", matching how other convergence tuning (Fscale) is already conditioned in this codebase, and covering fixed points from weirs/user-supplied pfix as well as high-fidelity breaklines. - HIGH_FIDELITY_MODE itself is unchanged and still gates heal_fixed_edges as before. Verified the default (unspecified 'imp', nfix=0) path still produces the same TestEleSizes grade=0.15 result as before this refactor (13878 points, 98.85% in resolution bounds).
…e it for JBAY meshgen now warns per box if geodata.weirs is non-empty but enforceWeirs isn't set to 1, since weirPfix/weirEgfix are only pulled into the mesh's fixed constraints when enforceWeirs is on -- otherwise weirs are silently not enforced during meshing. Enables enforceWeirs on Example_5b_JBAY_w_weirs.m, which specifies weirs but wasn't turning this on.
mesh1d.m required boubox/box_num0 as positional args and assumed fh0/h were always cell arrays/vectors, breaking the original single-polygon calling style (no nesting) entirely. Moved boubox and box_num0 into varargin, defaulting to [] and 1 respectively when omitted, and wrap fh0 in a cell if it isn't one already. Existing callers in meshgen.m are unaffected since the calling syntax doesn't change. mesh1d is private to @meshgen and can't be called from outside the class (MATLAB blocks adding private folders to the path). Added a static call_mesh1d passthrough so it can be exercised from tests without duplicating the implementation. test_1d_original.m now calls meshgen.call_mesh1d, has real pass/fail assertions (point/edge count, convergence, edge-length bounds) instead of just drawing a figure, and is wired into RunTests.m.
…mp gating and weir tolerance Read_shapefile: features that are open by more than the closure tolerance are still carried through mainland/inner classification as pseudo-closed polygons (placeholder area, guaranteed to clear the size thresholds), in addition to being recorded as linestrings. This mirrors the previous Read_shapefile's behavior and stops such features (e.g. a barrier island digitized as an open trace) from being silently dropped from the mesh boundary when linestrings aren't consumed (high_fidelity off). meshgen.build: comment out the nfix>0 -> imp=9999 branch pending further evaluation against high-fidelity meshes; imp now only forces 9999 when high-fidelity mode is on. Also restores the missing trailing newline at EOF. msh.make_bc: weir front/back coordinate matching tolerance is now h0-relative (gdat.h0/111e3) instead of a fixed 0.001 degrees, since crestline points should coincide almost exactly with fixed mesh nodes when enforceWeirs is on. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Collaborator
Author
|
@krober10nd I have trouble with Example 5b with weirs. Will post issues soon |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose of High-fidelity mode 3
There are now three modes of High fidelity, the newest one (3) is added in this PR:
geodataobject with high fidelity flag 1.Summary of changes
(A) Thin-/small-angle triangle collapse becomes a first-class cleanup stage. The old inline
collapse_thin_triangles(p,t,db)call insidemsh.cleanis retired in favor of an explicit, iterative, quality-driven collapse pipeline gated by a new'collapse'option. This introduces three new files —minAngleMeshQuality.m,collapseMinAngleTriangles.m— plus fixed-node-aware relocation logic and Laplacian re-smoothing between iterations. Collapse is now measured against a minimum-angle quality metric (angle/60°), not a raw distance/aspect heuristic.(B) Open (unclosed) vector features — "linestrings"/thalwegs — are now a first-class geometry type.
Read_shapefilewas rewritten to classify each shape as a closed polygon or an open linestring by endpoint-closure tolerance.geodatagains alinestringsproperty that is populated, plotted, and carried through the pipeline. This is the data-model groundwork for river/thalweg-constrained meshing and pairs with the new@CleanPSLGclass.(C) New
@CleanPSLGclass for planar straight-line graph conditioning. A self-contained class (mergeVertices,dropIntersectingEdges,pruneEncroachingEdges,dropNearbyEdges,preserveLongChains,deleteShortChains,cleanUnusedVertices) that conditions fixed-point/edge constraint sets before meshing.meshgennow routespfix/egfixthrough aCleanPSLGfiltering pipeline that preserves long contiguous chains while pruning nearby/parallel edges.(C′) New high-fidelity meshing mode
high_fidelity == 3. Basemeshgensupported two modes —1(resample shoreline/island vectors to local resolution viamesh1d) and2(constrain the vector verbatim).new/adds mode3, which performs mode-1 resampling and then prunes the resulting breakline PSLG via the@CleanPSLGpipeline. This is the integration point that binds threads (B) linestrings and (C) CleanPSLG together, and the option exercised byExample_13_New_Zealand_High_Fidelity. Constraint assembly is refactored out of the inline meshing loop into a new private methodgenerateBreaklineConstraints(), which additionally consumes the newgeodata.linestrings(thalwegs) as breakline constraints.(D) Weir / internal-barrier boundary handling extended.
msh.make_bcgains weir-specific controls (weir_height,weirs_set_elev,barinht,barincfsb,barincfsp, plussilent/noninteractive/toleranceUX flags).edgefxweir-spacing enforcement is bounds-checked and its spacing amplitude changed (weir_spacing → weir_spacing*8).meshgenweir enforcement is exposed via a new'enforceWeirs'constructor option.(E) ADCIRC→WW3 export hardened; NWS13 (OWI NetCDF) forcing support added.
writeww3no longer assumes dense, fully-populated open-boundary arrays — anormalize_open_boundary_nodeshelper tolerates sparse/empty/mis-sizedopedat. A regression test (TestWW3Export) is added and wired intoRunTests. Separately,Make_f15/writefort15add theowiWindNetcdf(NWS13) namelist path.Additional smaller robustness fixes: NaN-aware
shoelacearea, non-closed-loop-awareGet_line_edges,polyshape/polybuffer-basedfilter_polygon_constraints, and a field-name correction inextract_boundary(ibtype → ibtypee).