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.6.1 (2024-10-04)#
Bug Fixes#
Fix missing header file in the f2py wrapper. This impacted some versions of numpy and only linux as far as we know. (#129)
Trivial/Internal Changes#
fgen v0.6.0 (2024-07-09)#
Breaking Changes#
Fixed the type of integrables.
External libraries will now need to make sure that their integrable function has signature
function integrable(t, y) real(kind=8), intent(in) :: t real(kind=8), intent(in), dimension(:) :: y ! ! The line below is the one that differs real(kind=8), dimension(size(y)) :: integrable end function
rather than the previous expected signature of
function integrable(t, y) real(kind=8), intent(in) :: t real(kind=8), intent(in), dimension(:) :: y ! ! The line below is the one that differs real(kind=8), dimension(:), allocatable :: integrable end function
In other words,
real(kind=8), dimension(:), allocatable :: integrable->real(kind=8), dimension(size(y)) :: integrable. (#126)
Features#
Added the
initialise_storesflag tolibfgen.fgen_timeseries_builder_collection.TimeseriesBuilderCollection.add_timeseries_collection_to_stores().This allows the builders’ stores to be initialised at the same time as adding a
libfgen.fgen_timeseries_collection.TimeseriesCollectionto the builder collection. This is particularly useful when running coupled models, because it allows the store to be set up on the first time step with a call that can remain the same (except for the value ofinitialise_stores) throughout all iterations. (#128)
Bug Fixes#
Fixed the setting of
stepper % ywhen usinglibfgen.fgen_solve_ivp.solve_ivp().Previously,
stepper % ywould be interpolated between whatever the last value solved in the ‘main’ stepping and the value held by the stepper oncetime_eval % value_last_boundwas passed. This has been fixed,stepper % yis now interpolated between the second last and last values held by the stepper oncetime_eval % value_last_boundis passed. In other words, the result is now interpolating between the last two values visited by the stepper rather than the last value visited by the stepper and wherever the ‘main’ integration ended (which could be many steps earlier and hence lead to much less accurate interpolations). (#127)
Trivial/Internal Changes#
fgen v0.5.1 (2024-06-26)#
Improvements#
Added information about the lines that will likely need to go in a
CMakeLists.txtfile to the logs produced byfgen.commands.generate.generate_command().This allows the user to copy-paste into their own
CMakeLists.txtfile. The path prefix to include in these messages can be customised via the--cmake-prefixoption, which can make it possible to produce output that can be directly copy-pasted. (#124)
Bug Fixes#
Added the
--no-logging-setupflag to our CLI.This allows the logging setup to be disabled from the CLI. This gives the user slightly more control over logging and is important for testing too (where we need a different logging configuration from the default).
Full control over the logging is still an issue though, see https://gitlab.com/magicc/fgen/-/issues/81 for progress tracking. (#124)
Improved Documentation#
Added command-line interface documentation. (#125)
Trivial/Internal Changes#
fgen v0.5.0 (2024-06-24)#
Breaking Changes#
Updated the generated Python type hint for deferred-size arrays to
npt.NDArray[np.float64], rather than e.g.tuple[float, ...](#122)
Features#
Added support for wrapping arrays of derived types (either as attributes or method return values)
Move
fgen.fortran_parsing.FortranDataType.base_python_type()into the “public” API. (#106)Add an equality and inequality operator, including overload, to
libfgen.timeseries.Timeseriesandlibfgen.values_bounded.ValuesBounded.Both these objects have a
equal_toand anot_equal_tomethod as a result, but it is much easier to just use the operators directly (which works smoothly thanks to the overloading). (#115)Add
libfgen.timeseries_collection.TimeseriesCollection.get_timeseries_by_name().This allows the user to get a
libfgen.timeseries.Timeseriesfrom alibfgen.timeseries_collection.TimeseriesCollectionbased on its name alone. (#116)Added
libfgen.fgen_timeseries_builder.TimeseriesBuilderto facilitate building of alibfgen.fgen_timeseries.Timeseriesbit by bit. (#117)Added
libfgen.fgen_timeseries_builder_collection.TimeseriesBuilderCollectionto facilitate building multiplelibfgen.fgen_timeseries.Timeseriesbit by bit within one container. (#118)Added a number of different utilities that will help with coupled model solving, specifically:
Added
libfgen.fgen_array_helpers.searchsorted(). This is very similar tolibfgen.fgen_utils.searchsorted()but provides more information about the result and uses tolerances to check for exact matches rather than strict equality.Added
libfgen.fgen_coupling.get_next_time_eval_basic(). This provides a basic function for getting the next set of times to evaluate when coupling models.Added
libfgen.fgen_number_comparisons.isclose(). This provides a reusable function for checking if two numbers are close.Added
libfgen.fgen_time.TimeAxis.select(). This allows users to sub-select alibfgen.fgen_utils.TimeAxiseasily and resuably.Added
libfgen.fgen_timeseries_collection.TimeseriesCollection.build_from_solve_ivp_result(). This allows users to initialise alibfgen.fgen_timeseries_collection.TimeseriesCollectionobject from alibfgen.fgen_solve_ivp.IVPSolvingResulti.e. the results of solving an initial-value problem.
(#119)
Bug Fixes#
Ensure that the
enums.pyfile follows the sameforcebehaviour as other wrapped Python modules (#110)Fixed handling of methods that take an array of deferred shape as an input and return an array of deferred shape as output.
All other types of input/output have had their test coverage improved. However, this is definitely an area where our testing strategy does not cover all possible paths. Hence, future tests and work may be needed in future. (#123)
Trivial/Internal Changes#
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.ValueDefinitionmust now be initialised with afgen.models.UnitlessValueDefinitionas part of its input. (#44)Move black and jinja2 to optional dependencies.
pip install fgenwill 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 thefgenpackage or the CLI.The unused
openscm-unitsdependency 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_pythontofgen.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.templatorwas removed and has been moved tofgen.wrapper_building. This is the only user-facing change, all other changes are internal. (#86)Updated the
fgen generatecommand-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_nameandfgen.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.TimeAxisto support time axis handling including boundsfgen.models.MultiReturnDefinitionto handle the case where there are multiple return values from a function/method
(#44)
Added
libfgen.timeseries.Timeseries(#51)Added module
fgen_interp1dand modulefgen_array_helpers(#52)Added
solve_until_end_time_boundsargument 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_integrated1dandlibfgen.fgen_differentiate1dand corresponding changes in the underlying 1D interpolation modules.This change also required splitting out
libfgen.fgen_interp1d_optionsto 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.Packagefor 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.PackageSharedElementsto provide a common source for elements that are common across the wrappers in a package and addedfgen.data_models.unitless_value.is_fortran_units_holderto support and clarify how dynamic units are handled.fgen.data_models.unitless_value.is_fortran_units_holderallows 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_moduleand updating thefgen generatecommand to support taking in enum-defining yaml files. The changes tofgen generateare backwards-compatible hence require no interaction from users who do not wish to take advantage of this new feature. (#103)
Improvements#
Upgraded
fgenso 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.TimeAxisso 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.Timeseriesandlibfgen.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_conversionsthat 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/interp1dtosrc/libfgen/1d_handlingunification of the filenames in
src/libfgen/1d_handlingunification of the module names in
src/libfgen/1d_handlingunification of the option names in
libfgen.fgen_1d_handling_optionsusing
timerather thanxconsistently 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_evalis now a required argument and support for simply solving untilt_maxhas been removed. Thet_toleranceargument was also removed because the results will matcht_evalso 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 % valuesequal 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/solverstosrc/libfgen/steppersand renamedlibfgen.fgen_ivptolibfgen.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/modelstosrc/libfgen/data_modelsand 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_modelsfor consistency.Changes:
fgen.data_models.MethodDefinition->fgen.data_models.Methodfgen.data_models.ModuleDefinition->fgen.data_models.Modulefgen.data_models.MultiReturnDefinition->fgen.data_models.MultiReturnfgen.data_models.ValueDefinition->fgen.data_models.Valuefgen.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.templatorand 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_wrather thanw_module_namebecause 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_wandmodule_name_managerrather 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__.pyfile 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
searchsortedandis_monotonicfunctions to thefgen_utilsmodule (#30)Add support for evaluating an initial-value problem IVP on specified timesteps via the
t_evalparameter 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.PointerArrayConversionErrorto 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_SPECIFICATIONSfgen.fortran_parsing.SUPPORTED_TYPE_ATTRIBUTES–>fgen.fortran_parsing.SUPPORTED_ATTRIBUTE_SPECIFICATIONSfgen.fortran_parsing.FortranDataType.type_declaration->fgen.fortran_parsing.FortranDataType.type_specificationfgen.fortran_parsing.FortranDataType.attributes->fgen.fortran_parsing.FortranDataType.attribute_specificationsfgen.fortran_parsing.FortranDataType.fortran_type->fgen.fortran_parsing.FortranDataType.fortran_type_attribute_declarationfgen.fortran_parsing.FortranDataType.DimensionAttribute->fgen.fortran_parsing.FortranDataType.DimensionAttributeSpecificationIn
fgen.fortran_parsing.FortranDataType.from_str, keyword argumentfortran_type_declaration->fortran_type_attribute_declaration
(#11)
Improve typing offered by
fgen_runtime.verify_unitsfgen_runtime.verify_unitswill now correctly type the functions it decorates, for example making clear that a function which receives a float will now expect apint.Quantityas 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.Quantityif you already have a unit registry object instantiated, this is likely the better chance at runtime).The code generated by
fgenhas also been updated to match this new typing capability. This also caused changes tofgen’s API.FortranDataType.equivalent_python_typewill 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.
f2pycannot directly wrap a Fortran derived type, preventing the direct passing or returning of calculators by value in any wrapped functions. Instead,fgencan 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_typeexample.This also adds the concept of
linksto 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_instancesubroutine to the manager modules. This allows for calculators to be referenced by pointers that can be retrieved using themodel_indexof 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.f2pywhich 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