Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bindings/python/src/basic/attribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
namespace geode
{
template < typename type >
void python_attribute_class(

Check warning on line 34 in bindings/python/src/basic/attribute.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute.cpp:34:10 [misc-use-internal-linkage]

function 'python_attribute_class' can be made static or moved into an anonymous namespace to enforce internal linkage
pybind11::module& module, const std::string& typestr )
{
const auto read_name = absl::StrCat( "ReadOnlyAttribute", typestr );
Expand Down Expand Up @@ -64,7 +64,7 @@
}

template < typename type >
void python_attribute_values_class(

Check warning on line 67 in bindings/python/src/basic/attribute.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute.cpp:67:10 [misc-use-internal-linkage]

function 'python_attribute_values_class' can be made static or moved into an anonymous namespace to enforce internal linkage
pybind11::module& module, const std::string& typestr )
{
const auto values_name = absl::StrCat( "AttributeValues", typestr );
Expand All @@ -76,14 +76,14 @@
.def_readwrite( "no_value", &AttributeValues< type >::no_value );
}

void define_attributes( pybind11::module& module )

Check warning on line 79 in bindings/python/src/basic/attribute.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute.cpp:79:10 [misc-use-internal-linkage]

function 'define_attributes' can be made static or moved into an anonymous namespace to enforce internal linkage
{
pybind11::class_< AttributeProperties >( module, "AttributeProperties" )
.def( pybind11::init<>() )
.def_readwrite( "assignable", &AttributeProperties::assignable )
.def_readwrite( "interpolable", &AttributeProperties::interpolable )
.def_readwrite(
"transferable", &AttributeProperties::transferable );
.def_readwrite( "transferable", &AttributeProperties::transferable )
.def_readwrite( "time", &AttributeProperties::time );

python_attribute_values_class< bool >( module, "Bool" );
python_attribute_values_class< int >( module, "Int" );
Expand Down
50 changes: 49 additions & 1 deletion bindings/python/src/basic/attribute_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "../common.hpp"

#include <geode/basic/attribute_manager.hpp>
#include <geode/basic/attribute_time_series.hpp>
#include <geode/basic/constant_attribute.hpp>
#include <geode/basic/sparse_attribute.hpp>
#include <geode/basic/variable_attribute.hpp>
Expand All @@ -33,7 +34,7 @@
namespace geode
{
template < typename type >
void python_attribute_class( pybind11::class_< AttributeManager >& manager,

Check warning on line 37 in bindings/python/src/basic/attribute_manager.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute_manager.cpp:37:10 [misc-use-internal-linkage]

variable 'python_attribute_class' can be made static or moved into an anonymous namespace to enforce internal linkage

Check warning on line 37 in bindings/python/src/basic/attribute_manager.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute_manager.cpp:37:10 [cppcoreguidelines-avoid-non-const-global-variables]

variable 'python_attribute_class' is non-const and globally accessible, consider making it const
const std::string& suffix )
{
const auto read_suffix =
Expand Down Expand Up @@ -96,10 +97,46 @@
static_cast< std::shared_ptr< SparseAttribute< type > > (
AttributeManager::* )( const geode::uuid& ) >(
&AttributeManager::find_attribute< SparseAttribute, type > ) );
const auto create_time_step_constant_suffix =
absl::StrCat( "create_time_step_attribute_constant_", suffix );
manager.def( create_time_step_constant_suffix.c_str(),
&AttributeManager::create_time_step_attribute< ConstantAttribute,
type > );
const auto create_time_step_variable_suffix =
absl::StrCat( "create_time_step_attribute_variable_", suffix );
manager.def( create_time_step_variable_suffix.c_str(),
&AttributeManager::create_time_step_attribute< VariableAttribute,
type > );
const auto create_time_step_sparse_suffix =
absl::StrCat( "create_time_step_attribute_sparse_", suffix );
manager.def( create_time_step_sparse_suffix.c_str(),
&AttributeManager::create_time_step_attribute< SparseAttribute,
type > );
}

template < typename type >
void python_attribute_time_series_class(

Check warning on line 118 in bindings/python/src/basic/attribute_manager.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute_manager.cpp:118:10 [misc-use-internal-linkage]

function 'python_attribute_time_series_class' can be made static or moved into an anonymous namespace to enforce internal linkage
pybind11::module& module, const std::string& typestr )
{
const auto name = absl::StrCat( "AttributeTimeSeries", typestr );
pybind11::class_< AttributeTimeSeries< type > >( module, name.c_str() )
.def(
pybind11::init< const AttributeManager&, std::string_view >() )
.def( "nb_time_steps", &AttributeTimeSeries< type >::nb_time_steps )
.def( "time", &AttributeTimeSeries< type >::time )
.def( "step_attribute",
&AttributeTimeSeries< type >::step_attribute,
pybind11::return_value_policy::reference_internal )
.def( "value", &AttributeTimeSeries< type >::value )
.def( "element_values",
&AttributeTimeSeries< type >::element_values );
}

void define_attribute_manager( pybind11::module& module )

Check warning on line 135 in bindings/python/src/basic/attribute_manager.cpp

View workflow job for this annotation

GitHub Actions / test / tidy

bindings/python/src/basic/attribute_manager.cpp:135:10 [misc-use-internal-linkage]

function 'define_attribute_manager' can be made static or moved into an anonymous namespace to enforce internal linkage
{
pybind11::class_< AttributeTimeStep >( module, "AttributeTimeStep" )
.def_readonly( "time", &AttributeTimeStep::time )
.def_readonly( "attribute_id", &AttributeTimeStep::attribute_id );
pybind11::class_< AttributeManager > manager(
module, "AttributeManager" );
manager.def( pybind11::init<>() )
Expand All @@ -119,7 +156,9 @@
&AttributeManager::set_attribute_properties )
.def( "delete_elements", &AttributeManager::delete_elements )
.def( "attribute_ids_matching_name",
&AttributeManager::attribute_ids_matching_name );
&AttributeManager::attribute_ids_matching_name )
.def( "time_steps", &AttributeManager::time_steps )
.def( "time_series_names", &AttributeManager::time_series_names );
python_attribute_class< bool >( manager, "bool" );
python_attribute_class< int >( manager, "int" );
python_attribute_class< unsigned int >( manager, "uint" );
Expand All @@ -129,5 +168,14 @@
manager, "arraydouble2" );
python_attribute_class< std::array< double, 3 > >(
manager, "arraydouble3" );
python_attribute_time_series_class< bool >( module, "Bool" );
python_attribute_time_series_class< int >( module, "Int" );
python_attribute_time_series_class< unsigned int >( module, "UInt" );
python_attribute_time_series_class< float >( module, "Float" );
python_attribute_time_series_class< double >( module, "Double" );
python_attribute_time_series_class< std::array< double, 2 > >(
module, "ArrayDouble2" );
python_attribute_time_series_class< std::array< double, 3 > >(
module, "ArrayDouble3" );
}
} // namespace geode
32 changes: 32 additions & 0 deletions bindings/python/tests/basic/test-py-attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,37 @@ def test_sparse_attribute_after_element_deletion(manager, double_attribute_id):
raise ValueError("Element 7 of sparse attribute should be 12 ")


