epicf / ef_python

Low-energy charged particles' dynamics simulation using particle-in-cell method (Ef, python version)
MIT License
8 stars 11 forks source link

Define common geometric primitives classes; use them in particles sources and inner regions #21

Open noooway opened 5 years ago

noooway commented 5 years ago

Currently there is some code duplication between InnerRegions and ParticleSources concerning their geometric shape. It seems natural to define separate classes for geometric primitives and utilize them.

The work has been started in https://github.com/epicf/ef_python/tree/geometric-primitives branch.
For each primitive it is necessary to provide at least two methods: a way to check if a point with given coordinates is inside this primitive (to remove particles after collision) and a way to generate a point inside it (for sources).

Besides, annoying problem is to read primitive definitions from config. There is no natural support for subsections in INI-files so it is necessary to write geometric primitives as strings and then parse those string in class constructors. It has been implemented (though not tested yet), but probably there is a better solution.

Current status: It is necessary to test initialization of classes from text string. And it is necessary to replace current geometry-related code in inner regions and sources with this new classes. Then it would be necessary to update and test examples.

noooway commented 5 years ago

Что есть:

В расчетной области иногда нужно задавать геометрические объекты. Они могут либо определять форму источника частиц, либо форму электродов, от которых нужно считать поле.

В перспективе хочется импортировать геометрические объекты из CAD-систем. Например, сделать поддержку STL или STEP форматов. Но для простых тестов полезно иметь минимальную поддержку геометрии без внешних зависимостей.

Т.е. интерфейс мог бы выглядеть как-то так (см. https://github.com/epicf/ef_python/blob/geometric-primitives/GeometricPrimitives.py ):

class GeometricPrimitive:

    def __init__(self):
        self.expression = None

    @classmethod
    def init_primitive_from_string(cls, expression):
        pass

    @classmethod
    def init_primitive_from_hdf5(cls, h5field):
        pass

    def check_if_point_inside(self, point):
        pass

    def generate_random_point_uniform(self, random_in_range_function):
        pass

    def write_hdf5_attributes(self, h5field):
        pass
noooway commented 5 years ago

Что можно сделать:

Более-менее несложная вещь, которую можно попробовать реализовать - это конструктивная сплошная геометрия https://en.wikipedia.org/wiki/Constructive_solid_geometry . В ней определяется несколько трехмерных примитивов - цилиндр, сфера, куб, конус и др. , стандартные булевы операции - объединение, вычитание, пересечение и стандартные трансформации - сдвиг, масштабирование, вращение. Из них собираются объекты.

Получается что-то типа:

(difference (cylinder r=5, l=10) (cylinder r=3, l=10))
или
tube = Cylinder(r=5, l=10) - Cylinder(r=3, l=10)

Такой подход реализован в OpenSCAD https://en.wikipedia.org/wiki/OpenSCAD .

 difference()
  {
    cube([20,20,20]); // a 20 mm cube
    translate([10,10,-1]) // start 1 mm below the surface
      cylinder(r=5,h=20+1+1); // and go 1 mm above
  }

Также есть питон-модуль https://github.com/SolidCode/SolidPython , который позволяет использовать питоновский синтаксис для описание объектов и генерировать OpenSCAD-фаилы.

from solid import *
from solid.utils import *
d = cube(5) + right(5)(sphere(5)) - cylinder(r=2, h=6)

Думаю, что имело бы смысл реализовать в программе что-то похожее. Тогда можно будет писать в конфиге такие вещи:

[InnerRegion.tube]
potential = 0.0
geometry = (difference (cylinder r=5, l=10) (cylinder r=3, l=10))
noooway commented 5 years ago

Необходимость инициализировать объекты из текстовой строки создает проблемы - нужно писать парсер для строк.

Можно использовать S-выражения:

(difference (cylinder r=5, l=10) (cylinder r=3, l=10))

Они удачно подходят для такой задачи. Готовые парсеры можно найти в https://rosettacode.org/wiki/S-Expressions .

Можно использовать подход SolidPython и использовать парсер и синтаксис питона, т.е. конструкции типа

tube = Cylinder(r=5, l=10) - Cylinder(r=3, l=10)

Для разбора математических формул в SimpleEval https://github.com/epicf/ef_python/blob/geometric-primitives/libs/simpleeval/simpleeval.py уже используется AST https://docs.python.org/3/library/ast.html . Думаю, на основе AST можно сделать еще один нужный парсер.

Можно попытаться найти парсер OpenSCAD фаилов на питоне.

Возможно, есть еще какие-то варианты.

noooway commented 5 years ago

Также имеет смысл сделать визуализацию геометрии. Можно сделать либо экспорт в OpenSCAD формат, либо посмотреть визуализацию в jupyter-модуле https://github.com/epicf/ef_python/blob/master/ef/config/visualizer.py , либо в https://github.com/nickc92/ViewSCAD .

noooway commented 5 years ago

Можно попробовать использовать json в качестве формата и json-модуль в качестве парсера. После считывания строки в словарь разбор словаря уже будет проще.

Еще нашлось несколько библиотек на питоне:

Еще нашлись любопытные альтернативы OpenSCADу: