Open sbrisard opened 2 years ago
As a general scientific development principle, I tend to not think too much about APIs in advance. I know this is counter to what people say, that you should plan software development carefully, but I have just not found that to be useful, especially in scientific code whose purpose strongly depends on the research objectives of the moment. Indeed careful APIs easily lead to ossification of structure and then bloat. Rather, start from having something that works, even though it hurts aesthetics, and then play with it and figure out its pain points and the ways you might want to extend it later. We had several discussions about this with Michael at the beginning. He tends to be on the careful/planning side, and I tend to be on the careless/experimental one: we met in the middle and I think it was enriching for both of us (at least it was for me :-p)
That said I think your structure looks good? In general I would not sweat too much the structures: functions are more important than structures. Nothing forces you to use multiple dispatch if it's not natural for you.
If you wanted you could also use LinearOperators all the way, eg defining a RealToFourier and FourierToReal operator, and then doing something like (op::GreenOperator, x) = FourierToReal op.kernel RealToFourier x, with *(kernel, x) defined according to the physics. We have not done that in DFTK because we found functions to just be more flexible.
Regarding names we use real/fourier also even if sometimes the real quantities are complex numbers.
Thanks Antoine, I usually tend to aggre with you: start to write the program, then refactor. In the present case, I didn't even know where to start... but writing this post actually gave me a starting point!
That said I think your structure looks good? In general I would not sweat too much the structures: functions are more important than structures. Nothing forces you to use multiple dispatch if it's not natural for you.
I still have a lot to learn about Julia, because I don't really understand :'(
I am not sure about the general use of LinearOperators. I would not be able to use overloaded operators anyway, because I would like to operate in-place. Or did I miss something?
OK for real/fourier then.
Again, thanks for your time!
I still have a lot to learn about Julia, because I don't really understand :'(
I just meant that you don't necessarily have to fit in with julia's preferred way of doing things: the julia gods won't be mad at you if you don't use multiple dispatch and stick to a "C-like" imperative programming style. I did find that julia was not really friendly to OOP-style programming style, though.
I am not sure about the general use of LinearOperators. I would not be able to use overloaded operators anyway, because I would like to operate in-place. Or did I miss something?
I believe LinearMaps has support for in-place operations
Overview
Continuous Green operators
The (continuous) Green operator
Γ
is a linear operator that maps a field inℝ^d
onto a field inℝ^d
. It is attached to a specific physics: elasticity, conductivity, etc… In the remainder of this text, we will stick with the terminology of mechanics: the input ofΓ
will be referred to as the stress-polarizationτ
, while the output ofΓ
will be referred to as the strainε
. Note that the input and output can be tensors of any order; they are implemented in Scapin as column-vectors.For periodic boundary conditions over the unit-cell
Ω = (0, L₁) × (0, L₂) × …
, the Green operator is defined in the Fourier spacewhere
and
The Fourier modes of
Γ
,Γ^(k)
, are known in closed form. They of course depend on the physics considered.Discrete Green operators
For numerical simulations, the unit-cell
Ω
is discretized into a cartesian grid of sizeN = (N₁, N₂, …)
. The size of the cells ish = (h₁, h₂, …)
.𝒫
denotes the set of cell-indices (in Julia language,𝒫 = CartesianIndices(N)
). The lettersp
andq
will systematically be reserved to cell indices (in the real space), while the lettersm
andn
will systematically be reserved to frequency indices (in the Fourier space).We will use square brackets
[…]
in the remainder for subscripting:τₕ[i, p₁, p₂, …]
(or, in short,τₕ[i, p]
) is thei
-th component of the stress-polarization at cellp = (p₁, p₂, …)
. Likewise,τₕ^[i, n₁, n₂, …]
(or, in short,τₕ^[i, n]
) is thei
-th component of then
-th mode ofDFT(τₕ)
.We need to consider discrete Green operators, hereafter denoted by a subscript “
h
” (Γₕ
), which refers to the grid spacing. The discrete Green operatorΓₕ
is a linear operator that maps the discrete fieldτₕ
(defined over the grid) onto a discrete fieldεₕ
(also defined over the grid). All discrete Green operators implemented in Scapin are defined in the Fourier spacewhere
τₕ^
andεₕ^
are the discrete Fourier transforms ofτₕ
andεₕ
(along the lastd
axes). Therefore, application of the discrete Green operator reduces to the following operations: DFT of input → frequency-wise multiplication → inverse DFT.The Fourier modes
Γₕ^[n]
of the discrete Green operator are generally defined as a linear combination of some Fourier modes of the continuous Green operatorwhere the scalars
ϖ[𝓁]
and vectorsq[n, 𝓁]
define the discretization scheme.Implementation
In this section, we discuss various possible implementations of the Physical equations → Continuous Green operator → Discrete Green operator hierarchy of objects. Before we proceed, we observe that the following products
are implemented in a matrix-free form
the precise interface of the above functions is to be discussed in the present section.
Continuous Green operators
Option 1
Continuous Green operators are associated to a specific physical problem that must first be defined. For example
(
d
is the number of spatial dimensions,T
is the scalar type). Then we can define the associated Green operatorswhere
M
is the material type (for lack of a better name) :Fourier
,Hooke
, …Finally, we can define how Green operators operate in Fourier space
(equivalent of
muladd
should also be implemented). Note thatGreenOperator
should also haved
andT
in its list of type parameters.Question 1: is
apply_fourier
a name that is precise enough? I'd rather usemul
, which seems to be the Julia convention, but then I would have a name clash for discrete Green operators, whereapply
can operate both in the Fourier space and in the real space.Question 2: should the signature be
apply_fourier(Γ, τ, k)
orapply_fourier(Γ, k, τ)
? What does feel more natural? How would these methods be extended to allow formuladd
-like operations?Option 2
Objects of type
Fourier
andHooke
play a very minor role sinceGreenOperator{M}
are really wrappers aroundM
.The benefit of the distinction between the material type and its associated Green operator is the fact that it is possible to define a
apply
operator for an object of typeM
: this object would apply the constitutive law to its input:σ = C * ε
, whereC
is of typeM
.The drawback is the multiplication of type parameters.
An alternative would be to directly define objects of type
FourierGreenOperator
,HookeGreenOperator
, etc… and theapply_fourier!/apply_fourier
methods.Question 3: what is your opinion on option 2? Please elaborate!
Discrete Green operators
Discrete Green operators are associated to continuous Green operators:
Note that again, the underlying number of spatial dimensions
d
and scalar typeT
should be in the list of type parameters.The
τ^ ↦ Γₕ^[n]⋅τ^
operation would then be implemented as followsAnd the
τ^ ↦ Γ^(k)⋅τ^
convolution operationwhich would be implemented in principle as follows (implementation is not syntactically correct and unoptimized in terms of memory allocation)
Question 4: is
apply
an appropriate function name? Should we preferapply_real
? Would it not be confusing, since sometimes, working in the real space would still require complex numbers (owing to time Fourier transforms – e.g. for viscoelasticity).