def test_time_series():
manager = basic.AttributeManager()
manager.resize(4)
values = basic.AttributeValuesDouble()
values.default_value = 0
values.no_value = 0
for time in [10.0, 0.0, 5.0]:
properties = basic.AttributeProperties()
if properties.time is not None:
raise ValueError("[Test] Default properties should have no time")
attribute_id = manager.create_time_step_attribute_variable_double(
"pressure", time, values, properties
)
attribute = manager.find_attribute_variable_double(attribute_id)
attribute.set_value(2, 100 * time)
if attribute.properties().time != time:
raise ValueError("[Test] Step should hold its time")
if manager.time_series_names() != ["pressure"]:
raise ValueError("[Test] Wrong time series names")
steps = manager.time_steps("pressure")
if [step.time for step in steps] != [0.0, 5.0, 10.0]:
raise ValueError("[Test] Steps should be sorted by time")
series = basic.AttributeTimeSeriesDouble(manager, "pressure")
if series.nb_time_steps() != 3 or series.time(1) != 5.0:
raise ValueError("[Test] Wrong time series steps")
if series.element_values(2) != [0.0, 500.0, 1000.0]:
raise ValueError("[Test] Wrong element values")
if series.value(1, 2) != 500.0 or series.step_attribute(1).value(2) != 500.0:
raise ValueError("[Test] Wrong step value")


