Skip to content

Commit

Permalink
Use MultiFieldArrayRegistry to avoid accidental deletion of multifiel…
Browse files Browse the repository at this point in the history
…d arrays
  • Loading branch information
wdeconinck committed Sep 5, 2023
1 parent dff07da commit 225a0f4
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 130 deletions.
41 changes: 37 additions & 4 deletions src/atlas/field/MultiField.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
#include <map>
#include <memory>
#include <string>

#include "eckit/thread/AutoLock.h"
#include "eckit/thread/Mutex.h"
#include <mutex>

#include "atlas/field/Field.h"
#include "atlas/grid/Grid.h"
Expand All @@ -41,6 +39,34 @@ void force_link() {

//-----------------------------------------------------------------------------

class MultiFieldArrayRegistry : public field::FieldObserver {
private:
MultiFieldArrayRegistry() {}

public:
static MultiFieldArrayRegistry& instance() {
static MultiFieldArrayRegistry inst;
return inst;
}
void onFieldDestruction(FieldImpl& field) override {
std::lock_guard<std::mutex> guard(lock_);
map_.erase(&field);
}

~MultiFieldArrayRegistry() override = default;

void add(Field& field, std::shared_ptr<array::Array> array) {
std::lock_guard<std::mutex> guard(lock_);
map_.emplace(field.get(),array);
field->attachObserver(*this);
}

public:
std::mutex lock_;
std::map<FieldImpl*,std::shared_ptr<array::Array>> map_;

};

MultiFieldCreator::MultiFieldCreator(const eckit::Configuration&) {}

MultiFieldCreator::~MultiFieldCreator() = default;
Expand All @@ -51,7 +77,7 @@ MultiFieldCreator* MultiFieldCreatorFactory::build(const std::string& builder, c
return factory->make(config);
}

atlas::field::MultiField::MultiField(const eckit::Configuration& config) {
MultiField::MultiField(const eckit::Configuration& config) {
std::string type;
if (!config.get("type", type)) {
ATLAS_THROW_EXCEPTION("Could not find \"type\" in configuration");
Expand All @@ -60,6 +86,13 @@ atlas::field::MultiField::MultiField(const eckit::Configuration& config) {
reset(creator->create(config));
}

void MultiFieldImpl::add(Field& field) {
ATLAS_ASSERT(not fieldset_.has(field.name()), "Field with name \"" + field.name() + "\" already exists!");
fieldset_.add(field);
MultiFieldArrayRegistry::instance().add(field,array_);

}

//-----------------------------------------------------------------------------

} // namespace field
Expand Down
49 changes: 8 additions & 41 deletions src/atlas/field/MultiField.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,51 +41,18 @@ namespace field {
* Fields have to all be of same memory layout and data type
*/

namespace detail {
class ArrayAllocator {
public:
virtual array::Array* allocate(const array::ArraySpec&) = 0;
virtual void deallocate() = 0;
virtual ~ArrayAllocator() = default;
};

class NoArrayAllocator : public ArrayAllocator {
public:
virtual array::Array* allocate(const array::ArraySpec& arrayspec) override { return nullptr; }
virtual void deallocate() override {}
};

class StandardArrayAllocator : public ArrayAllocator {
public:
virtual array::Array* allocate(const array::ArraySpec& arrayspec) override {
array::ArraySpec spec(arrayspec);
return array::Array::create(std::move(spec));
}
virtual void deallocate() override {
// Nothing to deallocate.
// The Array owns the allocated data.
}
};
} // namespace detail

class MultiFieldImpl : public util::Object {
public: // methods
//-- Constructors

MultiFieldImpl() { allocator_ = std::unique_ptr<detail::ArrayAllocator>(new detail::NoArrayAllocator()); }

MultiFieldImpl(const array::ArraySpec& spec, detail::ArrayAllocator* allocator = nullptr) {
if (allocator) {
allocator_ = std::unique_ptr<detail::ArrayAllocator>(allocator);
}
else {
allocator_ = std::unique_ptr<detail::ArrayAllocator>(new detail::StandardArrayAllocator());
}
ATLAS_ASSERT(allocator_);
array_ = std::unique_ptr<array::Array>(allocator_->allocate(spec));
MultiFieldImpl() { }

MultiFieldImpl(const array::ArraySpec& spec) {
array::ArraySpec s(spec);
array_.reset(array::Array::create(std::move(s)));
}

virtual ~MultiFieldImpl() { allocator_->deallocate(); }
virtual ~MultiFieldImpl() {}


//-- Accessors
Expand Down Expand Up @@ -132,11 +99,11 @@ class MultiFieldImpl : public util::Object {
const FieldSet& fieldset() const { return fieldset_; }
FieldSet& fieldset() { return fieldset_; }

void add(Field& field);

public: // temporary public for prototyping
FieldSet fieldset_;
std::unique_ptr<array::Array> array_;
std::unique_ptr<detail::ArrayAllocator> allocator_;
std::shared_ptr<array::Array> array_;
util::Metadata metadata_;
};

Expand Down
12 changes: 5 additions & 7 deletions src/atlas/field/detail/MultiFieldInterface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,8 @@ MultiFieldImpl* atlas__MultiField__create(eckit::Configuration* config) {
}
auto multiarray_shape = array::make_shape(nblk, nfld, nlev, nproma);

MultiFieldImpl* field = new MultiFieldImpl{array::ArraySpec{datatype, multiarray_shape}};
auto& multiarray = field->array();
auto& fieldset = field->fieldset();
MultiFieldImpl* multifield = new MultiFieldImpl{array::ArraySpec{datatype, multiarray_shape}};
auto& multiarray = multifield->array();

size_t multiarray_field_idx = 0;
for (size_t i = 0; i < fields.size(); ++i) {
Expand Down Expand Up @@ -111,14 +110,13 @@ MultiFieldImpl* atlas__MultiField__create(eckit::Configuration* config) {
}
field.set_levels(nlev);
// field.set_blocks(nblk);
ATLAS_ASSERT(not fieldset.has(field.name()), "Field with name \"" + field.name() + "\" already exists!");

fieldset.add(field);
multifield->add(field);

multiarray_field_idx += field_vars;
}
ATLAS_ASSERT(field);
return field;
ATLAS_ASSERT(multifield);
return multifield;
}

void atlas__MultiField__delete(MultiFieldImpl* This) {
Expand Down
165 changes: 87 additions & 78 deletions src/tests/field/test_multifield_ifs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ MultiFieldImpl* MultiFieldCreatorIFS::create(const eckit::Configuration& config)
MultiFieldImpl* multifield = new MultiFieldImpl{array::ArraySpec{datatype, multiarray_shape}};

auto& multiarray = multifield->array();
auto& fieldset = multifield->fieldset();

size_t multiarray_field_idx = 0;
for (size_t i = 0; i < fields.size(); ++i) {
Expand Down Expand Up @@ -129,9 +128,8 @@ MultiFieldImpl* MultiFieldCreatorIFS::create(const eckit::Configuration& config)
}
}
field.set_levels(nlev);
ATLAS_ASSERT(not fieldset.has(field.name()), "Field with name \"" + field.name() + "\" already exists!");

fieldset.add(field);
multifield->add(field);

multiarray_field_idx += field_vars;
}
Expand Down Expand Up @@ -175,81 +173,92 @@ CASE("multifield_create") {
return p.json();
};

Log::info() << "json = " << json() << std::endl;

MultiField multifield{eckit::YAMLConfiguration{json()}};

const auto nblk = multifield.array().shape(0);
const auto nvar = multifield.array().shape(1);
const auto nfld = multifield.size();
EXPECT_EQ(nfld, 5);
EXPECT_EQ(nvar, 9);

EXPECT_EQ(multifield.size(), 5);
EXPECT(multifield.has("temperature"));
EXPECT(multifield.has("pressure"));
EXPECT(multifield.has("density"));
EXPECT(multifield.has("clv"));
EXPECT(multifield.has("wind_u"));

Log::info() << multifield.field("temperature") << std::endl;
Log::info() << multifield.field("pressure") << std::endl;
Log::info() << multifield.field("density") << std::endl;
Log::info() << multifield.field("clv") << std::endl;
Log::info() << multifield.field("wind_u") << std::endl;

auto temp = array::make_view<Value, 3>(multifield.field("temperature"));
auto pres = array::make_view<Value, 3>(multifield.field("pressure"));
auto dens = array::make_view<Value, 3>(multifield.field("density"));
auto clv = array::make_view<Value, 4>(multifield.field("clv")); // note rank 4
auto wind_u = array::make_view<Value, 3>(multifield.field("wind_u"));

EXPECT_EQ(multifield[0].name(), "temperature");
EXPECT_EQ(multifield[1].name(), "pressure");
EXPECT_EQ(multifield[2].name(), "density");
EXPECT_EQ(multifield[3].name(), "clv");
EXPECT_EQ(multifield[4].name(), "wind_u");

auto block_stride = multifield.array().stride(0);
auto field_stride = nproma * nlev;
auto level_stride = nproma;
auto nproma_stride = 1;

temp(1, 2, 3) = 4;
pres(5, 6, 7) = 8;
dens(9, 10, 11) = 12;
clv(13, 2, 14, 15) = 16;
wind_u(17, 18, 3) = 19;

EXPECT_EQ(temp.stride(0), block_stride);
EXPECT_EQ(temp.stride(1), level_stride);
EXPECT_EQ(temp.stride(2), nproma_stride);
EXPECT_EQ(temp.size(), nblk * nlev * nproma);

EXPECT_EQ(clv.stride(0), block_stride);
EXPECT_EQ(clv.stride(1), field_stride);
EXPECT_EQ(clv.stride(2), level_stride);
EXPECT_EQ(clv.stride(3), nproma_stride);

EXPECT_EQ(clv.size(), nblk * 5 * nlev * nproma);


// Advanced usage, to access underlying array. This should only be used
// in a driver and not be exposed to algorithms.
{
auto multiarray = array::make_view<Value, 4>(multifield);
EXPECT_EQ(multiarray.stride(0), block_stride);
EXPECT_EQ(multiarray.stride(1), field_stride);
EXPECT_EQ(multiarray.stride(2), level_stride);
EXPECT_EQ(multiarray.stride(3), nproma_stride);

EXPECT_EQ(multiarray(1, 0, 2, 3), 4.);
EXPECT_EQ(multiarray(5, 1, 6, 7), 8.);
EXPECT_EQ(multiarray(9, 2, 10, 11), 12.);
EXPECT_EQ(multiarray(13, 5, 14, 15), 16.);
EXPECT_EQ(multiarray(17, 8, 18, 3), 19.);

EXPECT_EQ(multiarray.size(), nblk * nvar * nlev * nproma);
SECTION("Print configuration") {
Log::info() << "json = " << json() << std::endl;
}

SECTION("test") {
MultiField multifield{eckit::YAMLConfiguration{json()}};

const auto nblk = multifield.array().shape(0);
const auto nvar = multifield.array().shape(1);
const auto nfld = multifield.size();
EXPECT_EQ(nfld, 5);
EXPECT_EQ(nvar, 9);

EXPECT_EQ(multifield.size(), 5);
EXPECT(multifield.has("temperature"));
EXPECT(multifield.has("pressure"));
EXPECT(multifield.has("density"));
EXPECT(multifield.has("clv"));
EXPECT(multifield.has("wind_u"));

Log::info() << multifield.field("temperature") << std::endl;
Log::info() << multifield.field("pressure") << std::endl;
Log::info() << multifield.field("density") << std::endl;
Log::info() << multifield.field("clv") << std::endl;
Log::info() << multifield.field("wind_u") << std::endl;

auto temp = array::make_view<Value, 3>(multifield.field("temperature"));
auto pres = array::make_view<Value, 3>(multifield.field("pressure"));
auto dens = array::make_view<Value, 3>(multifield.field("density"));
auto clv = array::make_view<Value, 4>(multifield.field("clv")); // note rank 4
auto wind_u = array::make_view<Value, 3>(multifield.field("wind_u"));

EXPECT_EQ(multifield[0].name(), "temperature");
EXPECT_EQ(multifield[1].name(), "pressure");
EXPECT_EQ(multifield[2].name(), "density");
EXPECT_EQ(multifield[3].name(), "clv");
EXPECT_EQ(multifield[4].name(), "wind_u");

auto block_stride = multifield.array().stride(0);
auto field_stride = nproma * nlev;
auto level_stride = nproma;
auto nproma_stride = 1;

temp(1, 2, 3) = 4;
pres(5, 6, 7) = 8;
dens(9, 10, 11) = 12;
clv(13, 2, 14, 15) = 16;
wind_u(17, 18, 3) = 19;

EXPECT_EQ(temp.stride(0), block_stride);
EXPECT_EQ(temp.stride(1), level_stride);
EXPECT_EQ(temp.stride(2), nproma_stride);
EXPECT_EQ(temp.size(), nblk * nlev * nproma);

EXPECT_EQ(clv.stride(0), block_stride);
EXPECT_EQ(clv.stride(1), field_stride);
EXPECT_EQ(clv.stride(2), level_stride);
EXPECT_EQ(clv.stride(3), nproma_stride);

EXPECT_EQ(clv.size(), nblk * 5 * nlev * nproma);


// Advanced usage, to access underlying array. This should only be used
// in a driver and not be exposed to algorithms.
{
auto multiarray = array::make_view<Value, 4>(multifield);
EXPECT_EQ(multiarray.stride(0), block_stride);
EXPECT_EQ(multiarray.stride(1), field_stride);
EXPECT_EQ(multiarray.stride(2), level_stride);
EXPECT_EQ(multiarray.stride(3), nproma_stride);

EXPECT_EQ(multiarray(1, 0, 2, 3), 4.);
EXPECT_EQ(multiarray(5, 1, 6, 7), 8.);
EXPECT_EQ(multiarray(9, 2, 10, 11), 12.);
EXPECT_EQ(multiarray(13, 5, 14, 15), 16.);
EXPECT_EQ(multiarray(17, 8, 18, 3), 19.);

EXPECT_EQ(multiarray.size(), nblk * nvar * nlev * nproma);
}
}

SECTION("test registry") {
{
Field field = MultiField {eckit::YAMLConfiguration{json()}}.field("temperature");
auto temp = array::make_view<Value,3>(field);
}
}
}

Expand Down

0 comments on commit 225a0f4

Please sign in to comment.