ESCOMP / ESCOMP/CAM-SIMA

aerosol_optics_core.F90: aero_optics object churn: persistent data design

Open
#537 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
No language data
Stars
9
Forks
22
Avg merge
8d 5h
Merged PRs (30d)
4

Description

### What is the feature/what would you like to discuss?

This applies to the same named file in both CAM and CAM-SIMA.

During the review of #504, @nusbaume originally wrote:
> Another topic for long-term discussion (not this PR), but I am guessing that constantly allocating and then deallocating these aero_optics objects is likely a non-trivial performance hit, especially if we are doing it every physics timestep. While just allocating them once and holding them forever in memory might not be an ideal solution either, I wonder if there is an intermediate option (e.g. a generic, pre-allocated object, or a memory pool) that we can use instead? Anyways, just wanted to bring up the possibility.

`claude-fable:5` wrote after we scoped the size of a potential change:
> The real cost per radiation step is 2×nbins object constructions (SW and LW each build the same object), where each constructor allocates two (ncol,pver) work arrays (wrh/krh or wmu/kmu) and recomputes the RH interpolation weights from state. So a persistent design saves the allocations and halves the weight computation.
> My recommendation:
>
> - Split each concrete constructor into two phases: an init-time part that binds the physprop table pointers and allocates the workspace once, and a per-timestep update(relh/sulfwtpct/geometric_radius, ...) method containing the current constructor tail (the weight computation, verbatim).
> - Cache the objects on the persistent aerosol_state instance (an optics_(:) component sized nbins, lazily built). That's the key to CAM thread-safety: state objects are already per-chunk in CAM (same reason num_work_ lives there), so the cache inherits chunk isolation for free.
> - create_aerosol_optics_object becomes get-or-create + update; sw_props/lw_props and all arithmetic are untouched, so it's b4b by construction (weights are pure functions of the same inputs — computing them once vs. twice per step is bit-identical). The state destructor deallocates the cache.
> - First increment can even keep update called in both aerosol_optics_sw_bin and aerosol_optics_lw_bin (exactly mirroring today), then drop the second call later.
>
> **The alternative (a shared scratch pool that wrh/krh point into) is a smaller diff but keeps the per-call construction, doesn't dedupe the weight computation, and has aliasing hazards if two objects ever coexist** — I'd skip it. The split-constructor version is mechanical across the ~7 optics classes plus aerosol_optics_core.

### Is there anyone in particular you want to be part of this conversation?

@nusbaume @fvitt

### Will this change (regression test) answers?

No

### Will you be implementing this enhancement yourself?

Any Software Engineer can do this

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with aerosol_optics_core.F90 in both CAM and CAM-SIMA, then trace create_aerosol_optics_object and the aerosol_optics_sw_bin and aerosol_optics_lw_bin entry points. Review how aerosol_state persists per chunk and how the optics classes construct their work arrays. Done means cached objects are updated across timesteps without repeated allocation, while regression answers remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
fortran
Domain
performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.