if __name__ == '__main__':
manager = basic.AttributeManager()
manager.resize(10)
Expand All @@ -177,3 +208,4 @@ def test_sparse_attribute_after_element_deletion(manager, double_attribute_id):
raise ValueError("[Test] Manager should have 10 elements")
manager.clear()
test_number_of_attributes(manager, 0)
test_time_series()
57 changes: 57 additions & 0 deletions include/geode/basic/attribute_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@

namespace geode
{
/*!
* One step of a time series: the attribute holding the values at the
* given time.
*/
struct AttributeTimeStep
{
double time;
uuid attribute_id;
};

/*!
* This class manages all its associated Attributes.
* Each Attribute is registered and can be retrieved by a given name.
Expand Down Expand Up @@ -146,6 +156,41 @@ namespace geode
return attribute_id;
}

/*!
* Create one step of a time series.
* A time series is the set of attributes sharing a name and having a
* time in their AttributeProperties.
* @param[in] attribute_name The name of the series.
* @param[in] time The time of this step, stored in properties.time.
* @exception OpenGeodeException if time is not finite, if an
* attribute with this name has no time, a different type or the same
* time.
*/
template < template < typename > class Attribute, typename T >
[[nodiscard]] geode::uuid create_time_step_attribute(
std::string_view attribute_name,
double time,
AttributeValues< T > default_values,
AttributeProperties properties )
{
check_new_time_step( attribute_name, time, typeid( T ).name() );
properties.time = time;
return create_attribute< Attribute, T >( attribute_name,
std::move( default_values ), std::move( properties ) );
}

/*!
* Get the steps of the time series with the given name, sorted by
* time. Empty if no attribute with this name has a time.
*/
[[nodiscard]] std::vector< AttributeTimeStep > time_steps(
std::string_view attribute_name ) const;

/*!
* Get the distinct names of the attributes having a time.
*/
[[nodiscard]] std::vector< std::string > time_series_names() const;

/*!
* Resize all the attributes to the given size
* @param[in] size The new attribute size
Expand Down Expand Up @@ -223,6 +268,11 @@ namespace geode
[[nodiscard]] std::string_view attribute_type(
const geode::uuid& ) const;

/*!
* Replace all the properties of the attribute.
* @warning The time is replaced too: passing properties with an empty
* time removes the attribute from its time series.
*/
void set_attribute_properties( geode::uuid attribute_id,
const AttributeProperties& new_properties );

Expand Down Expand Up @@ -295,6 +345,13 @@ namespace geode
void register_attribute(
std::shared_ptr< AttributeBase > attribute, const geode::uuid& );

/*!
* Check that a new step can be added to the time series.
*/
void check_new_time_step( std::string_view attribute_name,
double time,
std::string_view type ) const;

private:
IMPLEMENTATION_MEMBER( impl_ );
};
Expand Down
106 changes: 106 additions & 0 deletions include/geode/basic/attribute_time_series.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2019 - 2026 Geode-solutions
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

#pragma once

#include <memory>
#include <string_view>
#include <vector>

#include <absl/container/fixed_array.h>

#include <geode/basic/attribute.hpp>
#include <geode/basic/attribute_manager.hpp>
#include <geode/basic/common.hpp>

