xrdkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xrdkit-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amir Khesro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
xrdkit-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: xrdkit
3
+ Version: 0.1.0
4
+ Summary: Reusable X-ray diffraction analysis toolkit for electroceramics research
5
+ Keywords: x-ray diffraction,XRD,powder diffraction,Rietveld,Le Bail,GSAS-II,materials science,ceramics,crystallography
6
+ Author: Amir Khesro
7
+ Author-email: Amir Khesro <am.khesro@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
18
+ Classifier: Typing :: Typed
19
+ Requires-Dist: matplotlib>=3.11.1
20
+ Requires-Dist: numpy>=2.0.0
21
+ Requires-Dist: scipy>=1.13.0
22
+ Requires-Dist: pymatgen>=2026.5.4 ; extra == 'phases'
23
+ Requires-Python: >=3.11
24
+ Project-URL: Homepage, https://github.com/amirkhesro/xrdkit
25
+ Project-URL: Repository, https://github.com/amirkhesro/xrdkit
26
+ Project-URL: Issues, https://github.com/amirkhesro/xrdkit/issues
27
+ Project-URL: Changelog, https://github.com/amirkhesro/xrdkit/blob/main/CHANGELOG.md
28
+ Provides-Extra: phases
29
+ Description-Content-Type: text/markdown
30
+
31
+ # xrdkit
32
+
33
+ A reusable X-ray diffraction analysis toolkit for electroceramics research.
34
+
35
+ `xrdkit` provides shared, importable routines for loading, processing and
36
+ analysing XRD patterns, so that the same analysis code can be reused across
37
+ projects instead of being copied between one-off scripts.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install xrdkit
43
+ ```
44
+
45
+ Phase matching against the COD needs pymatgen, which comes with the `phases`
46
+ extra:
47
+
48
+ ```bash
49
+ pip install "xrdkit[phases]"
50
+ ```
51
+
52
+ GSAS-II is optional and is needed only for the Rietveld driver in
53
+ `xrdkit.gsas2`. It is not a Python dependency: install it separately and
54
+ point `XRDKIT_GSAS2_PYTHON` and `XRDKIT_GSAS2_HOME` at it, or leave it at
55
+ `~/gsas2main`. Every other module works without it.
56
+
57
+ ## Data
58
+
59
+ This repository contains **code only**. Raw and processed diffraction data
60
+ (including `.xrdml` files) live in a separate data repository and are never
61
+ committed here.
62
+
63
+ ## GSAS-II refinement
64
+
65
+ GSAS-II runs under its own Python, so `xrdkit.gsas2` writes a job as JSON and
66
+ runs `gsas2_driver.py` on it there (`run_job`), with `-B`; xrdkit writes
67
+ nothing into the GSAS-II installation. `find_gsas2` locates it
68
+ (`XRDKIT_GSAS2_PYTHON`, `XRDKIT_GSAS2_HOME`, or `~/gsas2main`).
69
+
70
+ GSAS-II's own version and update check, run whenever its scripting module is
71
+ imported, writes one bytecode file inside its installation
72
+ (`Lib/site-packages/scipy/__pycache__/__config__.cpython-313.pyc` in the
73
+ `~/gsas2main` install); it is written once, not on every run, and does not
74
+ come from xrdkit.
75
+
76
+ `build_refine_job` assembles a refinement from a list of stages, each adding
77
+ flags to the ones before (see the `gsas2_driver` module notes for every key):
78
+
79
+ | Stage key | Refines |
80
+ | --- | --- |
81
+ | `background`, `scale`, `zero`, `displacement`, `instrument` | histogram and instrument |
82
+ | `cell`, `le_bail`, `phase_fractions`, `size`, `mustrain` | phase, by name or all |
83
+ | `overall_uiso` | one Uiso for every atom of a phase |
84
+ | `uiso_groups` | `{phase: [[site, ...], ...]}`: one Uiso for every atom on each group's sites |
85
+ | `atoms`, `atom_flags` | F, X, U for every atom, or for named atoms |
86
+ | `coordinates` | `{phase: {site: "xz" or "all"}}`: the coordinates to free on each site; the rest are held, atoms sharing a site move together |
87
+ | `origin` | `{phase: {"site": label, "axis": "z"}}`: a site's coordinate held to fix the origin; a stage that would leave the origin floating along a polar axis fails |
88
+ | `occupancies` | `[{"phase", "sites": [...], "elements": [...]}]`: the named elements' occupancies on the named sites, each element's content over them held (so the vacancies over them too) |
89
+
90
+ Job options besides the stages: `limits`, `cycles`, `broadening`,
91
+ `background_start`, `scale_start`, `overall_uiso_start`, `le_bail_cycles`,
92
+ `max_passes` and `pass_tolerance` (refine each stage until it settles),
93
+ `on_flagged` and `on_unsettled` (below), `sanity` (`{"max_shift": 0.05,
94
+ "reference": {phase: [{"label", "xyz"}]}}`) and `bonds` (`True` or
95
+ `{"anions", "dmax", "dmin"}`). A phase's `atoms` edits the structure read
96
+ from its CIF in the project only: occupancies, positions and Uiso, and atoms
97
+ added on existing sites; `structure_edits` gives the edits that set up a
98
+ refined structure again, so that one refinement starts where another ended.
99
+
100
+ Every stage of the result records its residuals and parameters with esds, the
101
+ atoms as it left them, the coordinates it refined and held by site, its
102
+ occupancy constraints, and a sanity check: negative Uiso, occupancies outside
103
+ 0 to 1 (and site totals above 1), and sites moved more than `max_shift`
104
+ (fractional) from the reference. With `bonds`, `run_job` adds the final
105
+ model's cation to anion distances (`xrdkit.structure.bond_lengths`, to oxygen
106
+ up to 3 Å by default) to the result.
107
+
108
+ ### What becomes of a stage that goes wrong
109
+
110
+ So that a sequence can be run unattended and still leave a usable record,
111
+ every stage carries a `status` and the run goes on from the last stage kept:
112
+
113
+ | Status | The stage | The run |
114
+ | --- | --- | --- |
115
+ | `clean` | settled, and raised no sanity flag the stage before it lacked | kept |
116
+ | `unsettled` | reached `max_passes` still moving, and raised no new flag | kept |
117
+ | `flagged` | raised a new sanity flag, with `on_flagged` `"accept"` | kept |
118
+ | `rejected` | raised a new flag under `on_flagged` `"reject"` (the default), or did not settle under `on_unsettled` `"reject"` | rolled back |
119
+ | `failed` | raised | rolled back, and the run ends |
120
+
121
+ **Rejected.** A sanity flag means the model has gone somewhere it should not
122
+ be — a Uiso below zero, an occupancy outside 0 to 1, a site further from the
123
+ reference than `max_shift` — so the stage that raised one is rejected by
124
+ default. It is recorded in full, residuals, parameters and atoms, with
125
+ `rejected_because` and its name in the result's `rejected`. The project then
126
+ goes back to the state the last stage kept left it in — parameters, atoms
127
+ and constraints — and is saved so, and the stage's own entries are dropped
128
+ from the stages, so that what it alone refined is held in every stage after
129
+ it. `on_flagged: "accept"` keeps it instead, as `"flagged"`.
130
+
131
+ **Unsettled.** Reaching the pass cap only means the stage was still moving
132
+ when the passes ran out, which is not in itself wrong, so it is kept by
133
+ default and `largest_remaining_move` records the parameter that moved most
134
+ in its last pass and by how many esds. `on_unsettled: "reject"` rolls one
135
+ back like a flagged stage, for a caller that wants nothing unsettled in its
136
+ model.
137
+
138
+ **Undetermined.** Each stage also records, as `undetermined`, the refined
139
+ atom parameters the data do not determine
140
+ (`gsas2_driver.find_undetermined`): an occupancy whose esd is more than half
141
+ its allowed range, 0 to 1, and a coordinate or an isotropic Uiso whose esd
142
+ is larger than its shift from where the job found it. Such a value is not a
143
+ result, whatever its stage's status. The result's `undetermined` are those
144
+ of the last stage kept.
145
+
146
+ **The model a run leaves.** Every run ends with `final`, and with
147
+ `export_prefix` an `exports`, whatever became of its stages: the model of
148
+ the last stage kept, or, where no stage was kept, the one the job started
149
+ from, computed by a refinement of no cycles that moves nothing. The result's
150
+ `final_from` names the stage they come from, or `"job start"`; a starting
151
+ model has no refined parameter and so no esd.
152
+
153
+ ### Writing a sequence up
154
+
155
+ For a pipeline that runs several refinements in turn, `xrdkit.gsas2` writes
156
+ what they came to:
157
+
158
+ | Function | Gives |
159
+ | --- | --- |
160
+ | `stage_status` | one stage's status, worked out for a result written before the driver recorded one |
161
+ | `accepted_stages` | the stages of a result that were refined and kept |
162
+ | `stage_statuses` | every stage as `{name, status, reason, passes, rwp, gof, undetermined}`, `reason` saying why it is not clean |
163
+ | `stage_status_table` | those as markdown table lines |
164
+ | `log_tail` | the last lines of a GSAS-II log |
165
+ | `failure_markdown` | a write up of a refinement that could not be finished: the error, the stages it got through and the log tail |
166
+ | `summary_markdown` | a summary of a sequence: a row per refinement with its final Rwp, GOF and key values, the status of each stage, then every stage not clean with why and the parameters left undetermined |
167
+
168
+ ## Refinement settings
169
+
170
+ `xrdkit.config` reads the settings of a refinement pipeline from a TOML file
171
+ with the standard library's `tomllib` (`load_config`): one table per sample
172
+ under `samples` and one per reference structure under `structures`. It
173
+ checks them as it reads, and a key missing or unknown, a value of the wrong
174
+ kind, or a sample composition that does not fit its structure's sites (an
175
+ element on no site and not added by the rule, or more atoms per cell on a
176
+ kind of site than it has positions) raises `ConfigError` naming the file and
177
+ the table. `sample_settings` finds a sample by its id or table name, with its
178
+ structure.
179
+
180
+ | Table | Keys |
181
+ | --- | --- |
182
+ | `samples.<name>` | `id`, `scan`, `composition` (atoms per formula unit), `structure`, `start_cell` (`{file, model}` or `{a, c}`), `two_theta`, `background` (`{function, terms}`), `refine_microstrain`, `notes`; optionally `followed_reflections`, `trials` (`{runs = [{low, terms}], followed}`), `write_up` (text by mode and section), `unsettled` (the rule for each mode, over the top level one) |
183
+ | `unsettled` | at the top level, the default rule for a stage of each mode that has not settled, `"accept"` (the pipeline's default) or `"reject"`, by the caller's own mode names; each sample carries it merged with its own as its `unsettled` |
184
+ | `structures.<name>` | `cif`, `label`, `phase_name`, `space_group`, `formula_units`, `sites` (`{atoms = {label = element}, wyckoff, kind}`, kind A, B or O), `uiso_groups` (`{name, sites}`), `origin` (`{site, axis}`), `exchange` (`{elements, sites}`), `composition` (`{added = {element = host}}`); optionally `free_coordinates` (by Wyckoff position), `bond_limits` (`{kind = {min, max}}`) |
185
+
186
+ From a structure table, `xrdkit.structure.site_setup` finds the sites among a
187
+ phase's atoms as the driver reports them, checking that each site's atoms
188
+ share one position, are of the elements given and have the multiplicity of
189
+ its Wyckoff position, and that no atom is left over. It returns the sites by
190
+ kind, the Uiso groups, the coordinates to refine on each site, the origin site
191
+ and the exchange, in the forms the stage keys above take.
192
+ `composition_edits` gives the atom edits that put a nominal composition on
193
+ the sites by the table's rule: each element the CIF holds is scaled by one
194
+ factor over its sites, which keeps its distribution, and each added element
195
+ goes on its host's sites in proportion to the host's occupancy.
196
+ `cell_contents` gives the atoms of each element per cell.
197
+
198
+ For example, the sample pipeline of the XRD analysis repository reads every
199
+ sample and structure setting from its `config/samples.toml` and runs Le Bail,
200
+ fixed atoms, coordinates and occupancies in turn, each from the saved result
201
+ of the one before:
202
+
203
+ ```
204
+ python -B scripts/refine_sample.py 10 # Le Bail only
205
+ python -B scripts/refine_sample.py 10 --trials # Le Bail with the configured trial ranges and background terms
206
+ python -B scripts/refine_sample.py 10 --through occupancies # all four modes
207
+ python -B scripts/refine_sample.py 10 --from coordinates # one mode, from the saved fixed atoms result
208
+ python -B scripts/refine_sample.py 10 --from coordinates --through occupancies
209
+ ```
210
+
211
+ It passes each mode's `unsettled` rule and pass cap to its job, writes the
212
+ mode's write up with `failure_markdown` and stops with a non-zero status
213
+ where a mode cannot be finished, and writes `summary.md` beside the write
214
+ ups with `summary_markdown` at the end of every run.
215
+
216
+ ## Status
217
+
218
+ Under active development. The API is not yet stable and may change without
219
+ notice.
220
+
221
+ ## Citing
222
+
223
+ If xrdkit contributes to work you publish, please cite it. The metadata is in
224
+ [CITATION.cff](CITATION.cff), which GitHub renders as a ready-made citation
225
+ under **Cite this repository**.
226
+
227
+ ## License
228
+
229
+ MIT — see [LICENSE](LICENSE).
xrdkit-0.1.0/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # xrdkit
2
+
3
+ A reusable X-ray diffraction analysis toolkit for electroceramics research.
4
+
5
+ `xrdkit` provides shared, importable routines for loading, processing and
6
+ analysing XRD patterns, so that the same analysis code can be reused across
7
+ projects instead of being copied between one-off scripts.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install xrdkit
13
+ ```
14
+
15
+ Phase matching against the COD needs pymatgen, which comes with the `phases`
16
+ extra:
17
+
18
+ ```bash
19
+ pip install "xrdkit[phases]"
20
+ ```
21
+
22
+ GSAS-II is optional and is needed only for the Rietveld driver in
23
+ `xrdkit.gsas2`. It is not a Python dependency: install it separately and
24
+ point `XRDKIT_GSAS2_PYTHON` and `XRDKIT_GSAS2_HOME` at it, or leave it at
25
+ `~/gsas2main`. Every other module works without it.
26
+
27
+ ## Data
28
+
29
+ This repository contains **code only**. Raw and processed diffraction data
30
+ (including `.xrdml` files) live in a separate data repository and are never
31
+ committed here.
32
+
33
+ ## GSAS-II refinement
34
+
35
+ GSAS-II runs under its own Python, so `xrdkit.gsas2` writes a job as JSON and
36
+ runs `gsas2_driver.py` on it there (`run_job`), with `-B`; xrdkit writes
37
+ nothing into the GSAS-II installation. `find_gsas2` locates it
38
+ (`XRDKIT_GSAS2_PYTHON`, `XRDKIT_GSAS2_HOME`, or `~/gsas2main`).
39
+
40
+ GSAS-II's own version and update check, run whenever its scripting module is
41
+ imported, writes one bytecode file inside its installation
42
+ (`Lib/site-packages/scipy/__pycache__/__config__.cpython-313.pyc` in the
43
+ `~/gsas2main` install); it is written once, not on every run, and does not
44
+ come from xrdkit.
45
+
46
+ `build_refine_job` assembles a refinement from a list of stages, each adding
47
+ flags to the ones before (see the `gsas2_driver` module notes for every key):
48
+
49
+ | Stage key | Refines |
50
+ | --- | --- |
51
+ | `background`, `scale`, `zero`, `displacement`, `instrument` | histogram and instrument |
52
+ | `cell`, `le_bail`, `phase_fractions`, `size`, `mustrain` | phase, by name or all |
53
+ | `overall_uiso` | one Uiso for every atom of a phase |
54
+ | `uiso_groups` | `{phase: [[site, ...], ...]}`: one Uiso for every atom on each group's sites |
55
+ | `atoms`, `atom_flags` | F, X, U for every atom, or for named atoms |
56
+ | `coordinates` | `{phase: {site: "xz" or "all"}}`: the coordinates to free on each site; the rest are held, atoms sharing a site move together |
57
+ | `origin` | `{phase: {"site": label, "axis": "z"}}`: a site's coordinate held to fix the origin; a stage that would leave the origin floating along a polar axis fails |
58
+ | `occupancies` | `[{"phase", "sites": [...], "elements": [...]}]`: the named elements' occupancies on the named sites, each element's content over them held (so the vacancies over them too) |
59
+
60
+ Job options besides the stages: `limits`, `cycles`, `broadening`,
61
+ `background_start`, `scale_start`, `overall_uiso_start`, `le_bail_cycles`,
62
+ `max_passes` and `pass_tolerance` (refine each stage until it settles),
63
+ `on_flagged` and `on_unsettled` (below), `sanity` (`{"max_shift": 0.05,
64
+ "reference": {phase: [{"label", "xyz"}]}}`) and `bonds` (`True` or
65
+ `{"anions", "dmax", "dmin"}`). A phase's `atoms` edits the structure read
66
+ from its CIF in the project only: occupancies, positions and Uiso, and atoms
67
+ added on existing sites; `structure_edits` gives the edits that set up a
68
+ refined structure again, so that one refinement starts where another ended.
69
+
70
+ Every stage of the result records its residuals and parameters with esds, the
71
+ atoms as it left them, the coordinates it refined and held by site, its
72
+ occupancy constraints, and a sanity check: negative Uiso, occupancies outside
73
+ 0 to 1 (and site totals above 1), and sites moved more than `max_shift`
74
+ (fractional) from the reference. With `bonds`, `run_job` adds the final
75
+ model's cation to anion distances (`xrdkit.structure.bond_lengths`, to oxygen
76
+ up to 3 Å by default) to the result.
77
+
78
+ ### What becomes of a stage that goes wrong
79
+
80
+ So that a sequence can be run unattended and still leave a usable record,
81
+ every stage carries a `status` and the run goes on from the last stage kept:
82
+
83
+ | Status | The stage | The run |
84
+ | --- | --- | --- |
85
+ | `clean` | settled, and raised no sanity flag the stage before it lacked | kept |
86
+ | `unsettled` | reached `max_passes` still moving, and raised no new flag | kept |
87
+ | `flagged` | raised a new sanity flag, with `on_flagged` `"accept"` | kept |
88
+ | `rejected` | raised a new flag under `on_flagged` `"reject"` (the default), or did not settle under `on_unsettled` `"reject"` | rolled back |
89
+ | `failed` | raised | rolled back, and the run ends |
90
+
91
+ **Rejected.** A sanity flag means the model has gone somewhere it should not
92
+ be — a Uiso below zero, an occupancy outside 0 to 1, a site further from the
93
+ reference than `max_shift` — so the stage that raised one is rejected by
94
+ default. It is recorded in full, residuals, parameters and atoms, with
95
+ `rejected_because` and its name in the result's `rejected`. The project then
96
+ goes back to the state the last stage kept left it in — parameters, atoms
97
+ and constraints — and is saved so, and the stage's own entries are dropped
98
+ from the stages, so that what it alone refined is held in every stage after
99
+ it. `on_flagged: "accept"` keeps it instead, as `"flagged"`.
100
+
101
+ **Unsettled.** Reaching the pass cap only means the stage was still moving
102
+ when the passes ran out, which is not in itself wrong, so it is kept by
103
+ default and `largest_remaining_move` records the parameter that moved most
104
+ in its last pass and by how many esds. `on_unsettled: "reject"` rolls one
105
+ back like a flagged stage, for a caller that wants nothing unsettled in its
106
+ model.
107
+
108
+ **Undetermined.** Each stage also records, as `undetermined`, the refined
109
+ atom parameters the data do not determine
110
+ (`gsas2_driver.find_undetermined`): an occupancy whose esd is more than half
111
+ its allowed range, 0 to 1, and a coordinate or an isotropic Uiso whose esd
112
+ is larger than its shift from where the job found it. Such a value is not a
113
+ result, whatever its stage's status. The result's `undetermined` are those
114
+ of the last stage kept.
115
+
116
+ **The model a run leaves.** Every run ends with `final`, and with
117
+ `export_prefix` an `exports`, whatever became of its stages: the model of
118
+ the last stage kept, or, where no stage was kept, the one the job started
119
+ from, computed by a refinement of no cycles that moves nothing. The result's
120
+ `final_from` names the stage they come from, or `"job start"`; a starting
121
+ model has no refined parameter and so no esd.
122
+
123
+ ### Writing a sequence up
124
+
125
+ For a pipeline that runs several refinements in turn, `xrdkit.gsas2` writes
126
+ what they came to:
127
+
128
+ | Function | Gives |
129
+ | --- | --- |
130
+ | `stage_status` | one stage's status, worked out for a result written before the driver recorded one |
131
+ | `accepted_stages` | the stages of a result that were refined and kept |
132
+ | `stage_statuses` | every stage as `{name, status, reason, passes, rwp, gof, undetermined}`, `reason` saying why it is not clean |
133
+ | `stage_status_table` | those as markdown table lines |
134
+ | `log_tail` | the last lines of a GSAS-II log |
135
+ | `failure_markdown` | a write up of a refinement that could not be finished: the error, the stages it got through and the log tail |
136
+ | `summary_markdown` | a summary of a sequence: a row per refinement with its final Rwp, GOF and key values, the status of each stage, then every stage not clean with why and the parameters left undetermined |
137
+
138
+ ## Refinement settings
139
+
140
+ `xrdkit.config` reads the settings of a refinement pipeline from a TOML file
141
+ with the standard library's `tomllib` (`load_config`): one table per sample
142
+ under `samples` and one per reference structure under `structures`. It
143
+ checks them as it reads, and a key missing or unknown, a value of the wrong
144
+ kind, or a sample composition that does not fit its structure's sites (an
145
+ element on no site and not added by the rule, or more atoms per cell on a
146
+ kind of site than it has positions) raises `ConfigError` naming the file and
147
+ the table. `sample_settings` finds a sample by its id or table name, with its
148
+ structure.
149
+
150
+ | Table | Keys |
151
+ | --- | --- |
152
+ | `samples.<name>` | `id`, `scan`, `composition` (atoms per formula unit), `structure`, `start_cell` (`{file, model}` or `{a, c}`), `two_theta`, `background` (`{function, terms}`), `refine_microstrain`, `notes`; optionally `followed_reflections`, `trials` (`{runs = [{low, terms}], followed}`), `write_up` (text by mode and section), `unsettled` (the rule for each mode, over the top level one) |
153
+ | `unsettled` | at the top level, the default rule for a stage of each mode that has not settled, `"accept"` (the pipeline's default) or `"reject"`, by the caller's own mode names; each sample carries it merged with its own as its `unsettled` |
154
+ | `structures.<name>` | `cif`, `label`, `phase_name`, `space_group`, `formula_units`, `sites` (`{atoms = {label = element}, wyckoff, kind}`, kind A, B or O), `uiso_groups` (`{name, sites}`), `origin` (`{site, axis}`), `exchange` (`{elements, sites}`), `composition` (`{added = {element = host}}`); optionally `free_coordinates` (by Wyckoff position), `bond_limits` (`{kind = {min, max}}`) |
155
+
156
+ From a structure table, `xrdkit.structure.site_setup` finds the sites among a
157
+ phase's atoms as the driver reports them, checking that each site's atoms
158
+ share one position, are of the elements given and have the multiplicity of
159
+ its Wyckoff position, and that no atom is left over. It returns the sites by
160
+ kind, the Uiso groups, the coordinates to refine on each site, the origin site
161
+ and the exchange, in the forms the stage keys above take.
162
+ `composition_edits` gives the atom edits that put a nominal composition on
163
+ the sites by the table's rule: each element the CIF holds is scaled by one
164
+ factor over its sites, which keeps its distribution, and each added element
165
+ goes on its host's sites in proportion to the host's occupancy.
166
+ `cell_contents` gives the atoms of each element per cell.
167
+
168
+ For example, the sample pipeline of the XRD analysis repository reads every
169
+ sample and structure setting from its `config/samples.toml` and runs Le Bail,
170
+ fixed atoms, coordinates and occupancies in turn, each from the saved result
171
+ of the one before:
172
+
173
+ ```
174
+ python -B scripts/refine_sample.py 10 # Le Bail only
175
+ python -B scripts/refine_sample.py 10 --trials # Le Bail with the configured trial ranges and background terms
176
+ python -B scripts/refine_sample.py 10 --through occupancies # all four modes
177
+ python -B scripts/refine_sample.py 10 --from coordinates # one mode, from the saved fixed atoms result
178
+ python -B scripts/refine_sample.py 10 --from coordinates --through occupancies
179
+ ```
180
+
181
+ It passes each mode's `unsettled` rule and pass cap to its job, writes the
182
+ mode's write up with `failure_markdown` and stops with a non-zero status
183
+ where a mode cannot be finished, and writes `summary.md` beside the write
184
+ ups with `summary_markdown` at the end of every run.
185
+
186
+ ## Status
187
+
188
+ Under active development. The API is not yet stable and may change without
189
+ notice.
190
+
191
+ ## Citing
192
+
193
+ If xrdkit contributes to work you publish, please cite it. The metadata is in
194
+ [CITATION.cff](CITATION.cff), which GitHub renders as a ready-made citation
195
+ under **Cite this repository**.
196
+
197
+ ## License
198
+
199
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "xrdkit"
3
+ version = "0.1.0"
4
+ description = "Reusable X-ray diffraction analysis toolkit for electroceramics research"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ keywords = [
10
+ "x-ray diffraction",
11
+ "XRD",
12
+ "powder diffraction",
13
+ "Rietveld",
14
+ "Le Bail",
15
+ "GSAS-II",
16
+ "materials science",
17
+ "ceramics",
18
+ "crystallography",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 3 - Alpha",
22
+ "Intended Audience :: Science/Research",
23
+ "Operating System :: OS Independent",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Scientific/Engineering :: Physics",
28
+ "Topic :: Scientific/Engineering :: Chemistry",
29
+ "Typing :: Typed",
30
+ ]
31
+ dependencies = [
32
+ "matplotlib>=3.11.1",
33
+ "numpy>=2.0.0",
34
+ "scipy>=1.13.0",
35
+ ]
36
+
37
+ [[project.authors]]
38
+ name = "Amir Khesro"
39
+ email = "am.khesro@gmail.com"
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/amirkhesro/xrdkit"
43
+ Repository = "https://github.com/amirkhesro/xrdkit"
44
+ Issues = "https://github.com/amirkhesro/xrdkit/issues"
45
+ Changelog = "https://github.com/amirkhesro/xrdkit/blob/main/CHANGELOG.md"
46
+
47
+ [project.optional-dependencies]
48
+ phases = ["pymatgen>=2026.5.4"]
49
+
50
+ [build-system]
51
+ requires = ["uv_build>=0.12.0,<0.13.0"]
52
+ build-backend = "uv_build"
53
+
54
+ [dependency-groups]
55
+ dev = ["pytest>=9.1.1"]
@@ -0,0 +1,58 @@
1
+ [project]
2
+ name = "xrdkit"
3
+ version = "0.1.0"
4
+ description = "Reusable X-ray diffraction analysis toolkit for electroceramics research"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [
10
+ { name = "Amir Khesro", email = "am.khesro@gmail.com" }
11
+ ]
12
+ keywords = [
13
+ "x-ray diffraction",
14
+ "XRD",
15
+ "powder diffraction",
16
+ "Rietveld",
17
+ "Le Bail",
18
+ "GSAS-II",
19
+ "materials science",
20
+ "ceramics",
21
+ "crystallography",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 3 - Alpha",
25
+ "Intended Audience :: Science/Research",
26
+ "Operating System :: OS Independent",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Scientific/Engineering :: Physics",
31
+ "Topic :: Scientific/Engineering :: Chemistry",
32
+ "Typing :: Typed",
33
+ ]
34
+ dependencies = [
35
+ "matplotlib>=3.11.1",
36
+ "numpy>=2.0.0",
37
+ "scipy>=1.13.0",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/amirkhesro/xrdkit"
42
+ Repository = "https://github.com/amirkhesro/xrdkit"
43
+ Issues = "https://github.com/amirkhesro/xrdkit/issues"
44
+ Changelog = "https://github.com/amirkhesro/xrdkit/blob/main/CHANGELOG.md"
45
+
46
+ [project.optional-dependencies]
47
+ phases = [
48
+ "pymatgen>=2026.5.4",
49
+ ]
50
+
51
+ [build-system]
52
+ requires = ["uv_build>=0.12.0,<0.13.0"]
53
+ build-backend = "uv_build"
54
+
55
+ [dependency-groups]
56
+ dev = [
57
+ "pytest>=9.1.1",
58
+ ]