Skip to content

Commit

Permalink
Change polarizations and q-vectors convention
Browse files Browse the repository at this point in the history
Cartesian coordinates are used to indicate polarization of
incoming and outgoing photons, as well as for q-vectors.
  • Loading branch information
bastonero committed Feb 9, 2024
1 parent 461b0b3 commit cef301a
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 69 deletions.
12 changes: 6 additions & 6 deletions docs/source/howto/overrides.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(howto-overrides)=

# How-to use protocols and overrides
# Protocols and overrides

:::{important}
The following how-to assume that you are familiar with submitting the workflows of the package.
Expand All @@ -17,7 +17,7 @@ as you probably noticed in the [tutorials](tutorials).
Not necesseraly all the WorkChains have the `get_builder_from_protocol`. E.g. The {class}`~aiida_vibroscopy.workflows.dielectric.numerical_derivatives`.
:::

## How to use in general
## General usage

Depending on the WorkChain, you will need to specify some minimal, _but required_, inputs. Here is an example:

Expand Down Expand Up @@ -48,7 +48,7 @@ Out[1]:

```

## How to use overrides (beginner)
## Overrides (beginner)

As stated at the beginning, you might need to tweak your builder inputs before submitting. Instead of modifying the inputs via the builder, you can do it via `overrides`. The way the overrides must be secified __is WorkChain dependent__. The structure should be the same as the input specification of the specific WorkChain, so always refer to the inputs of the particular workflow, which you can find [here](topics-workflows).

Expand Down Expand Up @@ -169,7 +169,7 @@ builder = DielectricWorkChain.get_builder_from_protocol(code=code, structure=str

You got the gist, so it will be the same for the `phonon` inputs, which corresponds to the `PhononWorkChain`.

## How to overrides (advanced)
## Overrides (advanced)

Once you got used to using the overrides, you will notice your python script will become quite a mess.
The idea is to write the overrides in a separate file, using for example the convenient `YAML` format,
Expand Down Expand Up @@ -199,7 +199,7 @@ phonon:
scf:
pw:
kpoints_distance: 0.2
pw:
parameters:
...
dielectric:
central_difference:
Expand All @@ -209,7 +209,7 @@ dielectric:
...
```

## Get automated inputs for insulators, metals, and magnetism
## Automated inputs for insulators, metals, and magnetism

Inputs might change depending on the nature of the material: insulating, metallic, a form of magnetism.
This can be the need of specifying a smearing or a magnetic configuration.
Expand Down
26 changes: 10 additions & 16 deletions docs/source/howto/postprocess.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(howto-postprocess)=

# How-to post-process data
# Post-process data

Here you will find some _how-tos_ on how to post-process the `VibrationalData` and `PhonopyData`.
These can be generated via the `HarmonicWorkChain`, the `IRamanSpectraWorkChain` or the `PhononWorkChain`.
Expand All @@ -10,13 +10,12 @@ Please, have a look at the [tutorials](tutorials) for an overview, and at the sp
You can always access to the detailed description of a function/method putting a `?` question mark in front
of the function and press enter. This will print the description of the function, with detailed information
on inputs, outputs and purpose.
``` {note}

A rendered version can also be found in the documentation referring to
the dedicated [API section](reference).
```
:::

## How to get powder spectra
## Powder spectra

You can get the powder infrared and/or Raman spectra from a computed {{ vibrational_data }}. For Raman:

Expand All @@ -28,8 +27,8 @@ vibro = load_node(IDENTIFIER) # a VibrationalData node
polarized_intensities, depolarized_intensities, frequencies, labels = vibro.run_powder_raman_intensities(frequency_laser=532, temperature=300)
```

The total powder intensity will be the some of the polarized (backscattering geometry)
and depolarized (90º geometry) intensities:
The total powder intensity will be the some of the polarized (backscattering geometry, HH/VV setup)
and depolarized (90º geometry, HV setup) intensities:

```python
total = polarized_intensities + depolarized_intensities
Expand All @@ -44,7 +43,7 @@ intensities, frequencies, labels = vibro.run_powder_ir_intensities()
The `labels` output is referring to the irreducible representation labels of the modes.


