Я реализую код конечного элемента.
В методах конечных элементов нам нужны интегратор и интерполятор. Интегратор — это объект, который выполняет численное интегрирование геометрического объекта, например четырехугольника, треугольника и т. Д. Интегратор размещает несколько точек интегрирования или абсцисс внутри геометрических объектов, а затем использует интерполятор для аппроксимации значения функция в этих точках интеграции.
Например, четырехсторонний объект может использовать интегратор, который использует 4 точки интеграции.
* ------------- *
| |
| @ @ |
| |
| |
| @ @ |
| |
* ------------- *
где @ представляет местоположение точек интеграции. Интерполятор аппроксимирует значение функции в этих точках интеграции, используя значения в угловых узлах, представленных *. Вы можете думать об этом как о том, что каждое значение @ является своего рода средним значением всех *.
Следующая таблица показывает для удобства связь между различными классами, используемыми в этом вопросе:
Interpolator
|
| is a base for:
v
Interpolator_Template
|
| is a base for:
|
------------------------------------------
| | |
| | More shapes
V V
Interpolator_Quad Interpolator_Tria
| | |
| ... More QUADs More TRIAs
v
Interpolator_Quad_04Integrator
|
| has member variable
v
vector<Abscissa*> abscissa
|
| Abscissa is a base class for:
|
--------------------------------
| | |
| More shapes |
V V
Abscissa_Quad Abscissar_Tria
Каждая геометрическая форма имеет свою систему координат, поэтому мой интегратор и абсциссы выглядят так:
class Integrator {
public:
void
integrate();
private:
/**
* An interpolator class.
*/
Interpolator * _interpolator;
/**
* List of abscissae (for the quadrilateral shown above, _abscissae.size() == 4).
*/
std::vector< Abscissa * > _abscissae;
};
Базовый класс для всех естественных координат абсцисс.
class Abscissa {
};
Четырехсторонняя абсцисса действует на естественные координаты ξ и η.
class Abscissa_Quad final : public Abscissa {
public:
const double xi;
const double eta;
};
Абсцисса треугольника действует на натуральные координаты ζ1, ζ2 и ζ3.
class Abscissa_Tria final : public Abscissa {
public:
const double zeta_1;
const double zeta_2;
const double zeta_3;
};
Реализация интегратора будет интегрирована примерно так:
void
Integrator::integrate()
{
for ( Abscissa * abscissa : _abscissae ) {
_intepolator->eval_det_J( abscissa );
}
}
Все идет нормально. Позвольте мне показать вам мой класс интерполятор.
class Interpolator {
public:
/**
* Evaluate the determinant of the Jacobian (important for numerical integration).
*
* @note Common interface for all abscissa types.
*
* @param[in] abscissa Integration abscissa.
* @return Shape functions.
*/
virtual double
eval_det_J(
const Abscissa * abscissa ) const = 0;
};
Из интерполятора я извлекаю классы для всех геометрических фигур.
Вы можете заметить, что я использую класс Interpolator_Template в качестве основы. Проигнорируйте это сейчас, я объясню детали через секунду.
Этот класс содержит функции, общие для всех четырехугольников.
class Interpolator_Quad : public Interpolator_Template< Abscissa_Quad > {
public:
// ... More functions common to all quadrilaterals.
};
Этот производный класс соответствует четырехугольнику, нарисованному в начале этого вопроса.
Причина, по которой он получен, заключается в том, что могут существовать четырехугольники с большим количеством узлов интерполяции.
Этот класс реализует элемент QUAD_04 (четырехугольник с 4 узлами интерполяции), но в конечных элементах у нас также есть QUAD_08, QUAD_09 и т. Д.
class Interpolator_Quad_04 final : public Interpolator_Quad {
public:
double
eval_det_J(
const Abscissa_Quad * abscissa ) const;
};
double
Interpolator_Quad_04::eval_det_J(
const Abscissa_Quad * abscissa ) const
{
// Important! Get coordinates from an Abscissa_Quad object.
const double xi = abscissa.xi;
const double eta = abscissa.eta;
double det_J = ...
// ... Perform some computations and return the determinant of the Jacobian.
return det_J;
}
Позвольте мне вернуться к классу Interpolator_Template, который я пропустил, чтобы объяснить ранее. В какой-то момент в моем коде я выполняю понижение с объекта Abscissa * до объекта Abscissa_Quad *. Я добился этого, используя шаблонный класс в сочетании с не виртуальным шаблоном интерфейса.
template< class Abscissa_Derived >
class Interpolator_Template : public Interpolator {
public:
/**
* Implements Interpolator::eval_det_J.
*/
double
eval_det_J(
const Abscissa * abscissa ) const;
protected:
/**
* Implemented by Interpolator_Quad_04 in this example.
*/
virtual double
eval_det_J(
const Abscissa_Derived * abscissa ) const = 0;
private:
Abscissa_Derived *
eval_abscissa(
const Abscissa * abscissa ) const;
};
template< class Abscissa_Derived >
double
Interpolator_Template< Abscissa_Derived >::eval_det_J(
const Abscissa * abscissa ) const
{
Abscissa_Derived * abscissa_type = this->eval_abscissa( abscissa );
double det_J = this->eval_det_J( abscissa_type );
return det_J;
}
template< class Abscissa_Derived >
Abscissa_Derived *
Interpolator_Template< Abscissa_Derived >::eval_abscissa(
const Abscissa * abscissa ) const
{
// Dynamic cast occurs here.
// I will include some check later to check for nullptr.
return dynamic_cast< Abscissa_Derived * >( abscissa )
}
Я уверен, что этот код содержит ошибки, потому что мне нужно было копировать и вставлять только то, что я считал необходимым, чтобы донести свою мысль, а также выполнить изменения. Я надеюсь, однако, что моя идея проходит правильно.
Я знаю, что даункинг обычно является запахом кода, поэтому прежде чем я начал внедрять интеграторы и интерполяторы для всех геометрических форм в конечных элементах, я хотел бы узнать ваше мнение.
Это последний шаблон проектирования, который я реализовал. Я объясню другие проекты, которые я попытался ниже; однако, вы можете пропустить чтение этого раздела.
Шаблон проектирования с двойной диспетчеризацией (в частности, шаблон посетителя), в котором производным является интегратор, а не интерполятор. Например, у меня был Integrator_Quad_04, а не Interpolator_Quad_04. Интегратор_Quad_04 имел переменную-член Abscissa_Quad, так как абсциссы больше не были получены.
class Integrator_Quad_04 final : public Integrator {
private:
std::vector< Abscissa_Quad * > _abscissae;
public:
double
eval_det_J(
const std::size_t & index,
const Interpolator * interpolator ) const
{
// The interpolator acts as the visitor.
interpolator->eval_det_J( _abscissa[index] );
}
}
/// Abscissa_Quad is no longer derived from Abscissa.
class Abscissa_Quad {
public:
const double xi;
const double eta;
};
Затем интерполятор становится посетителем класса интегратора и обращается к его переменной-члену _abscissae. Я решил не идти по этому проекту, потому что тогда интерполятор должен был бы быть реализован на основе операций, а не формы.
class Interpolator {
// ...
};
class Eval_Det_J : public Interpolator {
double
eval_det_J(
const Abscissa_Quad * abscissa ) const;
double
eval_det_J(
const Abscissa_Tria * abscissa ) const;
};
Я пытался сделать что-то с множественной диспетчеризацией, но количество функций, необходимых для всех форм, росло довольно быстро.
Несколько вариантов двойной отправки + шаблонов.
Я нашел текущий шаблон дизайна, который я использую здесь:
Объектно-ориентированная задача проектирования, принцип подстановки Лискова
Как вы могли догадаться из кода, я компилирую с использованием C ++ 11.
Вы можете спросить, почему я не просто объединяю интегратор и интерполятор в один класс, и ответ таков: интегратор может работать на поддомене четырехугольника. Например, я мог бы ввести фиктивный треугольник внутри четырехугольника и поместить точки интегрирования внутри треугольника, но я все равно использовал бы четырехугольную интерполяцию для аппроксимации решения внутри точек треугольника. Конечно, мне нужно было бы реализовать отображение между треугольником и четырехугольными координатами, но это проблема для другого дня.
Я хочу знать, думаете ли вы, что уныние — не плохое решение этой проблемы, или я что-то упускаю. Возможно, я не знаю шаблон проектирования, который решает эту проблему, или, возможно, моя архитектура полиморфизма неверна.
Любые отзывы приветствуются. Спасибо!
Я публикую это как ответ, потому что это слишком много для комментария, и, возможно, это поможет.
Вы могли бы сделать Integrator
класс шаблона:
template<class Shape>
class Integrator {
typedef typename Shape::AbscissaType AbscissaType;
public:
void integrate() const {
for (const AbscissaType& abscissa : _abscissae) {
_intepolator->eval_det_J(abscissa);
}
}
template<class OtherShape>
Integrator<OtherShape> convert(/* maybe pass range of abscissae indices */) const {
// Abscissae classes must have converting constructors like AbscissaTria(const AbscissaQuad&);
std::vector<typename OtherShape::AbscissaType> newAbscissae(_abscissae.begin(), _abscissae.end());
// initialize resulting integrator with newAbscissae
}
Interpolator_Template<AbscissaType> _interpolator;
std::vector<AbscissaType> _abscissae;
};
Конкретные интеграторы наследуются от соответствующего базового шаблона, если он все еще необходим:
class Integrator_Quad_04 : public Integrator<Quad> {
};
Так что вам не нужна база Interpolator
класс и eval_det_J
является не виртуальной функцией, принимающей соответствующий тип абсцисс:
template<class AbscissaType>
class Interpolator_Template {
public:
double eval_det_J(const AbscissaType& abscissa) const;
}
я добавил Shape
здесь, чтобы подчеркнуть, что тип абсцисс зависит от формы, и у вас могут быть другие вещи, которые также зависят от формы:
struct Tria {
typedef AbscissaTria AbscissaType;
static const size_t NUM_EDGES = 3; // just example
};
struct Quad {
typedef AbscissaQuad AbscissaType;
static const size_t NUM_EDGES = 4; // just example
};
Я думаю, что у вас уже есть такие классы в вашем коде.
Также обратите внимание, что вам также не нужна база Abscissa
все классы абсцисс становятся независимыми.
РЕДАКТИРОВАТЬ: если вам нужно преобразовать абсциссы в другой тип, вы можете реализовать следующую функцию в Integrator
учебный класс:
template<class OtherShape>
Integrator<OtherShape> convert(/* maybe pass range of abscissae indices */) const {
}
РЕДАКТИРОВАТЬ 2: Добавлен пример реализации convert
в Integrator
учебный класс.