Skip to content

Commit

Permalink
GRIDEDIT-1546 Small refactoring after review and discussion
Browse files Browse the repository at this point in the history
  • Loading branch information
BillSenior committed Dec 2, 2024
1 parent 5f8f92c commit 23c17fd
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 103 deletions.
23 changes: 23 additions & 0 deletions libs/MeshKernel/include/MeshKernel/Definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ namespace meshkernel
{ array[i] } -> std::convertible_to<ResultType>;
};

/// @brief The concept specifies that the array type must have an access operator returning a reference to the array element type
template <typename ArrayType, typename ResultType>
concept ArrayNonConstAccessConcept = requires(ArrayType& array, const size_t i) {
{ array[i] } -> std::same_as<ResultType&>;
};

/// @brief A concept that specifies that the array must have a size function return the number of elements in the array
template <typename ArrayType>
concept ArraySizeConcept = requires(const ArrayType& array) {
Expand All @@ -136,10 +142,27 @@ namespace meshkernel
{ array.end() };
};

/// @brief A concept that specifies that the array must have a begin and end function.
///
/// Would like to also specify the return type here, but span needs some c++23 functionality here.
/// Then change all iterator usage to cbegin and cend returning a const_iterator
/// std::same_as<typename ArrayType::const_iterator>
template <typename ArrayType>
concept ArrayNonConstIteratorsConcept = requires(ArrayType& array) {
{ array.begin() } -> std::same_as<typename ArrayType::iterator>;
{ array.end() } -> std::same_as<typename ArrayType::iterator>;
};

/// @brief A concept that specifies all the functionality required to be usable as a constant array of doubles.
template <typename ArrayType>
concept ValidConstDoubleArray = ArrayConstAccessConcept<ArrayType, double> &&
ArrayConstIteratorsConcept<ArrayType> &&
ArraySizeConcept<ArrayType>;

/// @brief A concept that specifies all the functionality required to be usable as a constant array of doubles.
template <typename ArrayType>
concept ValidNonConstDoubleArray = ArrayNonConstAccessConcept<ArrayType, double> &&
ArrayNonConstIteratorsConcept<ArrayType> &&
ArraySizeConcept<ArrayType>;

} // namespace meshkernel
3 changes: 1 addition & 2 deletions libs/MeshKernel/include/MeshKernel/Mesh2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ namespace meshkernel
enum class Property
{
Orthogonality = 0,
EdgeLength = 1,
Bathymetry = 2
EdgeLength = 1
};

/// @brief Default destructor
Expand Down
4 changes: 1 addition & 3 deletions libs/MeshKernel/include/MeshKernel/SampleInterpolator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ namespace meshkernel
SampleInterpolator(const VectorType& xNodes,
const VectorType& yNodes,
const Projection projection)
: m_triangulation(xNodes, yNodes, projection)
{
}
: m_triangulation(xNodes, yNodes, projection) {}

/// @brief Get the number of nodes of size of the sample data.
UInt Size() const;
Expand Down
17 changes: 11 additions & 6 deletions libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,11 @@ namespace meshkernelapi
double regionControlPointX = mkernel_get_separator(),
double regionControlPointY = mkernel_get_separator());

/// @brief Deallocate property calculator
/// @param[in] propertyId The id of the property
/// @returns Error code
MKERNEL_API int mkernel_deallocate_property(int propertyId);

/// @brief Deallocate mesh state
/// @param[in] meshKernelId The id of the mesh state
/// @returns Error code
Expand Down Expand Up @@ -1344,15 +1349,15 @@ namespace meshkernelapi
///
/// @param[in] meshKernelId The id of the mesh state
/// @param[in] propertyValue The value representing the specific property
/// @param[in] geometrylist A reference to a GeometryList object that will be populated with the values of the requested property
/// @param[in/out] geometrylist A reference to a GeometryList object that will be populated with the values of the requested property
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_property(int meshKernelId, int propertyValue, const GeometryList& geometrylist);

/// @brief The dimension of a specified property of a 2D mesh.
///
/// @param[in] meshKernelId The id of the mesh state
/// @param[in] propertyValue The value representing the specific property
/// @param[in] dimension The dimension of the specified property
/// @param[out] dimension The dimension of the specified property
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_property_dimension(int meshKernelId, int propertyValue, int& dimension);

