Parameters.jl

repository·master·Indexed 19 days ago

https://github.com/mauro3/parameters.jl

A Julia package for handling numerical-model parameters. It provides the `@with_kw` macro for defining structs and NamedTuples with keyword constructors and default values, the `@consts` macro for bulk constant declaration, and integration with UnPack.jl via `@unpack` and `@pack!` for moving data between composite types, dictionaries, and local variables.

Tokens
2.4K
Snippets
10
Records
13
Agent score
62%

What's inside Parameters.jl

  1. Overview of Parameters.jl features

    master

    Parameters.jl is designed for handling numerical-model parameters and provides several key capabilities:

    • Keyword Constructors: Use @with_kw to define types with default values and keyword-based instantiation.
    • Assertions: Allows defining assertions on field values directly within the type definition.
    • Type-based Defaults: Supports creating type instances that inherit defaults from another existing type instance.
    • Packing/Unpacking: Provides @unpack_* (specific to a type) and generic @unpack / @pack! macros for moving data between types and variables.
    • Constant Definition: Use @consts for bulk constant declaration.
  2. Core features of Parameters.jl

    master

    Parameters.jl is designed for handling numerical-model parameters and provides two primary capabilities:

    1. Keyword type constructors with default values: Allows defining types where parameters can be passed as keywords with predefined defaults.
    2. Unpacking and packing: Provides utilities to convert between composite types (structs) and dictionaries (Dict).
  3. Enforce constraints and parameter interdependence

    master

    You can define invariants and relationships between fields directly within the @with_kw block:

    1. Assertions: Use @assert (or @smart_assert from SmartAsserts.jl) inside the type definition to enforce constraints. These assertions are moved to the constructor.
    2. Interdependence: Fields can depend on the values of other fields (e.g., c = a + b).
    3. Default Types: Use @deftype to set a default type for all fields in the struct, reducing boilerplate. You can override this default for specific fields by providing an explicit type.
    # Interdependence and Assertions
    @with_kw struct Para{R<:Real}
        a::R = 5
        b::R
        c::R = a + b
        @assert a > 0
    end
    
    # Default Types (@deftype)
    @with_kw struct Para2{R<:Real} @deftype R
        a = 5
        b::Any
        c = a + b
        d::Int = 4 # Overrides @deftype
    end
  4. Use type-specific (un)pack macros with caution

    master

    The @with_kw macro automatically generates type-specific macros: @unpack_TypeName, @pack_TypeName!, and @pack_TypeName. These macros unpack or pack all fields of the specified type at once.

    Warning: These are considered dangerous because:

    • They can shadow local variables or input arguments if they share names with type fields.
    • They can hijack existing names (e.g., adding a field pi to a type will shadow Base.pi).
    • They only work with actual fields, not properties.

    Recommendation: Use the generic @unpack and @pack! macros from UnPack.jl instead of these auto-generated type-specific versions.

    # Auto-generated by @with_kw for type 'Para'
    @unpack_Para pa
    @pack_Para! pa  # Only for mutables
    pa2 = @pack_Para # Creates a new instance (for immutables)
  5. Performance considerations for keyword constructors

    master

    While keyword constructors are convenient for setting up parameters, they are currently slower in Julia than standard positional constructors.

    Best Practice:

    • Use keyword constructors for high-level configuration and setup.
    • Do not use keyword constructors in performance-critical 'hot inner loops'.
    • For performance-critical code, use the standard positional constructor provided by the package.
  6. Unpack and Pack fields using `@unpack` and `@pack!`

    master

    To work with the fields of a type (especially when passing them into functions), use the @unpack and @pack! macros from UnPack.jl. These are the preferred methods as they are generic and work with @with_kw structs, named tuples, modules, and dictionaries.

    • @unpack field1, field2 = object is equivalent to field1, field2 = object.field1, object.field2.
    • @pack! object = field1 is equivalent to object.field1 = field1 (works on mutable objects).
    using UnPack
    
    @with_kw mutable struct MPara{R<:Real}
        a::R = 5
        b::R
    end
    
    pa = MPara(b = 7)
    
    function fn(pa::MPara)
        @unpack a, b = pa
        b = 77
        @pack! pa = b
    end
  7. Create Named Tuple constructors with `@with_kw`

    master

    The @with_kw macro can be used to decorate a named tuple expression, producing a constructor that returns a named tuple with default values. The resulting constructor is not type-locked, meaning you can pass different types of values to it.

    # Define a named tuple constructor with defaults
    MyNT = @with_kw (f = x -> x^3, y = 3, z = "foo")
    
    # Use the constructor
    nt = MyNT(f = x -> x^2, z = :foo)
    # Result: (f = #12, y = 3, z = :foo)
    
    # It is not type-locked
    nt2 = MyNT(f = "string")
    # Result: (f = "string", y = 3, z = "foo")
  8. Unpack fields from composite types using @unpack

    master

    The @unpack macro allows you to extract multiple fields from a composite type (like a struct) into individual local variables. This is a concise alternative to manual field access.

    Note: @unpack and @pack! are generic macros that work with any types via UnPack.jl.

    struct B
        a
        b
        c
    end
    
    @unpack a, c = B(4, 5, 6)
    # This is equivalent to:
    # BB = B(4, 5, 6)
    # a = BB.a
    # c = BB.c
  9. Create NamedTuples with default values using @with_kw

    master

    You can use @with_kw to define a constructor for NamedTuples that includes default values. This is useful for creating templates for configuration or parameter sets.

    using Parameters
    
    MyNT = @with_kw (x = 1, y = "foo", z = :(bar))
    
    MyNT()           # Returns (x = 1, y = "foo", z = :bar)
    MyNT(x = 2)      # Returns (x = 2, y = "foo", z = :bar)
  10. Define types with keyword constructors using @with_kw

    master

    The @with_kw macro allows you to define structs or NamedTuples with default values for their fields. This creates a keyword constructor that lets you instantiate the type by specifying only the fields that differ from the defaults.

    Important Performance Note: Keyword constructors are currently slower in Julia than standard positional constructors. Avoid using them in performance-critical hot inner loops. For such cases, use the standard positional constructor provided by the type.

    using Parameters
    
    @with_kw struct A
               a::Int = 6
               b::Float64 = -1.1
               c::UInt8
           end
    
    # Usage:
    A(c=4)          # Uses defaults for a and b
    A(c=4, a=2)     # Overrides a, uses default for b
  11. Define types with default values using `@with_kw`

    master

    Use the @with_kw macro to define structs with default values. This automatically generates a keyword constructor, allowing you to instantiate the type with specific values or rely on defaults. It also provides an enhanced show method for better inspection of the fields.

    Key features:

    • Keyword Construction: Specify only the fields you want to change.
    • Type Parameters: Supports parametric types (e.g., PhysicalPara{R}).
    • Modification: Create new instances based on existing ones using the semicolon syntax: Type(existing_instance; field = new_value).
    • Positional Construction: The standard positional constructor is still available and is recommended for performance in hot inner loops.
    • Custom Show: If you want to avoid the enhanced show method (to prevent method re-definition warnings), use @with_kw_noshow instead.
    using Parameters
    
    @with_kw struct PhysicalPara{R}
        rw::R = 1000.0
        ri::R = 900.0
        L::R = 3.34e5
        g::R = 9.81
        cw::R = 4220.0
        day::R = 24*3600.0
    end
    
    # Usage
    pp = PhysicalPara()                      # Defaults
    pp_f32 = PhysicalPara{Float32}()         # Explicit type
    pp2 = PhysicalPara(cw = 77.0, day = 987.0) # Keywords
    pp3 = PhysicalPara(pp2; cw = .11e-7)      # Based on previous
    pp4 = PhysicalPara(1, 2, 3, 4, 5, 6)    # Positional