Skip to content

Commit

Permalink
Merge pull request #229 from ecmwf/feature/MultiField
Browse files Browse the repository at this point in the history
Add MultiField to pack field allocations
  • Loading branch information
wdeconinck authored Oct 7, 2024
2 parents 91f1b75 + 83cd8cd commit 051dfc4
Show file tree
Hide file tree
Showing 18 changed files with 1,935 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/atlas/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,24 @@ field/FieldSet.cc
field/FieldSet.h
field/MissingValue.cc
field/MissingValue.h
field/MultiField.cc
field/MultiField.h
field/MultiFieldCreator.cc
field/MultiFieldCreator.h
field/MultiFieldCreatorIFS.cc
field/MultiFieldCreatorIFS.h
field/MultiFieldCreatorArray.cc
field/MultiFieldCreatorArray.h
field/State.cc
field/State.h
field/detail/FieldImpl.cc
field/detail/FieldImpl.h
field/detail/FieldInterface.cc
field/detail/FieldInterface.h
field/detail/MultiFieldImpl.cc
field/detail/MultiFieldImpl.h
field/detail/MultiFieldInterface.cc
field/detail/MultiFieldInterface.h
field/detail/MissingValue.cc
field/detail/MissingValue.h
)
Expand Down
74 changes: 74 additions & 0 deletions src/atlas/field/MultiField.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* (C) Copyright 2013 ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

#include "atlas/field/MultiField.h"

#include <iomanip>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <mutex>

#include "atlas/field/MultiFieldCreator.h"
#include "atlas/field/detail/MultiFieldImpl.h"
#include "atlas/runtime/Exception.h"

namespace atlas {
namespace field {

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

MultiField::MultiField(const eckit::Configuration& config) {
std::string type;
if (!config.get("type", type)) {
ATLAS_THROW_EXCEPTION("Could not find \"type\" in configuration");
}
std::unique_ptr<MultiFieldCreator> creator(MultiFieldCreatorFactory::build(type, config));
reset(creator->create(config));
}

MultiField::MultiField(const array::DataType datatype, const std::vector<int>& shape,
const std::vector<std::string>& var_names) {
std::unique_ptr<MultiFieldCreator> creator(MultiFieldCreatorFactory::build("MultiFieldCreatorArray"));
reset(creator->create(datatype, shape, var_names));
}

const Field& MultiField::field(const std::string& name) const { return get()->field(name); }
Field& MultiField::field(const std::string& name) { return get()->field(name); }
bool MultiField::has(const std::string& name) const { return get()->has(name); }
std::vector<std::string> MultiField::field_names() const { return get()->field_names(); }

const Field& MultiField::field(const idx_t idx) const { return get()->field(idx); }
Field& MultiField::field(const idx_t idx) { return get()->field(idx); }
idx_t MultiField::size() const { return get()->size(); }

const Field& MultiField::operator[](const idx_t idx) const { return get()->field(idx); }
Field& MultiField::operator[](const idx_t idx) { return get()->field(idx); }

const Field& MultiField::operator[](const std::string& name) const { return get()->field(name); }
Field& MultiField::operator[](const std::string& name) { return get()->field(name); }

const util::Metadata& MultiField::metadata() const { return get()->metadata(); }
util::Metadata& MultiField::metadata() { return get()->metadata(); }

MultiField::operator const array::Array&() const { return get()->array(); }
MultiField::operator array::Array&() { return get()->array(); }

MultiField::operator const FieldSet&() const { return get()->fieldset_; }
MultiField::operator FieldSet&() { return get()->fieldset_; }

const array::Array& MultiField::array() const { return get()->array(); }
array::Array& MultiField::array() { return get()->array(); }

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

} // namespace field
} // namespace atlas
132 changes: 132 additions & 0 deletions src/atlas/field/MultiField.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* (C) Copyright 2013 ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

/// @author Willem Deconinck
/// @date June 2015

#pragma once

#include <map>
#include <vector>

#include "atlas/array/Array.h"
#include "atlas/field/Field.h"
#include "atlas/field/FieldSet.h"
#include "atlas/util/Config.h"
#include "atlas/util/Factory.h"
#include "atlas/util/Metadata.h"
#include "atlas/util/Object.h"
#include "atlas/util/ObjectHandle.h"

namespace eckit {
class Parametrisation;
}

namespace atlas {
namespace field {
class MultiFieldImpl;
}
}