Expand Down Expand Up @@ -1448,7 +1453,7 @@ namespace meshkernelapi
/// @brief Determine if the property data for the mesh can be computed
/// @param[in] meshKernelId The id of the mesh state
/// @param[in] propertyId The id of the property
/// @param[out] propertyIsAvailable Indicate (true or false) is the property can be calculated.
/// @param[out] propertyIsAvailable Indicate (true or false) if the property can be calculated.
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_is_valid_property(int meshKernelId, const int propertyId, bool& propertyIsAvailable);

Expand Down Expand Up @@ -1603,11 +1608,11 @@ namespace meshkernelapi
MKERNEL_API int mkernel_mesh2d_set(int meshKernelId, const Mesh2D& mesh2d);

/// @brief Sets the property data for the mesh, the sample data points do not have to match the mesh2d nodes.
/// @param[in] meshKernelId The id of the mesh state
/// @param[in] propertyId The id of the property
/// @param[in] projectionType The projection type used by the sample data
/// @param[in] sampleData The sample data and associated sample data points.
/// @param[out] propertyId The id of the property
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_set_property(int meshKernelId, const int propertyId, const GeometryList& sampleData);
MKERNEL_API int mkernel_mesh2d_set_property(int projectionType, const GeometryList& sampleData, int& propertyId);

/// @brief Snaps a mesh to a land boundary.
/// @param[in] meshKernelId The id of the mesh state
Expand Down
31 changes: 24 additions & 7 deletions libs/MeshKernelApi/include/MeshKernelApi/PropertyCalculator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#pragma once

#include "MeshKernel/SampleInterpolator.hpp"

#include "MeshKernelApi/GeometryList.hpp"
#include "MeshKernelApi/State.hpp"

Expand All @@ -43,10 +45,10 @@ namespace meshkernelapi
/// @brief Determine is the calculator can compute the desired results correctly.
///
/// This has a default of checking that the mesh2d is not null and the number of nodes is greater than zero.
virtual bool IsValid(const MeshKernelState& state, const int propertyId) const;
virtual bool IsValid(const MeshKernelState& state) const;

/// @brief Calculate the property
virtual void Calculate(const MeshKernelState& state, const int propertyId, const GeometryList& geometryList) const = 0;
virtual void Calculate(const MeshKernelState& state, const GeometryList& geometryList) const = 0;

/// @brief Determine the size of the vector required to store the calculated properties
virtual int Size(const MeshKernelState& state) const = 0;
Expand All @@ -57,7 +59,7 @@ namespace meshkernelapi
{
public:
/// @brief Calculate the orthogonality for a mesh
void Calculate(const MeshKernelState& state, const int propertyId, const GeometryList& geometryList) const override;
void Calculate(const MeshKernelState& state, const GeometryList& geometryList) const override;

/// @brief Determine the size of the orthogonality vector required
int Size(const MeshKernelState& state) const override;
Expand All @@ -68,24 +70,39 @@ namespace meshkernelapi
{
public:
/// @brief Calculate the edge-length for a mesh
void Calculate(const MeshKernelState& state, const int propertyId, const GeometryList& geometryList) const override;
void Calculate(const MeshKernelState& state, const GeometryList& geometryList) const override;

/// @brief Determine the size of the edge-length vector required
int Size(const MeshKernelState& state) const override;
};

/// @brief Interpolate the depths at the mesh node points.
class DepthSamplePropertyCalculator : public PropertyCalculator
class InterpolatedSamplePropertyCalculator : public PropertyCalculator
{
public:
/// @brief Constructor
InterpolatedSamplePropertyCalculator(const GeometryList& sampleData,
const meshkernel::Projection projection,
const int propertyId);

/// @brief Determine is the calculator can interpolate depth values correctly
bool IsValid(const MeshKernelState& state, const int propertyId) const override;
bool IsValid(const MeshKernelState& state) const override;

/// @brief Calculate the edge-length for a mesh
void Calculate(const MeshKernelState& state, const int propertyId, const GeometryList& geometryList) const override;
void Calculate(const MeshKernelState& state, const GeometryList& geometryList) const override;

/// @brief Determine the size of the edge-length vector required
int Size(const MeshKernelState& state) const override;

private:
/// @brief Interpolator for the samples
std::unique_ptr<meshkernel::SampleInterpolator> m_sampleInterpolator;

/// @brief Projection sued for sample data.
meshkernel::Projection m_projection;

/// @brief Property id.
int m_propertyId = -1;
};

} // namespace meshkernelapi
3 changes: 0 additions & 3 deletions libs/MeshKernelApi/include/MeshKernelApi/State.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include <MeshKernel/Mesh1D.hpp>
#include <MeshKernel/Mesh2D.hpp>
#include <MeshKernel/OrthogonalizationAndSmoothing.hpp>
#include <MeshKernel/SampleInterpolator.hpp>