namespace geode
{
/*!
* Read-only access to the steps of a time series stored in an
* AttributeManager (see AttributeManager::create_time_step_attribute).
* This is a snapshot taken at construction: steps created later are not
* seen, and steps deleted later stay readable.
*/
template < typename T >
class AttributeTimeSeries
{
public:
AttributeTimeSeries(
const AttributeManager& manager, std::string_view name )
{
const auto steps = manager.time_steps( name );
times_.reserve( steps.size() );
attributes_.reserve( steps.size() );
for( const auto& step : steps )
{
OpenGeodeBasicException::check_exception(
times_.empty() || times_.back() < step.time, nullptr,
OpenGeodeException::TYPE::data,
"[AttributeTimeSeries] Time series '", name,
"' has several steps at time ", step.time );
times_.push_back( step.time );
attributes_.push_back( manager.find_read_only_attribute< T >(
step.attribute_id ) );
}
}

[[nodiscard]] index_t nb_time_steps() const
{
return static_cast< index_t >( times_.size() );
}

[[nodiscard]] double time( index_t step ) const
{
return times_.at( step );
}

[[nodiscard]] const ReadOnlyAttribute< T >& step_attribute(
index_t step ) const
{
return *attributes_.at( step );
}

[[nodiscard]] const T& value( index_t step, index_t element ) const
{
return attributes_.at( step )->value( element );
}

/*!
* Get the values of one element at every step, in time order.
*/
[[nodiscard]] absl::FixedArray< T > element_values(
index_t element ) const
{
absl::FixedArray< T > values( attributes_.size() );
for( const auto step : Indices{ attributes_ } )
{
values[step] = attributes_[step]->value( element );
}
return values;
}

private:
std::vector< double > times_;
std::vector< std::shared_ptr< ReadOnlyAttribute< T > > > attributes_;
};
} // namespace geode
16 changes: 16 additions & 0 deletions include/geode/basic/attribute_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@

#pragma once

#include <optional>

#include <absl/types/span.h>

#include <bitsery/ext/std_optional.h>

#include <geode/basic/common.hpp>
#include <geode/basic/growable.hpp>
#include <geode/basic/range.hpp>
Expand Down Expand Up @@ -59,12 +63,24 @@
archive.value1b( properties.assignable );
archive.value1b( properties.interpolable );
archive.value1b( properties.transferable );
},
[]( Archive& archive,
AttributeProperties& properties ) {
archive.value1b( properties.assignable );
archive.value1b( properties.interpolable );
archive.value1b( properties.transferable );
archive.ext( properties.time,
bitsery::ext::StdOptional{},
[]( Archive& archive2, double& value ) {
archive2.value8b( value );
} );
} } } );
}

bool assignable{ false };
bool interpolable{ false };
bool transferable{ true };
std::optional< double > time{};

Check warning on line 83 in include/geode/basic/attribute_utils.hpp

View workflow job for this annotation

GitHub Actions / test / tidy

include/geode/basic/attribute_utils.hpp:83:37 [readability-redundant-member-init]

initializer for member 'time' is redundant
};

template < typename AttributeType >
Expand Down Expand Up @@ -145,7 +161,7 @@
absl::FixedArray< double > lambdas_;
};

#define IMPLICIT_ATTRIBUTE_LINEAR_INTERPOLATION( Type ) \

Check warning on line 164 in include/geode/basic/attribute_utils.hpp

View workflow job for this annotation

GitHub Actions / test / tidy

include/geode/basic/attribute_utils.hpp:164:9 [cppcoreguidelines-macro-usage]

function-like macro 'IMPLICIT_ATTRIBUTE_LINEAR_INTERPOLATION' used; consider a 'constexpr' template function
template <> \
struct AttributeLinearInterpolationImpl< Type > \
{ \
Expand Down Expand Up @@ -179,7 +195,7 @@
IMPLICIT_ATTRIBUTE_LINEAR_INTERPOLATION( float );
IMPLICIT_ATTRIBUTE_LINEAR_INTERPOLATION( double );

#define IMPLICIT_ARRAY_ATTRIBUTE_LINEAR_INTERPOLATION( Type ) \

Check warning on line 198 in include/geode/basic/attribute_utils.hpp

View workflow job for this annotation

GitHub Actions / test / tidy

include/geode/basic/attribute_utils.hpp:198:9 [cppcoreguidelines-macro-usage]

function-like macro 'IMPLICIT_ARRAY_ATTRIBUTE_LINEAR_INTERPOLATION' used; consider a 'constexpr' template function
template < size_t array_size > \
struct AttributeLinearInterpolationImpl< std::array< Type, array_size > > \
{ \
Expand Down
1 change: 1 addition & 0 deletions src/geode/basic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ add_geode_library(
"algorithm.hpp"
"assert.hpp"
"attribute_manager.hpp"
"attribute_time_series.hpp"
"attribute_utils.hpp"
"attribute.hpp"
"bitsery_archive.hpp"
Expand Down
Loading
Loading