Changelog#
Versions follow Semantic Versioning (<major>.<minor>.<patch>
).
Backward incompatible (breaking) changes will only be introduced in major versions with advance notice in the Deprecations section of releases.
fgen v0.4.1 (2024-04-10)#
Improved Documentation#
Fixed the generated changelog from the release of
v0.4.0
(#109)
fgen v0.4.0 (2024-04-10)#
Breaking Changes#
Broke the initialisation for
fgen.models.ValueDefinition
.This was required to handle multi-return types.
fgen.models.ValueDefinition
must now be initialised with afgen.models.UnitlessValueDefinition
as part of its input. (#44)Move black and jinja2 to optional dependencies.
pip install fgen
will now only install the runtime dependencies required forfgen_runtime
. For users that are not building wrappers, this change will have no impact.For downstream packages that generate wrappers, you now need to specify
fgen[templates]
as a build dependency in order to use thefgen
package or the CLI.The unused
openscm-units
dependency was also removed. (#49)Changed the default module prefix to an empty string i.e. no prefix.
This simplifies the reasoning related to prefixes and makes it much easier to use modules with a name that does not use the “mod_” prefix.
It does break the previous behaviour. However, these changes are all in the auto-generated wrappers, so should require minimal (if any) changes for users. (#70)
Renamed
fgen.data_models.unitless_value.UnitlessValue.expose_to_python
tofgen.data_models.unitless_value.UnitlessValue.expose_getter_to_python
.The flag does the same thing, but this renaming clarifies its behaviour. (#79)
Re-structured the templates within
fgen
.fgen.templator
was removed and has been moved tofgen.wrapper_building
. This is the only user-facing change, all other changes are internal. (#86)Updated the
fgen generate
command-line interface so that it expects a series of yaml files rather than processing modules one at a time.This makes it much easier to handle dependencies between modules, as we have them all in memory at the same time. It also paves the way for improving our ability to wrap new types, for example enums (which require a Python definition of the enum to be written too, but only once at the package-level, as we can only pass integers across the Python-Fortran border). Such behaviour is not possible when we write the modules independently of each other.
If you previously did e.g.
fgen generate ... mod_1.yaml fgen generate ... mod_2.yaml
Now you can just do
fgen generate ... mod_1.yaml mod_2.yaml
This MR also removed
fgen.wrapper_building.process_module()
}, replacing it withfgen.wrapper_building.process_package()
. If you previously did, e.g.process_module(module, ...)
As demonstrated in the tests, you can get the same behaviour with
process_package(Package((module,)), ...)
(#88)
Removed
fgen.data_models.module.Module.short_name
andfgen.data_models.module.Module.short_name
.The same behaviour can be achieved by setting
fgen.data_models.module.Module.truncated_name
(and this option also provides much greater flexibility and control). (#96)
Features#
Pin fgen==v0.3.1 via the CMake module (#43)
Added the ability to handle fluxes as part of model output.
This MR included a number of additional pieces of functionality to make this work:
libfgen.fgen_time.TimeAxis
to support time axis handling including boundsfgen.models.MultiReturnDefinition
to handle the case where there are multiple return values from a function/method
(#44)
Added
libfgen.timeseries.Timeseries
(#51)Added module
fgen_interp1d
and modulefgen_array_helpers
(#52)Added
solve_until_end_time_bounds
argument tolibfgen.fgen_ivp.solve_ivp()
.This allows us to ensure that the model is solved up until the end of the last time step, rather than stopping at the start of the last time step, which is not helpful for creating timeseries objects etc. (#54)
Added
libfgen.fgen_timeseries.Timeseries.integrate()
andlibfgen.fgen_timeseries.Timeseries.differentiate()
.Integration and differentiation are only implemented for key use cases. If you have a different use case, the skeleton is there but you will need to implement the details yourself.
These are supported by
libfgen.fgen_integrated1d
andlibfgen.fgen_differentiate1d
and corresponding changes in the underlying 1D interpolation modules.This change also required splitting out
libfgen.fgen_interp1d_options
to avoid circular dependencies. (#55)Added
libfgen.fgen_values_bounded.ValuesBounded
(#57)Added
libfgen.fgen_timeseries.Timeseries.get_value_at_time()
to facilitate retrieving values at specific times.This also required adding
libfgen.fgen_boundary1d
, to support handling of the logic around which values to supply when on the boundary of our timesteps (which varies depending on the kind of interpolation which is being assumed). (#59)Added
libfgen.fgen_timeseries.Timeseries.interpolate()
,libfgen.fgen_timeseries.Timeseries.interpolate_single()
andlibfgen.fgen_timeseries.Timeseries.extrapolat_single()
to support interpolation and extrapolation operations withlibfgen.fgen_timeseries.Timeseries
. (#60)Added support for wrapping derived type attributes which are character arrays in Fortran, str in Python. (#71)
Added setters for and the ability for methods to return the following types:
intrinsic
float
int
boolean
characters/strings
fixed-length
allocatable-length
arrays
fixed-length
allocatable-length
derived types
accessed via pointers
encapsulate i.e. allocatable attributes are not yet supported, that will happen in #42
The setters are not added automatically, they must be opted into on a per-attribute basis by setting
expose_setter_to_python: true
.(#79)
Added support for wrapping derived types that have allocatable, other derived types as attributes (#80)
Added support for wrapping
libfgen.fgen_values_bounded.ValuesBounded
.The more general pattern is that we can now support things that should retrieve their units dynamically from Pint quantities, rather than forcing all unit specifications to be static. There are now quite a lot of moving parts in our wrapper generation. We aim to clean these up as part of #60. (#81)
Added
fgen.data_models.Package
for handling a collection of modules i.e. a package (#88)Added auto-generation of
_repr_pretty_
and_repr_html_
methods to the generated Python wrappers (#91)Added
fgen.data_models.package_shared_elements.PackageSharedElements
to provide a common source for elements that are common across the wrappers in a package and addedfgen.data_models.unitless_value.is_fortran_units_holder
to support and clarify how dynamic units are handled.fgen.data_models.unitless_value.is_fortran_units_holder
allows you to specify that this attribute is the attribute which holds the units in Fortran, i.e. it is where dynamic units should be passed into Fortran and where dynamic units should be retrieved from Fortran. (#93)Added support for wrapping enums defined in Fortran.
This was done by adding
fgen.wrapping_strategies.enum.WrappingStrategyEnum
,fgen.data_models.EnumDefiningModule
,fgen.data_models.enum_defining_module
and updating thefgen generate
command to support taking in enum-defining yaml files. The changes tofgen generate
are backwards-compatible hence require no interaction from users who do not wish to take advantage of this new feature. (#103)
Improvements#
Upgraded
fgen
so it can now wrap Fortran callables that return values in different units.For example, a callable that returns temperature in kelvin and ocean heat uptake in joules per year.
This required adding
fgen.models.CalculatorDefinition.units_str()
andfgen.models.CalculatorDefinition.units_multi_return()
. (#44)Updated the codebase to handle the introduction of
libfgen.fgen_values_bounded.ValuesBounded
.Specifically, updated
libfgen.fgen_time.TimeAxis
so that it is a sub-class oflibfgen.fgen_values_bounded.ValuesBounded
. (There doesn’t seem to be a better way to do this in Fortran given the lack of attrs-style validators.)Also updated
libfgen.fgen_timeseries.Timeseries
andlibfgen.fgen_ivp.solve_ivp()
to uselibfgen.fgen_values_bounded.ValuesBounded
.Also updated
libfgen.fgen_values_bounded.ValuesBounded.repr()
so that it can be indented inlibfgen.fgen_timeseries.Timeseries.repr()
. (#56)Added sane defaults to format strings in
libfgen.fgen_char_conversions
.As a result, you no longer need to pass format strings into the various formatting functions in
libfgen.fgen_char_conversions
.Also exposed constants in
libfgen.fgen_char_conversions
that can be used by other components if they wish. We expected this would be used rarely, but it avoids completely hard-coding the values with no explanation of what they are and provides a way to control them without exposing arguments to control these values in every single function that does any character handling. (#57)Clarified the one-dimensional handling module.
There is now an extensive docstring at the top of
libfgen.fgen_1d_handling_options
.This also included the following clean ups:
renaming
src/libfgen/interp1d
tosrc/libfgen/1d_handling
unification of the filenames in
src/libfgen/1d_handling
unification of the module names in
src/libfgen/1d_handling
unification of the option names in
libfgen.fgen_1d_handling_options
using
time
rather thanx
consistently throughoutlibfgen.fgen_1d_handling_options
, to reflect the fact that our x-axis is assumed to be a time axis (i.e. stricly monotonically increasing)
As the affected features are unreleased, we do not provide a more detailed migration guide.
(#61)
Updated
libfgen.fgen_ivp.solve_ivp()
so it only supports the use oft_eval
.t_eval
is now a required argument and support for simply solving untilt_max
has been removed. Thet_tolerance
argument was also removed because the results will matcht_eval
so there is no need to worry about tolerance anymore.As the affected features are unreleased, we do not provide a more detailed migration guide. The short story is this: any use of
libfgen.fgen_ivp.solve_ivp()
will now requiret_eval
. To get the same behaviour as previously, set the only value int_eval % values
equal to your previoust_max
. Then just drop the first value in your result. (#62)Re-named solver to stepper throughout the code, whenever the thing being referred to was an object used for numerically solving ODE’s.
This is a breaking change. For example, we have renamed
src/libfgen/solvers
tosrc/libfgen/steppers
and renamedlibfgen.fgen_ivp
tolibfgen.fgen_solve_ivp
. However, as the code it breaks has not yet been released, we do not label this as breaking nor do we provide a detailed migration guide. (#65)Aligned the names in fgen with the domain model described in Overview.
This is a breaking change. For example, we have renamed
src/libfgen/models
tosrc/libfgen/data_models
and renamed calculator to fortran derived type throughout the code. However, as the code it breaks has not yet been released, we do not label this as breaking nor do we provide a detailed migration guide. (#67)Renamed the classes in
fgen.data_models
for consistency.Changes:
fgen.data_models.MethodDefinition
->fgen.data_models.Method
fgen.data_models.ModuleDefinition
->fgen.data_models.Module
fgen.data_models.MultiReturnDefinition
->fgen.data_models.MultiReturn
fgen.data_models.ValueDefinition
->fgen.data_models.Value
fgen.data_models.UnitlessValueDefinition
->fgen.data_models.UnitlessValue
As the code it breaks was not released at the time of merging, we do not label this as breaking nor do we provide a detailed migration guide.
(#72)
Re-wrote
fgen.templator
and the associated template filesKey changes:
wrote docs explaining how our templating works
made the Fortran wrapper module template and Python wrapper module template more symmetric, this makes it easier to see how they work
split the templates out into a number of smaller files. This means there is more to keep track of, but the logic is much easier to follow within the limited scopes.
cleaned up the templates
changed the naming in the templates to try to use most significant bit naming, e.g. wrapper modules are now
module_name_w
rather thanw_module_name
because the key thing is the module that is being wrapped, then the fact that it is a wrapper. This also makes the names of our modules more consistent (you end up withmodule_name
,module_name_w
andmodule_name_manager
rather than having prefixes which makes it harder to line things up).
As the code it breaks has not yet been released, we do not label this as breaking nor do we provide a detailed migration guide.
(#76)
Increased the number of available Fortran instances in manager modules to 4096 and improved the validation and handling of dynamic unit values.
In testing in the notebook, we found that you can hit 2048 easily once you start making lots of plots (which can trigger recursive calls to access units, with associated instance usage). (#93)
When processing a package, generate a
__init__.py
file in the python directory if one does not already exist. This ensures that the Python directory is always a valid Python package. (#101)
Bug Fixes#
Improved Documentation#
Added documentation to clarify the domain model of fgen and the naming choices that follow.
The code changes required to match this documentation will be done as part of solving the following issues:
(#64)
Added documentation explaining how our templating works.
This sets out the patterns and strategies we use.
At the time of merging, this is an intended goal rather than actually being how it works. We will update the docs over time as we do the implementation. (#85)
Wrote docs describing how are wrapper builders and strategies will fit together. (#88)
Trivial/Internal Changes#
fgen v0.3.1 (2024-01-25)#
Improvements#
Add support for installing the fortran library via CMake (#41)
Improved Documentation#
Added notebooks about subtleties of solving models in terms of input interpolation and flux handling (#40)
Trivial/Internal Changes#
fgen v0.3.0 (2024-01-19)#
Features#
Add colour to logging and switch to loguru for internal logging handling (#23)
Add
searchsorted
andis_monotonic
functions to thefgen_utils
module (#30)Add support for evaluating an initial-value problem IVP on specified timesteps via the
t_eval
parameter tofgen_ivp.solve_ivp
(#34)Added basic conversions to character types (see
fgen_char_conversions
) (#37)Added a Euler forward solver (see
fgen_euler_forward
) (#38)
Improvements#
Adds Fortran-based unit tests using the test-drive framework (#21)
Added writing of
__str__
method on generated classes to provide a quick way to get more information about the Fortran values from Python (#24)Introduce
fgen_runtime.exceptions.PointerArrayConversionError
to provide more specific context when the error raised is related to conversion from a pointer to an array.This context is then used to provide more information when creating a generated object’s
__str__
representation. (#26)
Bug Fixes#
Trivial/Internal Changes#
fgen v0.2.1 (2023-12-07)#
Bug Fixes#
Wrapping of a class with more than one method (previously the methods would not have a newline between them so the generated code was not syntactically correct) (#22)
fgen v0.2.0 (2023-12-06)#
Breaking Changes#
Refactor the fortran data type parsing module to extract additional information about a fortran type. While this change is breaking to the API of
fgen
, it doesn’t impact the generated wrappers. (#9)Updates to names to better reflect the part of the Fortran specification that are being captured:
fgen.fortran_parsing.SUPPORTED_TYPE_DECLARATION
–>fgen.fortran_parsing.SUPPORTED_TYPE_SPECIFICATIONS
fgen.fortran_parsing.SUPPORTED_TYPE_ATTRIBUTES
–>fgen.fortran_parsing.SUPPORTED_ATTRIBUTE_SPECIFICATIONS
fgen.fortran_parsing.FortranDataType.type_declaration
->fgen.fortran_parsing.FortranDataType.type_specification
fgen.fortran_parsing.FortranDataType.attributes
->fgen.fortran_parsing.FortranDataType.attribute_specifications
fgen.fortran_parsing.FortranDataType.fortran_type
->fgen.fortran_parsing.FortranDataType.fortran_type_attribute_declaration
fgen.fortran_parsing.FortranDataType.DimensionAttribute
->fgen.fortran_parsing.FortranDataType.DimensionAttributeSpecification
In
fgen.fortran_parsing.FortranDataType.from_str
, keyword argumentfortran_type_declaration
->fortran_type_attribute_declaration
(#11)
Improve typing offered by
fgen_runtime.verify_units
fgen_runtime.verify_units
will now correctly type the functions it decorates, for example making clear that a function which receives a float will now expect apint.Quantity
as a result of being decorated. This type hinting isn’t perfect and may break, please raise an issue if it does.As part of this change, we have also removed
fgen_runtime.units.Quantity
. Please get the class from Pint instead via e.g.pint.registry.UnitRegsitry.Quantity
(orur.Quantity
if you already have a unit registry object instantiated, this is likely the better chance at runtime).The code generated by
fgen
has also been updated to match this new typing capability. This also caused changes tofgen
’s API.FortranDataType.equivalent_python_type
will now return float and int for the Fortran real and integer types rather than Quantity as was previously the case. (#20)
Features#
Adds the option to reference another calculator in a wrapped function.
f2py
cannot directly wrap a Fortran derived type, preventing the direct passing or returning of calculators by value in any wrapped functions. Instead,fgen
can pass the model index of a calculator, which the Python module can then convert into a concrete instance. A working example can be found in thederived_type
example.This also adds the concept of
links
to module configuration. These links describe other wrapped modules that are dependencies. (#12)Support deferred shape arrays being used as calculator attributes (#15)
Improvements#
Added
get_instance
subroutine to the manager modules. This allows for calculators to be referenced by pointers that can be retrieved using themodel_index
of the calculator. (#6)Add support for type hints (#7)
Add a check to see if a valid model index was found (#8)
Fix up order of imports in generated Python and add type hints to
_UNITS
(#18)
Improved Documentation#
Updated documentation throughout
fgen.fortran_parsing
(#11)
Trivial/Internal Changes#
fgen v0.1.2 (2023-07-17)#
Features#
Add
fgen.f2py
which wrapsnumpy.f2py
, but applies some additional error handling (#5)
fgen v0.1.1 (2023-07-14)#
Improvements#
Add support for Python v3.9 (#3)
Improved Documentation#
fgen v0.1.0 (2023-07-03)#
Feature#
Initial release