#include "MeshKernelApi/ApiCache/BoundariesAsPolygonCache.hpp"
#include "MeshKernelApi/ApiCache/CachedPointValues.hpp"
Expand Down Expand Up @@ -84,8 +83,6 @@ namespace meshkernelapi
// Exclusively owned state
meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes

std::shared_ptr<meshkernel::SampleInterpolator> m_sampleInterpolator; ///< Associated samples and interpolator

// Cached values, used when dimensions are computed first, followed by values being retrieved in a separate call
std::shared_ptr<FacePolygonPropertyCache> m_facePropertyCache; ///< face property cache
std::shared_ptr<BoundariesAsPolygonCache> m_boundariesAsPolygonCache; ///< boundaries as polygon cache
Expand Down
85 changes: 61 additions & 24 deletions libs/MeshKernelApi/src/MeshKernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ namespace meshkernelapi
/// @brief Stack of undo actions
static meshkernel::UndoActionStack meshKernelUndoStack;

int GeneratePropertyId()
{
// The current property id, initialised with a number larger than the Mesh2D:::Property enum values
static int currentPropertyId = static_cast<int>(meshkernel::Mesh2D::Property::EdgeLength);

// Increment and return the current property id value.
return ++currentPropertyId;
}

std::map<int, std::unique_ptr<PropertyCalculator>> allocatePropertyCalculators()
{
std::map<int, std::unique_ptr<PropertyCalculator>> propertyMap;
Expand All @@ -137,9 +146,6 @@ namespace meshkernelapi
propertyId = static_cast<int>(meshkernel::Mesh2D::Property::EdgeLength);
propertyMap.emplace(propertyId, std::make_unique<EdgeLengthPropertyCalculator>());

propertyId = static_cast<int>(meshkernel::Mesh2D::Property::Bathymetry);
propertyMap.emplace(propertyId, std::make_unique<DepthSamplePropertyCalculator>());

return propertyMap;
}

Expand Down Expand Up @@ -505,6 +511,26 @@ namespace meshkernelapi
return lastExitCode;
}

MKERNEL_API int mkernel_deallocate_property(int propertyId)
{
lastExitCode = meshkernel::ExitCode::Success;
try
{

if (!propertyCalculators.contains(propertyId) || propertyCalculators[propertyId] == nullptr)
{
throw meshkernel::MeshKernelError("The property id does not exist: {}.", propertyId);
}

propertyCalculators.erase(propertyId);
}
catch (...)
{
lastExitCode = HandleException();
}
return lastExitCode;
}

MKERNEL_API int mkernel_mesh2d_is_valid_property(int meshKernelId, const int propertyId, bool& propertyIsAvailable)
{
lastExitCode = meshkernel::ExitCode::Success;
Expand All @@ -514,7 +540,7 @@ namespace meshkernelapi
propertyIsAvailable = meshKernelState.contains(meshKernelId) &&
propertyCalculators.contains(propertyId) &&
propertyCalculators[propertyId] != nullptr &&
propertyCalculators[propertyId]->IsValid(meshKernelState.at(meshKernelId), propertyId);
propertyCalculators[propertyId]->IsValid(meshKernelState.at(meshKernelId));
}
catch (...)
{
Expand All @@ -523,32 +549,25 @@ namespace meshkernelapi
return lastExitCode;
}

