@@ -520,11 +520,11 @@ end
520
520
# Grouped structs to reduce AtmosModel type parameters
521
521
522
522
"""
523
- AtmosHydrology
523
+ AtmosWater
524
524
525
525
Groups moisture-related models and types.
526
526
"""
527
- Base. @kwdef struct AtmosHydrology {MM, PM, CM, NCFM, CCDPS}
527
+ Base. @kwdef struct AtmosWater {MM, PM, CM, NCFM, CCDPS}
528
528
moisture_model:: MM = nothing
529
529
precip_model:: PM = nothing
530
530
cloud_model:: CM = nothing
@@ -613,7 +613,7 @@ Base.@kwdef struct AtmosSurface{ST, SM, SA}
613
613
end
614
614
615
615
# Add broadcastable for the new grouped types
616
- Base. broadcastable (x:: AtmosHydrology ) = tuple (x)
616
+ Base. broadcastable (x:: AtmosWater ) = tuple (x)
617
617
Base. broadcastable (x:: AtmosForcing ) = tuple (x)
618
618
Base. broadcastable (x:: AtmosRadiation ) = tuple (x)
619
619
Base. broadcastable (x:: AtmosAdvection ) = tuple (x)
@@ -639,10 +639,9 @@ struct AtmosModel{H, F, R, A, TC, GW, HD, VD, SP, SU, NU}
639
639
disable_surface_flux_tendency:: Bool
640
640
end
641
641
642
- # BACKWARD COMPATIBILITY FOR AtmosModel
643
642
# Map grouped struct types to their names in AtmosModel struct
644
643
const ATMOS_MODEL_GROUPS = (
645
- (AtmosHydrology , :hydrology ),
644
+ (AtmosWater , :hydrology ),
646
645
(AtmosForcing, :forcing ),
647
646
(AtmosRadiation, :radiation ),
648
647
(AtmosAdvection, :advection ),
@@ -653,17 +652,12 @@ const ATMOS_MODEL_GROUPS = (
653
652
(AtmosNumerics, :numerics ),
654
653
)
655
654
656
- # Auto-generate map from property_name to group_field
657
- # Use let closure to avoid polluting module scope with temporary variables
658
- const GROUPED_PROPERTY_MAP = let
659
- property_map = Dict {Symbol, Symbol} ()
660
- for (group_type, group_field) in ATMOS_MODEL_GROUPS
661
- for property in fieldnames (group_type)
662
- property_map[property] = group_field
663
- end
664
- end
665
- property_map
666
- end
655
+ # Auto-generate map from property_name to group_field
656
+ const GROUPED_PROPERTY_MAP = Dict {Symbol, Symbol} (
657
+ property => group_field for
658
+ (group_type, group_field) in ATMOS_MODEL_GROUPS for
659
+ property in fieldnames (group_type)
660
+ )
667
661
668
662
# Forward property access: atmos.moisture_model → atmos.moisture.moisture_model
669
663
# Use ::Val constant for @generated compile-time access
@@ -729,7 +723,7 @@ This constructor provides sensible defaults for a minimal dry atmospheric model
729
723
730
724
All model components are automatically organized into appropriate grouped sub-structs
731
725
internally:
732
- - [`AtmosHydrology `](@ref)
726
+ - [`AtmosWater `](@ref)
733
727
- [`AtmosForcing`](@ref)
734
728
- [`AtmosRadiation`](@ref)
735
729
- [`AtmosAdvection`](@ref)
@@ -779,7 +773,7 @@ The default AtmosModel provides:
779
773
780
774
# Available Structs
781
775
782
- ## AtmosHydrology
776
+ ## AtmosWater
783
777
- `moisture_model`: DryModel(), EquilMoistModel(), NonEquilMoistModel()
784
778
- `precip_model`: NoPrecipitation(), Microphysics0Moment(), Microphysics1Moment(), Microphysics2Moment()
785
779
- `cloud_model`: GridScaleCloud(), QuadratureCloud(), SGSQuadratureCloud()
@@ -834,76 +828,9 @@ The default AtmosModel provides:
834
828
- Property access works both ways: `model.moisture_model` and `model.hydrology.moisture_model`
835
829
"""
836
830
function AtmosModel (; kwargs... )
837
- # TODO : Break this into separate functions for defaults and kwarg processing
838
- # this is hard to understand and maintain
831
+ group_kwargs, atmos_model_kwargs = _partition_atmos_model_kwargs (kwargs)
839
832
840
- # Set defaults that create a minimal viable atmospheric model
841
- default_args = (
842
- moisture_model = DryModel (),
843
- precip_model = NoPrecipitation (),
844
- cloud_model = GridScaleCloud (),
845
- surface_model = PrescribedSurfaceTemperature (),
846
- sfc_temperature = ZonallySymmetricSST (),
847
- insolation = IdealizedInsolation (),
848
- numerics = AtmosNumerics (
849
- energy_upwinding = Val (:first_order ),
850
- tracer_upwinding = Val (:first_order ),
851
- edmfx_upwinding = Val (:first_order ),
852
- edmfx_sgsflux_upwinding = Val (:none ),
853
- test_dycore_consistency = nothing ,
854
- limiter = nothing ,
855
- diff_mode = Explicit (),
856
- ),
857
-
858
- # Top-level
859
- disable_surface_flux_tendency = false ,
860
- )
861
-
862
- # Process keyword arguments: categorize into grouped types (e.g., hydrology -> AtmosHydrology)
863
- # vs direct AtmosModel fields (e.g., hyperdiff, numerics). Use GROUPED_PROPERTY_MAP to organize grouped types
864
- # into appropriate grouped structs, then construct AtmosModel with all top-level fields.
865
- group_kwargs = Dict {Symbol, Dict{Symbol, Any}} ()
866
- for (_, group_field) in ATMOS_MODEL_GROUPS
867
- group_kwargs[group_field] = Dict {Symbol, Any} ()
868
- end
869
-
870
- # Kwargs for direct AtmosModel fields (hyperdiff, numerics, vert_diff, disable_surface_flux_tendency)
871
- atmos_model_kwargs = Dict {Symbol, Any} ()
872
-
873
- # Merge defaults with kwargs
874
- all_kwargs = merge (default_args, kwargs)
875
-
876
- # Sort kwargs into a hierarchy of dicts
877
- for (key, value) in all_kwargs
878
- if haskey (GROUPED_PROPERTY_MAP, key)
879
- group_field = GROUPED_PROPERTY_MAP[key]
880
- group_kwargs[group_field][key] = value
881
- elseif key in fieldnames (AtmosModel)
882
- atmos_model_kwargs[key] = value
883
- else
884
- available_grouped = sort (collect (keys (GROUPED_PROPERTY_MAP)))
885
- available_direct = sort ([
886
- fn for fn in fieldnames (AtmosModel) if fn ∉ [
887
- :hydrology ,
888
- :forcing ,
889
- :radiation ,
890
- :advection ,
891
- :turbconv ,
892
- :gravity_wave ,
893
- :sponge ,
894
- :surface ,
895
- ]
896
- ])
897
- available_all = [available_grouped; available_direct]
898
- error (
899
- " Unknown AtmosModel argument: $key . " *
900
- " Available arguments:\n " *
901
- join (available_all, " \n " ),
902
- )
903
- end
904
- end
905
-
906
- moisture = AtmosHydrology (; group_kwargs[:hydrology ]. .. )
833
+ moisture = AtmosWater (; group_kwargs[:hydrology ]. .. )
907
834
forcing = AtmosForcing (; group_kwargs[:forcing ]. .. )
908
835
radiation = AtmosRadiation (; group_kwargs[:radiation ]. .. )
909
836
advection = AtmosAdvection (; group_kwargs[:advection ]. .. )
@@ -946,6 +873,88 @@ function AtmosModel(; kwargs...)
946
873
)
947
874
end
948
875
876
+ const _DEFAULT_ATMOS_MODEL_KWARGS = (
877
+ moisture_model = DryModel (),
878
+ precip_model = NoPrecipitation (),
879
+ cloud_model = GridScaleCloud (),
880
+ surface_model = PrescribedSurfaceTemperature (),
881
+ sfc_temperature = ZonallySymmetricSST (),
882
+ insolation = IdealizedInsolation (),
883
+ numerics = AtmosNumerics (
884
+ energy_upwinding = Val (:first_order ),
885
+ tracer_upwinding = Val (:first_order ),
886
+ edmfx_upwinding = Val (:first_order ),
887
+ edmfx_sgsflux_upwinding = Val (:none ),
888
+ test_dycore_consistency = nothing ,
889
+ limiter = nothing ,
890
+ diff_mode = Explicit (),
891
+ ),
892
+
893
+ # Top-level
894
+ disable_surface_flux_tendency = false ,
895
+ )
896
+
897
+ """
898
+ _partition_atmos_model_kwargs(kwargs)
899
+
900
+ Partition the given kwargs into grouped and direct kwargs matching the AtmosModel struct.
901
+
902
+ Helper function for the AtmosModel constructor.
903
+ """
904
+ function _partition_atmos_model_kwargs (kwargs)
905
+
906
+ # Merge default minimal model arguments with given kwargs
907
+ all_kwargs = merge (_DEFAULT_ATMOS_MODEL_KWARGS, kwargs)
908
+
909
+ # group_kwargs contains a Dict for each group in ATMOS_MODEL_GROUPS
910
+ group_kwargs = Dict (map (ATMOS_MODEL_GROUPS) do (_, group_field)
911
+ group_field => Dict {Symbol, Any} ()
912
+ end )
913
+
914
+ # Sort kwargs into a hierarchy of dicts matching the AtmosModel struct
915
+ atmos_model_kwargs = Dict {Symbol, Any} ()
916
+ unknown_args = Symbol[]
917
+
918
+ for (key, value) in pairs (all_kwargs)
919
+ if haskey (GROUPED_PROPERTY_MAP, key)
920
+ group_field = GROUPED_PROPERTY_MAP[key]
921
+ group_kwargs[group_field][key] = value
922
+ elseif key in fieldnames (AtmosModel)
923
+ atmos_model_kwargs[key] = value
924
+ else
925
+ push! (unknown_args, key)
926
+ end
927
+ end
928
+
929
+ # Throw error for all unknown arguments at once
930
+ if ! isempty (unknown_args)
931
+ _throw_unknown_atmos_model_argument_error (unknown_args)
932
+ end
933
+
934
+ return group_kwargs, atmos_model_kwargs
935
+ end
936
+
937
+ """
938
+ _throw_unknown_atmos_model_argument_error(unknown_args)
939
+
940
+ Throw a helpful error message for unknown AtmosModel constructor arguments.
941
+ """
942
+ function _throw_unknown_atmos_model_argument_error (unknown_args)
943
+ n_unknown = length (unknown_args)
944
+ plural = n_unknown > 1 ? " s" : " "
945
+
946
+ # All valid arguments: forwarded properties + direct AtmosModel fields
947
+ available_forwarded = sort (collect (keys (GROUPED_PROPERTY_MAP)))
948
+ available_direct = sort (collect (fieldnames (AtmosModel)))
949
+ available_all = sort (unique ([available_forwarded; available_direct]))
950
+
951
+ error (
952
+ " Unknown AtmosModel argument$plural : $(join (unknown_args, " , " )) . " *
953
+ " Available arguments:\n " *
954
+ join (available_all, " \n " ),
955
+ )
956
+ end
957
+
949
958
# Convenience constructors for common configurations
950
959
951
960
"""
0 commit comments