## How to get single crystal spectra
## Single crystal polarized spectra

::: {important}
It is extremely important that you match the experimental conditions, meaning the direction of
Expand All @@ -53,7 +52,7 @@ on the convention used, both in the code and in the experiments.
:::

::: {note} Convention
In the following methods, the direction of the light must be given in crystal/fractional coordinates.
In the following methods, the direction of the light must be given in **Cartesian coordinates**.
:::

You can get the single crystal infrared and/or Raman spectra from a computed {{ vibrational_data }}. For Raman:
Expand Down Expand Up @@ -86,7 +85,7 @@ incoming = [0,0,1] # light polatization of the incident laser beam
intensities, frequencies, labels = vibro.run_single_crystal_ir_intensities(pol_incoming=incoming)
```

::: {admonition} Cartesian coordinates
<!-- ::: {admonition} Cartesian coordinates
:class: hint
To get the direction in Cartesian coordinates expressed in crystal coordinates of the primitive cell, you can use the following
snippet
Expand All @@ -99,14 +98,9 @@ incoming_cartesian = [0,0,1]
inv_cell = np.linalg.inv(cell)
incoming = np.dot(invcell.T, incoming_cartesian)
```
For the q-direction instead you need to transform them into reciprocal space crystal coordinates, as follows
```python
q_nac_cartesian = [0,0,1]
q_nac_crystal = np.dot(cell, q_nac_cartesian)
```
:::
::: -->

## How to get the IR/Raman active modes
## IR/Raman active modes

To get the active modes from a computed {{ vibrational_data }}:

Expand Down
2 changes: 1 addition & 1 deletion docs/source/howto/understand.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(howto-understand)=

# How-to understand the input/builder structure
# Understand the input/builder structure