namespace atlas {
namespace field {

/**
* \brief MultiField class that owns a collection of fields that are co-allocated
*
* Fields can only be described by parametrisation during the construction.
* Once setup, no additional fields can be added.
*
* Fields have to all be of same memory layout and data type
*/

class MultiField : public util::ObjectHandle<MultiFieldImpl> {
public: // methods
//-- Constructors
using Handle::Handle;

MultiField(const eckit::Configuration&);
MultiField(const array::DataType datatype, const std::vector<int>& shape,
const std::vector<std::string>& var_names);

//-- Accessors

const Field& field(const std::string& name) const;
Field& field(const std::string& name);
bool has(const std::string& name) const;
std::vector<std::string> field_names() const;

const Field& field(const idx_t idx) const;
Field& field(const idx_t idx);
idx_t size() const;

const Field& operator[](const idx_t idx) const;
Field& operator[](const idx_t idx);

const Field& operator[](const std::string& name) const;
Field& operator[](const std::string& name);

const util::Metadata& metadata() const;
util::Metadata& metadata();

// -- Modifiers

/// @brief Implicit conversion to Array
operator const array::Array&() const;
operator array::Array&();

operator const FieldSet&() const;
operator FieldSet&();

/// @brief Access contained Array
const array::Array& array() const;
array::Array& array();

private:
template<typename datatype>
void create(const std::vector<int> shape, const std::vector<std::string> var_names);
};

/**
* \brief MultiFieldArrayRegistry
*/

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_;

};

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

} // namespace field
} // namespace atlas
58 changes: 58 additions & 0 deletions src/atlas/field/MultiFieldCreator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* (C) Copyright 2013 ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

// file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end

#include "atlas/field/MultiFieldCreator.h"

#include <map>
#include <sstream>

#include "eckit/thread/AutoLock.h"
#include "eckit/thread/Mutex.h"

#include "atlas/field/MultiFieldCreatorIFS.h"
#include "atlas/field/MultiFieldCreatorArray.h"
#include "atlas/grid/Grid.h"
#include "atlas/runtime/Exception.h"
#include "atlas/runtime/Log.h"


namespace atlas {
namespace field {


namespace {

void force_link() {
static struct Link {
Link() {
MultiFieldCreatorBuilder<MultiFieldCreatorIFS>();
MultiFieldCreatorBuilder<MultiFieldCreatorArray>();
}
} link;
}

} // namespace

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

MultiFieldCreator::MultiFieldCreator() = default;

MultiFieldCreator::~MultiFieldCreator() = default;

MultiFieldCreator* MultiFieldCreatorFactory::build(const std::string& builder, const eckit::Configuration& config) {
force_link();
auto factory = get(builder);
return factory->make(config);
}

} // namespace field
} // namespace atlas
89 changes: 89 additions & 0 deletions src/atlas/field/MultiFieldCreator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* (C) Copyright 2013 ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

#ifndef atlas_field_MultiFieldCreator_h
#define atlas_field_MultiFieldCreator_h

#include <string>

#include "atlas/util/Object.h"

#include "atlas/field/MultiField.h"

namespace eckit {
class Configuration;
}

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

namespace atlas {
namespace field {

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

/*!
* \brief Base class for creating new multifields based on Configuration
*
* \details
* Example to create field[100][3] of default type double:
* \code{.cpp}
* FieldImpl* field = Field::create(
* Config
* ("creator","ArraySpec") // ArraySpec MultiFieldCreator
* ("shape",array::make_shape(100,3)) // Rank 2 field with indexing [100][3]
* );
* \endcode
*/
class MultiFieldCreator : public util::Object {
public:
MultiFieldCreator();
MultiFieldCreator(const eckit::Configuration& config);

virtual ~MultiFieldCreator();

virtual MultiFieldImpl* create(const eckit::Configuration& config = util::Config()) const = 0;
virtual MultiFieldImpl* create(const array::DataType datatype, const std::vector<int>& shape,
const std::vector<std::string>& var_names) const = 0;
};

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

class MultiFieldCreatorFactory : public util::Factory<MultiFieldCreatorFactory> {
public:
static std::string className() { return "MultiFieldCreatorFactory"; }

/*!
* \brief build MultiFieldCreator with options specified in parametrisation
* \return mesh generator
*/
static MultiFieldCreator* build(const std::string&, const eckit::Configuration& = util::Config());

using Factory::Factory;

private:
virtual MultiFieldCreator* make() = 0;
virtual MultiFieldCreator* make(const eckit::Configuration&) = 0;
};

template <class T>
class MultiFieldCreatorBuilder : public MultiFieldCreatorFactory {
virtual MultiFieldCreator* make() { return new T(); }
virtual MultiFieldCreator* make(const eckit::Configuration& config) { return new T(config); }

public:
using MultiFieldCreatorFactory::MultiFieldCreatorFactory;
};

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

} // namespace field
} // namespace atlas

#endif
Loading

0 comments on commit 051dfc4

Please sign in to comment.