back to home

JuliaPhysics / DynamicQuantities.jl

Efficient and type-stable physical quantities in Julia

View on GitHub
154 stars
28 forks
36 issues
Julia

AI Architecture Analysis

This repository is indexed by RepoMind. By analyzing JuliaPhysics/DynamicQuantities.jl in our AI interface, you can instantly generate complete architecture diagrams, visualize control flows, and perform automated security audits across the entire codebase.

Our Agentic Context Augmented Generation (Agentic CAG) engine loads full source files into context on-demand, avoiding the fragmentation of traditional RAG systems. Ask questions about the architecture, dependencies, or specific features to see it in action.

Source files are only loaded when you start an analysis to optimize performance.

Embed this Badge

Showcase RepoMind's analysis directly in your repository's README.

[![Analyzed by RepoMind](https://img.shields.io/badge/Analyzed%20by-RepoMind-4F46E5?style=for-the-badge)](https://repomind.in/repo/JuliaPhysics/DynamicQuantities.jl)
Preview:Analyzed by RepoMind

Repository Overview (README excerpt)

Crawler view

DynamicQuantities defines a simple statically-typed type for Julia. Physical dimensions are stored as a *value*, as opposed to a parametric type, as in Unitful.jl. This can greatly improve both runtime performance, by avoiding type instabilities, and startup time, as it avoids overspecializing methods. • Performance • Usage • Constants • Symbolic Units • Custom Units • Affine Units • Arrays • Unitful • Types Performance DynamicQuantities can greatly outperform Unitful when the compiler cannot infer dimensions in a function: **Note the μ and n: this is a 1000x speedup!** Here, the DynamicQuantities quantity object allows the compiler to build a function that is type stable, while the Unitful quantity object, which stores its dimensions in the type, requires type inference at runtime. However, if the dimensions in your function *can* be inferred by the compiler, then you can get better speeds with Unitful: While both of these are type stable, because Unitful parametrizes the type on the dimensions, functions can specialize to units and the compiler can optimize away units from the code. Usage You can create a object by using the convenience macro : or by importing explicitly: Note that is an exported submodule, so you can also access this as . You may like to define so that you can simply write, say, or . This supports a wide range of SI base and derived units, with common prefixes. You can also construct values explicitly with the type, with a value and keyword arguments for the powers of the physical dimensions ( , , , , , , ): Elementary calculations with are supported: Each of these values has the same type, which means we don't need to perform type inference at runtime. Furthermore, we can do dimensional analysis by detecting : The dimensions of a can be accessed either with for the entire object: or with , , etc., for the various dimensions: Finally, you can strip units with : You can also convert a quantity to a desired unit and *then* strip the units using a two-argument version of : This is equivalent to but performs dimension checks first. Constants There are a variety of physical constants accessible via the submodule: which you may like to define as These can also be used inside the macro: Similarly, you can just import each individual constant: For the full list, see the docs. Symbolic Units You can also choose to not eagerly convert to SI base units, instead leaving the units as the user had written them. For example: You can convert to regular SI base units with : This also works with constants: You can also convert a quantity in regular base SI units to symbolic units with the infix operator You can also convert between different symbolic units. (Note that you can write this more explicitly with .) Finally, you can also import these directly: or constants: Note that and are exported, so you can simply access these as and , respectively. Custom Units You can create custom units with the macro: and then use it in calculations normally: Affine Units You can also use "*affine*" units such as Celsius or Fahrenheit, using the string macro: These are regular objects, meaning that you can use them in the same way as regular quantities, including taking differences. To convert back, you can use the two-argument with the particular affine unit: Arrays For working with an array of quantities that have the same dimensions, you can use a : This is a subtype , meaning that indexing a specific element will return a : This also has a custom broadcasting interface which allows the compiler to avoid redundant dimension calculations, relative to if you had simply used an array of quantities: So we can see the version saves on both time and memory. By default, DynamicQuantities will create a from an , similarly to how a is created from a scalar in the Usage examples: Unitful DynamicQuantities allows you to convert back and forth from Unitful.jl: Types Both a 's values and dimensions are of arbitrary type. The default (for the macro) performs exponent tracking for SI units, and (for the macro) performs exponent tracking for all known unit and constant symbols, using a sparse array. You can create custom spaces dimension spaces by simply creating a Julia struct subtyped to : Exponents are tracked by default with the type (alias for ), which represents rational numbers with an integer numerator and fixed denominator. This is much faster than . You can change the type of the value field by initializing with a value explicitly of the desired type. or by conversion: For many applications, using (alias for ) will suffice as the base dimensions type, and can be faster as it means the entire struct will fit into 64 bits. You can change the type of the dimensions field by passing the type you wish to use as the second argument to :