In AiiDA the CalcJobs and WorkChains have usually nested inputs and different options on how to run the calculation
and/or workflows. To understand the nested input structure of CalcJobs/Workflows, we made them available in a clickable
Expand Down
2 changes: 1 addition & 1 deletion docs/source/topics/conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ parenthesis we give the corresponding attributes to access quantity via the {{ v
3. Atomic displacements index.

- Non-linear optical susceptibility (`nlo_susceptibility`): in units of pm/V; 3rd rank tensors, with equivalent indices.
- Raman tensors (`raman_tensors`): in units of 4pi/Angstrom; array with 4 indices, respectively:
- Raman tensors (`raman_tensors`): in units of 1/Angstrom; array with 4 indices, respectively:

1. Atomic index.
2. Atomic displacements index.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ def compute_susceptibility_derivatives(
preprocess_data: PreProcessData, electric_field: orm.Float, diagonal_scale: orm.Float, accuracy_order: orm.Int,
**kwargs
) -> dict:
"""Return the Raman (4pi/Angstrom) and the non-linear optical susceptibility (pm/V) tensors.
"""Return the Raman (1/Angstrom) and the non-linear optical susceptibility (pm/V) tensors.
..note:
* If the numerical accuracy order is greater than 2, arrays at lower orders are given as well.
* Units are 4pi/Angstrom for Raman tensors, normalized using the UNITCELL volume.
* Units are 1/Angstrom for Raman tensors, normalized using the UNITCELL volume.
* Units are pm/V for non-linear optical susceptibility
* Raman tensors indecis: (atomic, atomic displacement, electric field, electric field)
Expand Down Expand Up @@ -378,7 +378,7 @@ def compute_susceptibility_derivatives(
chis_data.update({key_order: deepcopy(chis_array_data)})

units_data = orm.Dict({
'raman_tensors': r'$4\pi/\AA$',
'raman_tensors': r'$1/\AA$',
'nlo_susceptibility': 'pm/V',
})

Expand Down
23 changes: 13 additions & 10 deletions src/aiida_vibroscopy/calculations/spectra_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def compute_active_modes(
Raman and infrared active modes can be extracted using `selection_rule`.
:param nac_direction: (3,) shape list, indicating non analytical
direction in fractional unit cell space coordinates
direction in Cartesian coordinates
:param selection_rule: str, can be `raman` or `ir`, it uses symmetry in
the selection of the modes for a specific type of process
:param sr_thr: float, threshold for selection rule (the analytical value is 0)
Expand All @@ -69,9 +69,11 @@ def compute_active_modes(
nac_dir = [0, 0, 0] if nac_direction is None else np.array(nac_direction)

# Notation: columns convention; k = reciprocal; x = direct space; (p) = primitive; (u) = unitcell
# C(p) = C(u) * M --> M = C(u)^-1 * C(p)
# C(p) = C(u) * M ==> M = C(u)^-1 * C(p)
# x(p) = M^-1 x(u) ==> k(p) = M^T k(u);
q_reduced = np.dot(phonopy_instance.primitive_matrix.T, nac_dir) # in reduced (PRIMITIVE) coordinates
# q_reduced = np.dot(phonopy_instance.primitive_matrix.T, nac_dir) # in reduced/crystal (PRIMITIVE) coordinates
q_reduced = np.dot(phonopy_instance.primitive.cell,
nac_dir) / (2. * np.pi) # in reduced/crystal (PRIMITIVE) coordinates

# Step 1 - set the irreducible representations and the phonons
phonopy_instance.set_irreps(q=[0, 0, 0], nac_q_direction=q_reduced, degeneracy_tolerance=degeneracy_tolerance)
Expand Down Expand Up @@ -191,8 +193,8 @@ def compute_raman_susceptibility_tensors(
* Unitcell volume for Raman tensor as normalization (in case non-primitive cell was used).
:param phonopy_instance: Phonopy instance with non-analytical constants included
:param nac_direction: non-analytical direction in unit cell space coordinates
:param raman_tensors: dChi/du in Cartesian coordinates (in 4pi/Angstrom)
:param nac_direction: non-analytical direction in Cartesian coordinates
:param raman_tensors: dChi/du in Cartesian coordinates (in 1/Angstrom)
:param nlo_susceptibility: non linear optical susceptibility
in Cartesian coordinates (in pm/V)
:param use_irreps: whether to use irreducible representations
Expand All @@ -213,8 +215,9 @@ def compute_raman_susceptibility_tensors(
sqrt_volume = np.sqrt(volume)
raman_tensors *= volume

ucell = phonopy_instance.unitcell.cell.T # as rows
q_cartesian = np.dot(ucell, nac_direction) # in Cartesian coordinates
# rcell = np.linalg.inv(phonopy_instance.unitcell.cell) # as columns
# q_cartesian = np.dot(rcell, nac_direction) # in Cartesian coordinates
q_cartesian = nac_direction

selection_rule = 'raman' if use_irreps else None

Expand Down Expand Up @@ -303,7 +306,7 @@ def compute_polarization_vectors(
.. note:: the unite for polarization vectors are in (debey/angstrom)/sqrt(AMU)
:param phonopy_instance: Phonopy instance with non-analytical constants included
:param nac_direction: non-analytical direction in fractional coordinates (unit cell)
:param nac_direction: non-analytical direction in Cartesian coordinates
:param use_irreps: whether to use irreducible representations
in the selection of modes, defaults to True
:param degeneracy_tolerance: degeneracy tolerance
Expand Down Expand Up @@ -355,7 +358,7 @@ def compute_complex_dielectric(
:param freq_range: frequency range in cm^-1; set to `auto` for automatic choice
:param gammas: list or single value of broadenings, i.e. full width at half maximum (FWHM)
:param nac_direction: (3,) shape list, indicating non analytical
direction in fractional unit cell space coordinates
direction in Cartesian coordinates
:param use_irreps: whether to use irreducible representations
in the selection of modes, defaults to True
:param degeneracy_tolerance: degeneracy tolerance
Expand Down Expand Up @@ -387,7 +390,7 @@ def compute_complex_dielectric(
else:
sigmas = [float(gammas) for _ in frequencies]

if freq_range == 'auto':
if isinstance(freq_range, str):
xi = max(0, frequencies.min() - 200)
xf = frequencies.max() + 200
freq_range = np.arange(xi, xf, 1.)
Expand Down
Loading

0 comments on commit cef301a

Please sign in to comment.