Merge upstream/development (IBSEB, erf-model/ERF#3960 and #4022) into ERF-Fire - #427
Merged
Merged
Conversation
…ance Eight phases on faces of resolved buildings: face storage, shortwave with ray-cast shadowing, longwave through sky/ground/building view fractions, sensible heat through the existing immersed-forcing wall model, slab conduction, the prognostic balance, and two canonical cases. Radiation reaches the balance through a provider interface (prescribed, or the two-stream column when that branch is merged), so the branch stays based on development and carries only its own commits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…balance erf.ibseb.enable builds, on every level, a compact list of the wall faces of the resolved buildings: a face between a fluid cell (blanking < 0.5) and a solid cell (>= 0.5), stored once on the rank that owns the fluid cell as device arrays (cell, direction, side, building id, material id, area, skin temperature, slab temperatures, view fractions, fluxes), contiguous per local box. Building ids label the solid columns of the blanking. Nothing evolves yet. Output through cell-centred fields on demand: ibseb_nfaces and ibseb_tskin in the plotfile, IBSEBState in the checkpoint, refilled into the rebuilt list on restart so restarts do not depend on the rank count. A [IBSEB] summary line and a per-building CSV every erf.ibseb.csv_int steps. Regtest Exec/CanonicalTests/SEB/Storage: the ImmersedForcingTest skyscraper; face counts per direction checked against the plotfile mask, identical on one and four ranks (2056 faces, 51400 m2), checkpoint at step 2 and restart to step 4 reproducing the CSV row. Sphinx: inputs table and a theory page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
plot_storage.py draws, with yt, a horizontal slice of the mask and of the faces-per-cell field through the building and vertical slices of the mean skin temperature through its centroid, with the mask outline, in absolute metres (ERF plotfiles carry no unit metadata) and with the y-normal slice swapped to (x, z). The four sources of the balance get file, class and function documentation in the Doxygen style of the existing code, spelling out the face convention (fluid cell, direction, side, outward normal), the ownership and per-fab layout of the list, the building labelling, the six-slot checkpoint field, and which phase fills which array. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
erf.ibseb.debug now prints, in the fire module's [FIRE DEBUG] style: the inputs and the global face counts at build, each building's footprint columns and index box, every rank's face count with its fab ranges and device memory, a summary with per-building rows every step, and the checkpoint save and load of the face state. The regtests move to Exec/CanonicalTests/SEB/Phase<n>_<name>, starting with Phase1_Storage, so the phase order is visible in the tree; the plan, the theory page and the README follow. The y-normal slice of plot_storage.py gets its labels after yt's axis swap, so they read x and z. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A prescribed radiation provider (erf.ibseb.radiation = prescribed) gives the direct-normal and horizontal diffuse irradiances and the sun vector, either fixed (sun_mode = fixed, for analytic tests) or from the site and time (sun_mode = solar: Spencer solar geometry, Bird direct beam, a diffuse share of the attenuated beam), through a single copied header so the branch has no dependency on ERF-Radiation. Per face and per step: direct = DNI max(0, n.s) unless the ray from the face centre toward the sun hits a building, decided by a 2D walk over the column tops of the level (a small array replicated on every rank; the ray only rises, so a column blocks it when the entry height is below its top); diffuse = f_sky D + f_ground albedo_ground (DNI cos z + D) with the placeholder view fractions (roof: sky 1; wall: sky and ground 0.5 each); absorbed = (1 - albedo) times the sum. Called at the start of every step. New plot fields ibseb_sw_abs and ibseb_shadow, shortwave columns in the per-building CSV, a per-rank face dump (erf.ibseb.dump_faces_file), and the sun in the [IBSEB DEBUG] output. Regtest Exec/CanonicalTests/SEB/Phase2_Shortwave: a short box 40 m east of a tall one; the shadow flag of every face matches an independent Python ray cast, the fluxes match the formulas on every face, the tall core roof and west wall are unshadowed with the exact incidence, the shadow on the short box's core west wall stops at H - gap tan(elevation), one and four ranks agree, and the solar mode gives the solstice-noon zenith at Boulder. The embedded-boundary reader steps each edge over one cell, which the test reads from the dump rather than assuming. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… balance Once at initialisation every face samples a cosine-weighted hemisphere around its outward normal (erf.ibseb.view_n_az x view_n_el rays, stratified so the counts are view factors) through the column walk of the shadow test made direction-aware: a rising ray is blocked by a column whose top is above its entry height, a falling ray by a column it descends into, and reaches the ground otherwise. The fractions of rays ending on the sky, the ground and a building replace the phase 2 placeholders in the diffuse shortwave. Longwave per step: LW_in = f_sky LW_sky + f_ground eps_g sigma T_g^4 + f_bldg sigma T_skin^4, the sky term fixed (erf.ibseb.lw_down) or gray (sky_emissivity sigma T_air^4 with the air temperature of the face's fluid cell, through the equation of state), the building term the isothermal- surroundings approximation; LW_net = eps (LW_in - sigma T_skin^4). No face-to-face view factors, no radiosity. New plot fields ibseb_lw_net and ibseb_f_sky, longwave in the CSV and the face dump, the sampling summary in the debug output. Regtest Exec/CanonicalTests/SEB/Phase3_Longwave on the two-box deck: the three fractions of every face sum to one and equal an independent Python hemisphere sampling; the tall core roof sees only sky, no roof sees the ground, the tall west wall sees exactly half sky with its rim ledge filling a quarter of the view just above it and little 50 m up, the short box's core west wall sees the tall box; the longwave formulas hold on every face for both sky modes; one and four ranks agree. Phase 2 still passes with the sampled fractions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Per face and per step: the tangential wind of the fluid cell at half a cell from the wall gives u* with the roughness z0_wall, the skin-to-air potential-temperature difference (the skin temperature converted with the cell's Exner function) gives theta* with z0h_wall, and H = rho c_p u* theta*, positive out of the face. Walls are neutral; erf.ibseb.stability_correction applies the surface layer's similarity functions on roofs. The latent flux stays zero with its argument in place. The flux enters the atmosphere as an explicit source: every face deposits H A / (c_p V Pi) into the rho-theta equation of its fluid cell, added after make_sources at every slow stage with atomic adds; erf.ibseb.couple_heat = false diagnoses without applying. The immersed forcing's own surface- temperature inputs are refused when the balance is on, since it now owns the temperature condition at the buildings. New plot field ibseb_H, the flux in the CSV, the dump and the debug output, and H_total_W per step. Regtest Exec/CanonicalTests/SEB/Phase4_Sensible: a 40 m cube held at 320 K in an 8 m/s wind at 300 K. u* and H match the formulas on every face of the dump, H is positive and largest on the windward wall, one and four ranks agree, and with the flux applied the extra internal energy of the air against the diagnostic run at the same step matches the summed face flux (the domain is a rigid closed box, so it is c_v, not c_p, that closes). A mass-inflow, pressure-outflow variant (as the Askervein canonical) runs with a wake warmer than the inflow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every face carries a slab of erf.ibseb.n_slab_layers uniform layers between its skin and the building interior at T_interior, solved implicitly with the Thomas algorithm in the form of the SLUCM branch's slab solver, with the skin temperature as the top boundary instead of a flux (ERF_IBSEBSlab.H); G = 2k/dz (T_skin - T_0) is the conduction into the wall, positive inward. Unconditionally stable, up to 256 layers, advanced every step. Materials: erf.ibseb.material_file names a CSV in the SLUCM schema (rank-0 read, broadcast), material_default and material_by_building assign them by building id; the conductivity, heat capacity, thickness, albedo and emissivity become per-face arrays, and the shortwave and longwave now use the per-face optical properties. Without a file the uniform k_therm, rho_cp, thickness, albedo and emissivity apply. New plot field ibseb_G, G in the summary and the dump with the material columns and the top and bottom slab layers. Regtest Exec/CanonicalTests/SEB/Phase5_Ground: a 200 mm slab in 1 mm layers follows the semi-infinite erfc solution for a boundary step (bottom layer 300.925 K against 300.921 K expected at 50 s) with no flux through the skin; a light 20 mm slab reaches G = k dT / L exactly with a linear profile; two buildings carry the concrete and timber of the CSV; the slab and its flux restart exactly through a checkpoint. The atmosphere-derived columns differ by about 1e-4 after a restart, which is the immersed-forcing atmosphere of development, not the balance, and is noted in the plan. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every face now solves SW_abs + eps Q_ext + LW_net - H - LE - G = 0 for its skin temperature at the end of each step (ERF_IBSEBBalance.H, lifted from the SLUCM facet solver). The conduction term is the flux the implicit slab step will take, G = a T - b from two trial slab steps, so the balance and the slab agree to rounding and the slab energy changes by exactly dt (G - G_bottom). The wall term of the incoming longwave folds into the emission as (1 - f_bldg); the wall-function coefficient is frozen at the wind of the step. Q_ext is an incident external flux absorbed with the emissivity, the hook for a fire's radiation (erf.ibseb.Q_ext_uniform for tests). The bounds and the step cap are inputs since fire exposure exceeds the urban canopy model's 380 K. erf.ibseb.prognostic defaults to true; the phase 2-5 decks pin it false to keep checking each term on its own. Fixes the solar azimuth of phase 2, which was mirrored east-west (sign of the sine term); the noon check could not see it, the sunrise deck did. Regtest SEB/Phase6_Prognostic on a cube: residual below 1e-8 W/m2 on every face at every step, every stored flux consistent with the skin temperature, slab energy exact per step, closure over the run within the summed residual, an independent Python model (own Newton, dense implicit slab) driven by the per-step face dumps reproducing the skin temperature to 1e-9 K, an external flux run past 380 K, a checkpoint restart, and the sun rising over the cube at Boulder with the east wall warming first. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…l_cells) A building from a nodal height map has sliver cells with 1-20 percent solid at the corners of the reader's one-cell ramp. The drag on a partial cell fades with its fraction, but the wall law was applied at full rate to any cell above 0.005 solid whose normal neighbour was exactly zero, so the top cell of a sliver column was forced like a wall on top of an almost free cell. In a neutral 3 m/s run on a 40 m cube at 10 m and 0.5 s a vertical two-cell checkerboard in theta grew there over hours (10 K, 5 m/s spikes) until the floating-point trap fired after 1.8-2.5 h, with the surface energy balance off, a no-slip ground, the forcing outside the substeps and a coarser small-cell threshold as well. With erf.if_snap_partial_cells the six forcing functions place the wall law and the surface temperature, flux and Obukhov conditions on cells at least half solid whose normal neighbour is less than half solid, the rule the face balance uses; cells below half solid keep the fraction-weighted drag only. Faces passing several wall tests at once have their relaxations averaged so the explicit substep forcing stays within its limit. Default false: with the switch off every result is bit-identical (checked on the phase 6 closure deck). Regtest Exec/RegTests/ImmersedForcingTest/PartialCells: the height-map cube for 2.64 h with the switch on stays neutral to 1e-3 K with a wake; --reproduce runs the original selection, which traps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Exec/CanonicalTests/SEB/Phase7_IsolatedBuilding runs a 40 m concrete cube at Boulder on the June solstice from midnight for 24 hours in a light westerly with the prescribed clear-sky provider and a gray sky. The cube is an exact box (eb2.geometry = box) so the immersed forcing has no partial cells; the per-building CSV gains the sun (zenith, azimuth, DNI, diffuse) so the day can be plotted without debug output. The day: every face radiates below the air at night, the roof coldest at dawn; the east wall is the first face to rise above the air after sunrise, forty minutes before the roof; the east wall peaks at 09:39, the roof at 13:55 at 340 K two hours after the sun, the west wall at 16:45; the south wall is 9 K warmer than the north at 13:00; conduction turns around at 18:10 and the slab's stored 16.5 MJ/m2 keeps the roof above the air past 23:00. The checker asserts that sequence, the balance residual over the day (4e-8 W/m2), the absorbed shortwave on the roof against the clear-sky formulas integrated independently in Python (24.882 MJ/m2 both) and the slab energy against the integrated conduction (0.01 %). The plot script draws the skin temperature by orientation with the air, the roof budget, the sun path, a slab Hovmoller and yt slices. Records under findings that the sensible flux off the 340 K roof is only 10-30 W/m2 with the neutral wall function, which the next PR addresses with the stability functions and a convective velocity scale, both behind switches; and that the restart non-exactness of the immersed forcing persists after erf-model erf-model#3956. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The neutral log law on the tangential wind cannot shed heat from a hot face in calm air, which is why the day canonical's roof reached 340 K. Two switches, both off by default so every earlier result is unchanged: erf.ibseb.convective_velocity = deardorff puts a convective velocity scale into the wind the wall function sees, U_eff^2 = U_tan^2 + (beta w*)^2 (Beljaars), with w* = (g/theta H/(rho c_p) depth)^(1/3) from the previous step's flux out of the face. The depth is the mixed layer above a roof (z_i - z_face, floored at the building height) and the building height for a wall; z_i follows the day by the bulk Richardson diagnostic on the horizontal-mean profile (erf.ibseb.z_i_mode = bulk_ri), from the surface layer's pblh at the column (pblh), or fixed (z_i). erf.ibseb.stability_correction now iterates Dyer's similarity functions on the roofs to convergence on the face's own Obukhov length, under- relaxed as the surface layer's iteration is in erf-model erf-model#3486, seeded from the ground surface layer's 2D field at the column (erf.ibseb.obukhov_seed). Walls stay on the log law. The face's L stays its own because a roof in a separation zone or a sunlit wall can be in the opposite regime from the ground. The face dump gains w_star, Olen, z_i and h_bld; the summary gains w_star_max. Regtest SEB/Phase8_WallFunction: the cube in calm air under a strong sun; the neutral law sheds 0.2 W/m2 from a 342 K roof and the scale 270 W/m2; w*, the depth, u* and H follow the formulas to 1e-9; the roofs' L is negative and consistent with u* and theta* and the corrected log law to 1e-7; the bulk Richardson depth on a capped sounding is the first cell above the inversion. The default path is bit-identical to the phase 6 reference. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…dary aware erf.if_snap_partial_cells now reads the cell and face blanking snapped to solid or fluid at half, so a height-map building becomes the staircase of whole cells an exact box is (wall faces fully blanked, interiors damped, nothing on the sliver cells), and it selects the point-implicit drag: the staircase puts full-strength forcing on every wall face, whose explicit rate at rims and corners drives the density negative within seconds on a thin slab (the first version, which only moved the selection threshold, survived the cube for 2.6 h but killed a 3-cell slab in two minutes). With the switch off every result is bit-identical. The PartialCells regtest runs the height-map cube 2.6 h clean. erf.pbl_ib_aware (MRF and YSUNew only; MYNN, MYJ, YSU and SHOC untouched) makes each column's surface the first fluid cell above the immersed solid: the bulk Richardson heights, the boundary-layer depth and the K profile are measured from it, the diffusivities vanish inside the solid, and the surface scales of a column with solid cells are a neutral log law at its top with erf.pbl_ib_z0, since the ground surface layer evaluates u*, theta* and L on cells inside the building. Without immersed cells the results are bit-identical; not supported with terrain-fitted coordinates. Regtest ImmersedForcingTest/PBL_IBAware: with the switch the schemes are finite everywhere and zero inside the cube, and the profile over the roof has the shape of the ground's against the local height (MRF peaks 15 m above each); without it MRF fills the domain with NaN and YSUNew drives the density negative at its second step, reported not asserted. The balance prints a cost line (per-step time of the slowest rank, faces per rank, initialisation time) for estimating city-scale cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Exec/CanonicalTests/SEB/Phase8_BuildingSet: four buildings from a nodal height map on a 480 m periodic domain at 10 m (a 60 m concrete slab, a 40 m brick cube east of it, two 20 m timber blocks), three materials by building, a 3 m/s westerly, Boulder on the solstice from 05:00 for six hours with the prescribed provider, the convective velocity scale and the stability functions on, and the immersed forcing snapped to whole cells. Checked: the four buildings and their materials; the residual (3e-8 W/m2 over 1444 rows); the mutual shadowing (at sunrise the cube's shadow lies on the slab's east wall, 17 percent of its faces, clearing to 6 percent; the 20 m blocks free of shadow by late morning); the building view fractions of the facing walls (28 and 48 percent) against the far block's (18); the timber roofs ending at 333 K against the concrete slab's 320 K; w* on every sunlit face and 93 percent of the roofs unstable; the cost line (0.4 ms per step for 157 faces per rank). The plot script draws the per-building temperatures, the shadow fractions, a face map from above and a yt slice. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…3 through a restart The report row gained net-longwave and sensible-flux columns after phase 1 was written; they read the air temperature and wind, which the immersed forcing does not restart bit-for-bit (2e-5 relative, see Phase5_Ground). The geometry, skin and slab columns stay exact. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Single-precision builds (Apple Clang and SYCL, Mesh SINGLE) refused two brace-initialised arrays in ERF_IBFaceSet.cpp that narrowed double to float: the face-centre coordinates and the cell-centred velocity. Both are now built from Real expressions. The IB-aware YSUNew kernels read the surface height into a local that seven of them never used; those locals are removed (no change in behaviour). codespell flagged the Fourier-number variable "Fo" in the slab solver and its Python re-implementation, and a "tha" alias in the phase 6 check; renamed to Fourier and th_air. Phase 6 and the PBL IB-awareness regtests pass on the rebuilt binary. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
plot_buildingset.py gains three figures from the existing run output: every face coloured by the incident direct beam at 06:00, 08:00 and 10:00 solar time (a view from the south-east and a top-down map with roofs as squares and wall columns as bars), and the horizontal wind in the first two cells above the ground with a vertical slice of u through the slab and the cube from the plotfile. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts in the MRF and YSUNew kernels, where development's PBLH work (erf-model#3486) meets this branch's immersed-boundary awareness (local surface ksurf and effective surface scales us_eff/ts_eff/qs_eff/ol_eff): - MRF passes 2 and 4: keep development's convective/shear w* blend, evaluated on the column's local surface (theta_v at ksurf) with the effective u* and theta*, as the branch does for every other surface term. - MRF K-profile: development's ghost-cell guard and notes, with the branch's k < ksurf (no diffusivity inside a solid) condition. - YSUNew K-profile: development's rho guard, plus the branch's ksurf and effective Obukhov length. - VH96 shear correction (new on development, off by default): use the effective u*. On columns without immersed cells it equals u*, so runs without erf.pbl_ib_aware are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ents - M_PI is undefined on MSVC (C2065 in ERF_IBSEBSolar.H, which failed the WIN32 jobs of the previous head); the solar geometry and the reports use PI from ERF_Constants.H. - The reduced face counts were `long`, which is 32-bit on Windows and has no ReduceLongSum overload; they are amrex::Long. - init_ibseb() aborts on a non-uniform vertical grid (the face areas, heights and the ray cast take the level's constant cell sizes) and on erf.regrid_int > 0 (the face list is built once from the blanking); ibseb_report() bounds its level loop by the face sets it holds. - Inputs.rst states the grid and regrid requirements. - The comments describe the routines instead of the development phases. Verified: a Debug build with MPI, no FFT, all warnings, assertions and bound checking is warning-free and runs the 471 unit tests; the Phase 6 closure, external-flux and restart checks and the PBL_IBAware regtest pass on 2 ranks; the six touched translation units compile in single precision. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The face detection takes every solid column of the blanking for a building, so with erf.terrain_type = ImmersedForcing the terrain would be put under the balance as well, which the documentation says it is not. init_ibseb() now requires erf.buildings_type = ImmersedForcing and aborts on terrain by immersed forcing; Inputs.rst says so. IBSEB_Cube runs the Phase6_Prognostic cube (32x32x16, 40 steps, fixed sun, prognostic skin, slab, heat flux into the air) and compares the plotfile with its face diagnostics against a gold; nothing in CI exercised the balance before. Passes in a Debug build with MPI and no FFT in 57 s on 2 ranks; a run with a different albedo fails the comparison. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Resolves the MRF conflict with erf-model#3972 (PBL passes on the tile work box): the immersed-boundary work arrays of MRF and YSUNew now cover the same grown tile box as the PBLH passes, and the blanking's halo is part of the smoothing halo check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Development merged erf-model#3972 and erf-model#3992, which put the PBL passes on a grown tile work box; the IB-aware per-column arrays now live on that box too (merge commit), and this registers PBL_IBAware_MRF_Tiling and PBL_IBAware_YSUNew_Tiling in the tiling-parity harness: the 40 m cube under a heated surface layer with a capped mixed layer, so the PBL height differs between the roof columns and open ground. Tiled and untiled runs agree bitwise for both schemes. The solar header takes PI from ERF_NumericalConstants.H, as the constants split of erf-model#3994 asks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…at they test Source/ImmersedBoundarySEB/IBSEB_DEVELOPMENT.md is a development log; it is removed from the branch and listed in .gitignore so a local copy stays private, and nothing in the code or the documentation refers to it. The cases under Exec/CanonicalTests/SEB are renamed after what they test (FaceStorage, Shortwave, Longwave, SensibleHeat, SlabConduction, PrognosticSkin, IsolatedBuilding, WallFunction, BuildingSet) and their READMEs, decks, scripts and the theory page no longer describe the work as numbered phases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…start of the flux, slab bound erf.if_snap_partial_cells: with every solid cell at 1 the roof test (t_blank < t_blank_below) could not fire, the side-wall cells carried the wall law and the interior drag together, and the thermal wall tests (t_blank < neighbour) could not fire either. Under the snap the roof is the top solid cell, a cell carrying a wall law or the roof drag is not an interior cell, and the thermal wall tests read the neighbour blanking; with the snap off every mask is as before. erf.pbl_ib_aware: MRF measured its PBL height from the top of the immersed column while YSUNew stored the absolute height; both now store the absolute height (pblh, Lturb), so every consumer of get_pblh() and the balances z_i_mode = pblh see one convention. The bulk Richardson depth returns heights above the domain bottom in both branches (the fallback was ProbHi, wrong when ProbLo(2) != 0). The sensible flux is part of the checkpointed face state (six slots of 2 + n_layers), and the initial diagnostics of a restart no longer overwrite it, so the Deardorff convective scale restarts exactly; the WallFunction case checks it (step-599 dumps identical to 0.0 through a checkpoint at step 300). SLAB_MAX_LAYERS is 32 (was 256, 8 kB of stack per GPU thread) and n_slab_layers is checked against it. PartialCells rerun with the new masks: 19000 steps, theta within 0.000 K, max |w| 0.48 m/s (was 0.60), wake -0.33 m/s; PBL_IBAware, the tiling parity tests, IBSEB_Cube, the PrognosticSkin restart and 511 unit tests pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The 6 h run with the fixed masks and the checkpointed flux: roof means at 11:00 move by 0.3 to 0.5 K (slab 320.2, north block 332.2, cube 323.7, far block 332.1 K), w* 0.10-0.73 m/s on the sunlit faces; every check passes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ory page ERF_IBSEBSolar.H carried a copy of the ERF-Radiation solar geometry so the branch had no dependency on it; with erf-model#3950 merged the five functions are wrappers around Source/Radiation/ERF_SolarGeometry.H (same Spencer coefficients, hour angle, zenith, azimuth and Earth-Sun distance). The sunrise case (10800 steps, sun_mode = solar) is bitwise identical before and after: every report line and the final face dumps. The longwave kernel uses the shared Stefan-Boltzmann constant instead of a local constexpr captured into the device lambda; the reserved two_stream provider note names erf.radiation_model; a figure caption in the theory page ran into the next paragraph (Sphinx error). Checks from the audit notes: the seven touched units compile in single precision; no MSVC long/M_PI patterns in the diff; the IBSEB_Cube and tiling decks leave no unused ParmParse keys; IBSEB_Cube split into 8-cell boxes in x and y agrees with its gold to 2e-10 (a z split is refused by the implicit vertical diffusion guard of erf-model#3998); the Debug tree of the merged head builds without warnings and passes 553 unit tests and the three IBSEB CTests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…length, dead input, validation, plotfile docs Source/ImmersedBoundarySEB has a Make.package and is wired into Exec/Make.ERF, so the GNU Make build compiles and links again (Exec: make COMP=llvm USE_NETCDF=FALSE builds ERF3d.llvm.TEST.MPI.ex). With erf.pbl_ib_aware the YSUNew K-profile used the height above the domain bottom as the mixing length; it is now the height above the column own surface (zq_kp1 - zib) in the unstable and stable branches, and the stable z/L ratio is formed the same way. On the heated tiling deck the first cell above the 40 m roof drops from Kmv 3.69 to 0.86 kg/m/s; without immersed cells nothing changes (zib = 0). erf.ibseb.utc_offset_hours cancelled algebraically in the hour angle and had no effect; it is removed (the sun follows time_zero_utc_s in UTC and longitude_deg), a deck that sets it aborts, and the case decks no longer carry it. latitude_deg, longitude_deg and day_of_year are range-checked instead of silently clamped; Inputs.rst states the ranges. The eight ibseb_* plotfile variables are in Plotfile3DReference.rst, with the erf.ibseb.enable gate and the per-cell face-mean meaning. Verified: every erf.ibseb input perturbed on a 6-step run moves the face state (44 inputs, the skin bounds once set inside the faces range); the three rejections abort with their messages; PBL_IBAware, both tiling parity tests, IBSEB_Cube and 553 unit tests pass; Debug build warning-free; Sphinx builds the two pages clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ed face blanking, smoothing frame, report steps, restart width - The five solar functions live in ERF_IBSEBSolar.H again (erf-model#4004 replaced the shared header with the CESM orbital form, which has no azimuth); bitwise identical to the removed header over 1e6 samples; the header includes what it uses. - With erf.if_snap_partial_cells the momentum kernels take a face's blanking from the two cells it joins (solid when either is), in both builds; a face between a solid and a fluid cell is wall-normal and gets the drag toward zero, a face between two solid cells the roof or wall law of its row or the interior drag, never both; the snap off is bit-identical; the terrain kernels no longer read the switch (their wall law is weighted by the fluid fraction). - MRF smooths the PBL height in the absolute frame (zib on before ApplyPBLHSmoothing, off after) and both MRF and YSUNew keep the smoothed height at or above the column's own floor; new gold CTest PBL_IBAware_MRF_Smoothing (the previous binary fails it, Lturb 10 percent off). - ibseb_report takes the number of completed steps (post_timestep's nstep + 1), so the step-0 report is written once and the rows land after csv_int, 2 csv_int, ...; the case scripts and READMEs follow. - A restart whose erf.ibseb.n_slab_layers differs from the checkpoint's aborts naming both (VisMF header width); FaceStorage runs that negative case. - Shortwave/Longwave rank-independence checks: geometry, view, shadow and shortwave exact, atmosphere columns to 1e-9 (the dump moved from step 1 to step 2). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…k slab within the 32-layer bound, reference outputs rerun - SlabConduction's thick-slab deck used 200 layers, outside the 32-layer bound set in the first review round; it is now 32 mm in 1 mm layers over 20 s (the wave from the interior travels 4 mm and the skin side stays untouched), README and checker text follow. - PartialCells (max |w| 0.49 m/s, wake -0.47 m/s), BuildingSet (roof means about 2 K lower with the wall-normal faces damped, every roof unstable), PrognosticSkin (step labels, 201 dumps) and FaceStorage reference outputs rerun with the new binary. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… snap, checkpoint field on column blocks, column map over the built box, vector shear, slab guard - With erf.if_snap_partial_cells the boundary solid faces take the full log-law target (the partial-cell weight 1 - t_blank had made them no-slip); PartialCells now requires that erf.if_z0 changes the flow (0.20 m/s after 400 steps; the previous binary gave exactly 0). - IBSEBState lives on 8 x 8 column blocks up to the highest face-owning cell around the buildings, with a transfer layer (this rank's grids cut by the blocks, AllGatherBoxes, ParallelCopy) so a restart works on any rank count; init_ibseb aborts on a checkpoint of other buildings; FaceStorage restarts the 4-rank checkpoint on 1 rank and rejects a rotated height map. On the 128^3 FaceStorage level the field covers 15616 of 2097152 cells (2.1 MB against 288 MB). - The ray cast's column map, labels and building heights cover the bounding box of the built columns; the two ray walks look a column up through ibseb::column_top(), open ground outside; the shortwave and longwave dumps are unchanged. - The bulk Richardson depth uses |U(z) - U_1|^2 of the wind vector; slab_skin_response guards N like advance_slab_dirichlet. - Reference outputs rerun: PartialCells (w 0.57, wake -0.41), BuildingSet (roof means 319.6/331.1/323.0/331.0 K), PrognosticSkin closure lines. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follows the fourth review of erf-model#3960, which left the checkpoint scaling with the built volume and the ray cast's column map replicated at full width on every rank. - IBSEBState goes on 4 x 4 column blocks clipped to the k-range that owns faces, instead of 8 x 8 blocks running from the ground to the roof. Face- owning cells are the fluid cells against solid, a one-cell shell, so a block over the interior of a wide footprint now keeps only the layer above the roof while a block on a wall still spans its height. - The field carries one slot per face of a cell, numbered in the (dir, side) order build() already walks, rather than a fixed six; its width is the largest face count on any cell of the level, which build() reproduces from the same blanking on restart. Three nodal fields would have been the obvious way to drop the slot index, but nodal block boxes share their faces at block boundaries and the ParallelCopy of save_state() would have two destinations for one node with only one of them written. On FaceStorage the field is now 32 boxes over 7872 cells at 18 components against 15616 cells at 36, a quarter of the size; n_slots is 3 there because three faces meet on a cell at the rim, and it is never worse than the six the layout used to assume. - The slot numbering is relative, so it would shift if a cell's face list depended on the decomposition. It cannot -- the blanking carries ComputeGhostCells() + 2 ghost cells -- and build() now asserts the ghost layer it rests on. - The column map of the ray cast drops from three Real arrays plus a mask to one int array of the highest solid cell of each column: colmax was redundant (a column is solid exactly when its top is not -1), h_col_top held the same fact again as a height, and ibseb::column_top() now builds the height from the index. That is 4 bytes per built column per rank instead of 24, and one all-reduce instead of two. The flood-fill label and stack are scoped so they free before the per-face uploads. - Docs and the FaceStorage README follow the new layout, and the theory notes record what still scales with the built area. FaceStorage, Shortwave and Longwave pass on 1 and 4 ranks: the checkpoint round trip and its redistribution onto another rank count, both restart aborts, 0/2616 shadow mismatches against the checkers' independent ray cast at both zenith angles with the recorded 738 and 644 shadowed faces, and 0/2616 view-fraction mismatches against the independent hemisphere sampling. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Surface energy balance on immersed-boundary building faces (erf.ibseb), with immersed-boundary fixes for the forcing and the MRF/YSUNew schemes
…f-model#3960 The 24 h day passes unchanged; the table now starts from the initial state (step 0) and two last digits move with the relabelled dumps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
std::max(Real, double) does not deduce when Real is float (the Mesh SINGLE CI jobs); the height is formed in double and cast, which is identical in double precision. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
IBSEB: single-precision build of ERF_IBFaceSet.cpp, IsolatedBuilding reference table
… into ERF-Fire Brings the immersed-boundary surface energy balance module and its single-precision follow-up. Conflicts were argument lists only: ComputeTurbulentViscosity and ComputeDiffusivityMRF take upstream's terrain_blank and keep the fork's qheating_rates and Q_fire_atm (single call site each, updated); the MRF work arrays keep both the fire buoyancy flux and the smoothing floor; the MRF Scalar_v clamp and dust Schmidt scaling stay, with the length scale stored as the absolute height; the Sphinx index lists both theory pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With erf.pbl_ib_aware the MRF passes read the density, u*, theta* and the surface-layer temperature at the first fluid cell of an immersed column (ksurf) instead of the ground surface layer's values, which come from cells inside the solid there. The fire boost of w* (erf.pbl_mrf_fire_thermal_excess) still read the ground values; it now takes the same effective scales. Without erf.pbl_ib_aware the effective scales are copies of the ground ones and ksurf is the bottom cell, so fire runs are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two READMEs pointed at a plan file that is not in the tree. BuildingSet now points at the PartialCells regression test for the negative density without the snap; IsolatedBuilding names the two inputs that add the stability functions and the convective velocity scale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Summary
Merges
upstream/developmentinto ERF-Fire to bring the immersed-boundary surface energy balance (erf-model#3960) and its single-precision follow-up (erf-model#4022). Only those two PRs are new on development since the last sync (#419). ERF-Fire's #425 is merged in as well, so the branch is current.Not included
The branch stops at development's erf-model#4022 merge (
e44fe5e02). Development has since merged three more PRs; they come with the next sync:ERF_MRI.H,ERF_SlowRhsPre.cppandERF_SlowRhsPost.cppand regenerates theDensityCurrent_anelasticgold files. ERF-Fire has its own anelastic changes in overlapping files, so it needs its own merge and a full regression run.Conflict resolution
terrain_blanktoComputeTurbulentViscosityandComputeDiffusivityMRF(the MRFerf.pbl_ib_awarepath). The fork had addedQ_fire_atm. Both functions now takeqheating_rates,terrain_blankandQ_fire_atm. Each has a single call site, updated to match. Every argument is aMultiFab*, so the order was checked by hand at both call sites.kbfs_fire_fab) and upstream's smoothing floor (pblh_floor_fab) are kept.Scalar_vclamp and dust Schmidt scaling stay. The length scale is stored as the absolute height (pblh_corr + zib), as upstream now does.theory/Fire.rstandtheory/ImmersedBoundarySEB.rst.Two small commits on top
erf.pbl_ib_aware, the MRF passes read an immersed column's surface scales at its first fluid cell. The fork's fire boost still read the ground surface layer (density atklo,u_star,t_star,t10av), which comes from cells inside the solid there. It now reads the same effective scales. Withouterf.pbl_ib_awarethose scales are copies of the ground values and the surface cell isklo, so fire runs are unchanged. No deck yet combines fire witherf.pbl_ib_aware; the fire-to-building coupling follow-up adds one.Test plan
ctest -L regressionon that build: 148 of 148 pass. This includes:IBSEB_CubeandPBL_IBAware_MRF_Smoothinggold tests;PBL_IBAware_MRF_TilingandPBL_IBAware_YSUNew_Tiling;ABL_MRF_TilingandABL_MRF_Tiling_Smooth;FireMrfThermalExcess, which exercises the fire w* boost withoutpbl_ib_aware;ERF_ComputeDiffusivityMRF.cpp,ERF_ComputeTurbulentViscosity.cpp,ERF_AdvanceDycore.cpp): clean.git diff --checkon the resolved and new lines: clean. The trailing whitespace git reports comes from upstream's own gold headers and READMEs, which are left as merged.SEB/FaceStorage,SEB/Shortwave,SEB/Longwave,SEB/SensibleHeat,SEB/SlabConduction,SEB/PrognosticSkinandSEB/WallFunction;RegTests/ImmersedForcingTest/PBL_IBAwareandRegTests/ImmersedForcingTest/PartialCells.resid_maxexcluded. The upstream run ofWallFunctionwas on 2 ranks; it differs only in the tenth digit of the neutral deck's 0.07 W/m2 mean, and the 4-rank run here equals the README's 4-rank reference exactly.SEB/IsolatedBuilding(2 h) andSEB/BuildingSet(1 h) were not rerun on this merge. Their code is identical to upstream's, where both were rerun, and the one source change specific to this merge (the MRF fire boost) is inactive in them.🤖 Generated with Claude Code