MKERNEL_API int mkernel_mesh2d_set_property(int meshKernelId, const int propertyId, const GeometryList& sampleData)
MKERNEL_API int mkernel_mesh2d_set_property(int projectionType, const GeometryList& sampleData, int& propertyId)
{
lastExitCode = meshkernel::ExitCode::Success;
propertyId = -1;

try
{
if (!meshKernelState.contains(meshKernelId))
{
throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist.");
}
meshkernel::range_check::CheckOneOf<int>(projectionType, meshkernel::GetValidProjections(), "Projection");
auto const projection = static_cast<meshkernel::Projection>(projectionType);

if (meshKernelState[meshKernelId].m_mesh2d == nullptr)
{
throw meshkernel::MeshKernelError("The selected mesh not exist.");
}
int localPropertyId = GeneratePropertyId();

if (propertyCalculators[propertyId] != nullptr && propertyCalculators[propertyId]->IsValid(meshKernelState.at(meshKernelId), propertyId))
if (propertyCalculators.contains(localPropertyId))
{
throw meshkernel::MeshKernelError("The property, {}, has already been defined.", propertyId);
throw meshkernel::ConstraintError("The pproperty id already exists: id = {}.", localPropertyId);
}

std::span<const double> xNodes(sampleData.coordinates_x, sampleData.num_coordinates);
std::span<const double> yNodes(sampleData.coordinates_y, sampleData.num_coordinates);
meshKernelState[meshKernelId].m_sampleInterpolator = std::make_shared<meshkernel::SampleInterpolator>(xNodes, yNodes, meshKernelState[meshKernelId].m_projection);

std::span<const double> dataSamples(sampleData.values, sampleData.num_coordinates);
meshKernelState[meshKernelId].m_sampleInterpolator->SetData(propertyId, dataSamples);
propertyCalculators.try_emplace(localPropertyId, std::make_unique<InterpolatedSamplePropertyCalculator>(sampleData, projection, localPropertyId));
propertyId = localPropertyId;
}
catch (...)
{
Expand Down Expand Up @@ -1573,6 +1592,7 @@ namespace meshkernelapi
MKERNEL_API int mkernel_mesh2d_get_property(int meshKernelId, int propertyValue, const GeometryList& geometryList)
{
lastExitCode = meshkernel::ExitCode::Success;

try
{
if (!meshKernelState.contains(meshKernelId))
Expand All @@ -1585,9 +1605,26 @@ namespace meshkernelapi
return lastExitCode;
}

if (propertyCalculators.contains(propertyValue) && propertyCalculators[propertyValue] != nullptr && propertyCalculators[propertyValue]->IsValid(meshKernelState.at(meshKernelId), propertyValue))
if (!propertyCalculators.contains(propertyValue) || propertyCalculators[propertyValue] == nullptr)
{
throw meshkernel::MeshKernelError("The property calculator does not exist.");
}

if (geometryList.num_coordinates < propertyCalculators[propertyValue]->Size(meshKernelState.at(meshKernelId)))
{
throw meshkernel::ConstraintError("Array size too small to store property values {} < {}.",
geometryList.num_coordinates,
propertyCalculators[propertyValue]->Size(meshKernelState.at(meshKernelId)));
}

if (geometryList.values == nullptr)
{
throw meshkernel::ConstraintError("The property values are null.");
}

if (propertyCalculators[propertyValue]->IsValid(meshKernelState[meshKernelId]))
{
propertyCalculators[propertyValue]->Calculate(meshKernelState.at(meshKernelId), propertyValue, geometryList);
propertyCalculators[propertyValue]->Calculate(meshKernelState[meshKernelId], geometryList);
}
else
{
Expand Down Expand Up @@ -1620,7 +1657,7 @@ namespace meshkernelapi

if (propertyCalculators.contains(propertyValue) && propertyCalculators[propertyValue] != nullptr)
{
dimension = propertyCalculators[propertyValue]->Size(meshKernelState.at(meshKernelId));
dimension = propertyCalculators[propertyValue]->Size(meshKernelState[meshKernelId]);
}
else
{
Expand Down
Loading

0 comments on commit 23c17fd

Please sign in to comment.