| Name | cohesivm JSON |
| Version |
1.0.0
JSON |
| download |
| home_page | None |
| Summary | Combinatorial h+/e- Sample Investigation using Voltaic Measurements |
| upload_time | 2024-08-12 13:57:53 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.9 |
| license | None |
| keywords |
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
# COHESIVM: Combinatorial h+/e- Sample Investigation using Voltaic Measurements
## Introduction
The COHESIVM Python package provides a versatile framework for conducting combinatorial voltaic measurements in
scientific research and development. In order to enable a broad range of electrical and electrochemical analysis
methods, COHESIVM uses a generalized design of the main components which facilitates the extension and adaptation to
different measurement devices and routines. These components are cohesively put together in an
[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) class which runs compatibility checks and executes the actual measurements.
### Key Features:
- **Modular Design:** COHESIVM adopts a module-oriented approach where components such as measurement devices
([``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device)), contacting interfaces ([``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface)), and measurement
routines ([``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement)) are abstracted into interchangeable units. This modular
architecture enhances flexibility in experimental setups and makes it easy to add new component implementations.
- **Combinatorial Flexibility:** By abstracting the class for the contacting interface, COHESIVM enables diverse
configurations for sample investigation. Researchers can simply implement their combinatorial sample design or even
interface a robotic contacting system.
- **Data Handling:** Collected data is stored in a structured [HDF5](https://www.hdfgroup.org/solutions/hdf5/) database
format using the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) class, ensuring efficient data management and accessibility.
[``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) is collected based on the [DCMI standard](http://purl.org/dc/terms/) which is
extended by COHESIVM-specific metadata terms.
- **Analysis and GUIs:** Alongside the measurement routines, analysis functions and plots can be implemented, extending
the [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) base class. Together with the graphical user interface (also available for
conducting experiments and reviewing the database contents), initial screening of the data is facilitated.
## Table of Contents
- [Getting Started](#getting-started)
- [Graphical User Interfaces](#graphical-user-interfaces)
- [Examples](#examples)
- [Package Reference](#package-reference)
- [Contributing](#contributing)
- [License](#license)
## Getting Started <a name="getting-started"></a>
### Installation
#### Using pip
To install the core COHESIVM package from the Python Package Index (PyPI), simply run:
```console
pip install cohesivm
```
This command will download and install the latest stable version of COHESIVM and its core dependencies.
If you want to use the Graphical User Interfaces inside your [Jupyter](https://jupyter.org/) environment, make sure to
specify the ``gui`` extra:
```console
pip install cohesivm[gui]
```
Further extras exist for the implemented devices and for developers (refer to the ``pyproject.toml`` in the source
files to get a listing of all available optional dependencies).
#### Cloning from GitHub
If you want to install the development version of the package from the GitHub repository, follow these steps:
1. **Clone** the repository to your local machine:
```console
git clone https://github.com/mxwalbert/cohesivm.git
```
2. **Navigate** into the cloned directory:
```console
cd cohesivm
```
3. **Install** the package and its dependencies:
```console
pip install .
```
### Configuration
A ``config.ini`` file should be placed in the root of your project to configure the hardware ports/addresses of the
contact interfaces and measurement devices. Some DCMI metadata terms also need to be defined there. COHESIVM implements
a config parser which allows to access these values, e.g.:
```pycon
>>> import cohesivm
>>> cohesivm.config.get_option('DCMI', 'creator')
Dow, John
```
A working file with the implemented interfaces and devices can be copied from the GitHub repository, or you can create
your own from this template:
```ini
# This file is used to configure the project as well as the devices and interfaces (e.g., COM ports, addresses, ...).
# METADATA ------------------------------------------------------------------------------------------------------------
[DCMI]
# The following options correspond to the terms defined by the Dublin Core Metadata Initiative.
# See https://purl.org/dc/terms/ for detailed descriptions.
publisher = "Your Company Ltd."
creator = "Dow, John"
rights = <https://link.to/licence>
subject = "modular design"; "combinatorial flexibility"; "data handling"; "analysis and gui"
# ---------------------------------------------------------------------------------------------------------------------
# INTERFACES ----------------------------------------------------------------------------------------------------------
[NAME_OF_USB_INTERFACE]
com_port = 42
# ---------------------------------------------------------------------------------------------------------------------
# DEVICES -------------------------------------------------------------------------------------------------------------
[NAME_OF_NETWORK_DEVICE]
address = localhost
port = 8888
timeout = 0.1
# ---------------------------------------------------------------------------------------------------------------------
```
### Basic Usage
With working implementations of the main components ([``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device),
[``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface), [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement)), setting up and running an
experiment only takes a few lines of code:
```python
from cohesivm import config
from cohesivm.database import Database, Dimensions
from cohesivm.devices.agilent import Agilent4156C
from cohesivm.measurements.iv import CurrentVoltageCharacteristic
from cohesivm.interfaces import MA8X8
from cohesivm.experiment import Experiment
from cohesivm.progressbar import ProgressBar
# Create a new or load an existing database
db = Database('Test.h5')
# Configure the components
smu = Agilent4156C.SweepVoltageSMUChannel()
device = Agilent4156C.Agilent4156C(channels=[smu], **config.get_section('Agilent4156C'))
interface = MA8X8(com_port=config.get_option('MA8X8', 'com_port'), pixel_dimensions=Dimensions.Circle(radius=0.425))
measurement = CurrentVoltageCharacteristic(start_voltage=-2.0, end_voltage=2.0, voltage_step=0.01, illuminated=True)
# Combine the components in an experiment
experiment = Experiment(
database=db,
device=device,
interface=interface,
measurement=measurement,
sample_id='test_sample_42',
selected_contacts=None
)
# Optionally set up a progressbar
pbar = ProgressBar(experiment)
# Run the experiment
with pbar.show():
experiment.quickstart()
```
If you want to change the measurement device to a different one, you only need to adjust the lines for the
[``Channel``](https://cohesivm.readthedocs.io/en/latest/reference/channels.html#cohesivm.channels.Channel) and the [``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device) accordingly:
```python
from cohesivm.devices.ossila import OssilaX200
smu = OssilaX200.VoltageSMUChannel()
device = OssilaX200.OssilaX200(channels=[smu], **config.get_section('OssilaX200'))
```
## Graphical User Interfaces <a name="graphical-user-interfaces"></a>
If you work with [Jupyter](https://jupyter.org/), you may use the Graphical User Interfaces (GUIs) which are implemented
in the form of [Jupyter Widgets](https://ipywidgets.readthedocs.io/en/stable/). Currently, three GUIs are available:
### Experiment GUI

On the left panel "Control", you see the current [``ExperimentState``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.ExperimentState), followed by a
representation of the [``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface) and the control buttons at the bottom. The circles are
annotated with the [``contact_ids``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface.contact_ids) and the colors correspond to their current state.
On the right panel "Plot", the currently running [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) is displayed. The plot is
automatically updated as soon as new measurement data arrives in the
[``data_stream``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.data_stream) of the [``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) object.
### Database GUI

This GUI enables to display and filter the measurement data which is stored in an HDF5 file. At the top, you select to
display the data grouped in terms of the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) or by the
[``sample_name``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_name) of the [``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) object. If you
choose the former one, you may additionally filter the data by means of measurement parameters. The button to the very
right of each data row enables you to copy the dataset path, to access it in the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database).
### Analysis GUI

Similar to the Experiment GUI, the "Interface" panel represents the contacts with their respective IDs. They can be
clicked to display the measured data in the "Plot" panel to the right. There, the arrows can be used to switch between
[``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions) that are defined in the [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) class. The
results of the [``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions), which are also implemented there, are shown in the table
below.
Detailed guides to work with the GUIs can be found in the [documentation](https://cohesivm.readthedocs.io/en/latest/).
## Examples <a name="examples"></a>
### Run an Experiment
Follow the [Basic Usage](https://cohesivm.readthedocs.io/en/latest//getting_started/basic_usage.html) example to set up and run an
[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment). If you do not have all components ready yet, follow these tutorials:
- [Implement a Device](https://cohesivm.readthedocs.io/en/latest//tutorials/device.html)
- [Implement an Interface](https://cohesivm.readthedocs.io/en/latest//tutorials/interface.html)
- [Implement a Measurement](https://cohesivm.readthedocs.io/en/latest//tutorials/measurement.html)
To follow the other examples you may just run the code from the [Basic Usage](https://cohesivm.readthedocs.io/en/latest//getting_started/basic_usage.html)
example even if you do not have access to the hardware. This will fail but create an HDF5 file and store an empty
dataset entry.
### Manage the Data
After you collected some data and stored it in an HDF5 file, you can use the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) class
to work with it. First, initialise the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) object and list the samples which are stored
there:
```pycon
>>> from cohesivm.database import Database
>>> db = Database('Test.h5')
>>> db.get_sample_ids()
['test_sample_42']
```
This is exactly the [``sample_id``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_id) which was specified when the
[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) was configured, and it can be used to retrieve the actual
[``dataset``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.dataset) path in the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) object:
```pycon
>>> db.filter_by_sample_id('test_sample_42')
['/CurrentVoltageCharacteristic/55d96687ee75aa11:26464063430fe52f:a69a946e7a02e547:c8965a35118ce6fc:67a8bfb44702cfc7:8131a44cea4d4bb8/2024-07-01T10:44:59.033161-test_sample_42']
```
The resulting list contains the path strings for all experiments with the specified
[``sample_id``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_id) (currently only one entry). These strings get quite long because they
contain the [``name``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement.name) of the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement)
procedure, followed by a hashed representation of the [``settings``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement.settings) dictionary,
and finally the datetime combined with the [``sample_id``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_id). With this
[``dataset``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.dataset) path, you may retrieve some information from the
[``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) object which got created by the [``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment):
```pycon
>>> dataset = db.filter_by_sample_id('test_sample_42')[0]
>>> metadata = db.load_metadata(dataset)
>>> metadata.sample_id, metadata.device, metadata.interface, metadata.measurement
('test_sample_42', 'Agilent4156C', 'MA8X8', 'CurrentVoltageCharacteristic')
```
Storing a new dataset is less trivial because you need a fully qualified [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) object,
which asks for a large number of arguments. Anyway, this is usually handled by the
[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) class because it guarantees that the specified components are compatible. For
testing, the [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) object from above may be used to initialize a new dataset:
```pycon
>>> db.initialize_dataset(metadata)
'/CurrentVoltageCharacteristic/55d96687ee75aa11:26464063430fe52f:a69a946e7a02e547:c8965a35118ce6fc:67a8bfb44702cfc7:8131a44cea4d4bb8/2024-07-01T10:46:05.910371-test_sample_42'
```
This yields practically the same ``dataset`` path as before, only the datetime is different. Adding data entries, on
the other hand, is fairly simple because you only need to specify the ``dataset`` and the ``contact_id`` (alongside
the ``data`` of course):
```pycon
>>> db.save_data(np.array([1]), dataset)
>>> db.save_data(np.array([42]), dataset, '1')
```
Finally, you may load a data entry by specifying the ``contact_id`` (or a list of several) or load an entire dataset,
including the [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata):
```pycon
>>> db.load_data(dataset, '0')
[array([1])]
>>> db.load_data(dataset, ['0', '1'])
[array([1]), array([42])]
>>> db.load_dataset(dataset)
({'0': array([1]), '1': array([42])}, Metadata(CurrentVoltageCharacteristic, Agilent4156C, MA8X8))
```
The [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) class also implements methods for filtering datasets based on
[``settings``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement.settings) of the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement). Check out the
documentation of the [``filter_by_settings``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database.filter_by_settings) and
[``filter_by_settings_batch``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database.filter_by_settings_batch) to learn more.
### Analyse the Results
The [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) is tightly bound with the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) because
this will determine how the data is shaped and which features you want to extract from it. Therefore, the base class
should be extended as explained in this tutorial:
- [Implement an Analysis](https://cohesivm.readthedocs.io/en/latest//tutorials/analysis.html)
However, in the following example, the base class will be used to show the basic functionality.
Since the [``MA8X8``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.MA8X8) interface was used in the previous examples, the dataset should be filled
with ``data`` accordingly. If you already have an HDF5 file from following the basic usage example ("Test.h5"), then
this script should do the job:
```python
import numpy as np
from cohesivm.database import Database
# load existing data and corresponding metadata
db = Database('Test.h5')
dataset = db.filter_by_sample_id('test_sample_42')[0]
metadata = db.load_metadata(dataset)
# create a new data to not interfere with previous examples
dataset = db.initialize_dataset(metadata)
# iterate over contact_ids and save data arrays
for contact_id in metadata.contact_ids:
db.save_data(np.array(range(10), dtype=[('Voltage (V)', float)]), dataset, contact_id)
# load the data
data, metadata = db.load_dataset(dataset)
```
This time, the [``save_data``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database.save_data) method was used correctly (contrary to the previous
examples) because the provided ``data`` should always be
a [structured array](https://numpy.org/doc/stable/user/basics.rec.html).
Next, [``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions) and [``plots``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.plots) must be defined:
```pycon
>>> def maximum(contact_id: str) -> float:
... return max(data[contact_id]['Voltage (V)'])
>>> functions = {'Maximum': maximum}
>>> plots = {} # will be spared for simplicity (refer to the tutorial instead)
```
This approach seems too complex for what the function does, but it makes sense if you consider that this should be
implemented in a separate [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) class. There, the data is stored as a property and the
[``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions) (i.e., methods) have direct access to it. Due to the use of structured
arrays (which facilitate to store the quantity and the unit alongside the data), the label also needs to be stated
explicitly. But, again, this will normally be available as a property.
In the following, the class is initialized with and without using the [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) from the
dataset. The former approach has the advantage that all available fields could be accessed by the
[``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions), e.g., values that are stored in the
[``measurement_settings``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata.measurement_settings).
```pycon
>>> from cohesivm.analysis import Analysis
# without metadata, the contact_position_dict must be provided
>>> analysis = Analysis(functions, plots, data, metadata.contact_position_dict)
# with metadata, additional metadata fields can be used in the analysis
>>> analysis = Analysis(functions, plots, (data, metadata))
>>> analysis.metadata.measurement_settings['illuminated']
True
```
The main usage of the [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis), besides providing the framework for
the [Analysis GUI](https://cohesivm.readthedocs.io/en/latest//guis/analysis.html), is to quickly generate maps of analysis results:
```pycon
>>> analysis.generate_result_maps('Maximum')[0]
array([[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.],
[9., 9., 9., 9., 9., 9., 9., 9.]])
```
As expected, the maximum value of the generated data is placed in a 2D numpy array on locations corresponding to
the [``contact_positions``](https://cohesivm.readthedocs.io/en/latest/reference/Interface.html#interfaces.Interface.contact_positions).
## Package Reference <a name="package-reference"></a>
The package reference can be found in the [documentation](https://cohesivm.readthedocs.io/en/latest/).
## Contributing <a name="contributing"></a>
We welcome contributions from the community to make COHESIVM better! If you'd like to contribute an implementation
of a [``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device), an [``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface),
a [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) or an [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis), please follow these steps:
1. Fork the repository to your own GitHub account.
2. Clone your forked repository to your local machine.
3. Create a new branch for your new component: git checkout -b my-new-component.
4. Make your changes and ensure the code passes existing tests.
5. Add new tests for your changes, if applicable.
6. Commit your changes with clear and concise messages.
7. Push your branch to your forked repository: git push origin my-new-feature.
8. Open a pull request to the main repository, describing the changes and why they should be merged.
Please make sure to follow the project's structure. The best way to start is to have a look at the tutorials given in
the [documentation](https://cohesivm.readthedocs.io/en/latest/). Also, don't forget to write tests for your newly implemented feature which may requires a new custom
marker in the ``conftest.py`` (e.g., if you implement a physical device for which the tests will always fail if it is
not connected).
You may also contribute by submitting feature requests, bugs and other issues over GitHub.
Thank you for your contributions!
## License <a name="license"></a>
The source code of this project is licensed under the [MIT license](LICENSE), and the hardware design and schematics
are licensed under the [CERN OHL v2 Permissive license](hardware/LICENSE).
Raw data
{
"_id": null,
"home_page": null,
"name": "cohesivm",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": null,
"author": null,
"author_email": "Maximilian Wolf <maximilian.wolf@ait.ac.at>, Selina G\u00f6tz <selina.goetz@ait.ac.at>, \"Georg K.H. Madsen\" <georg.madsen@tuwien.ac.at>, Theodoros Dimopoulos <theodoros.dimopoulos@ait.ac.at>",
"download_url": "https://files.pythonhosted.org/packages/ff/d5/c4a0eae2b491cf61e75743e6c352ae5f323213bbc01f8526f8e26b7da7a4/cohesivm-1.0.0.tar.gz",
"platform": null,
"description": "# COHESIVM: Combinatorial h+/e- Sample Investigation using Voltaic Measurements\n\n## Introduction\n\nThe COHESIVM Python package provides a versatile framework for conducting combinatorial voltaic measurements in\nscientific research and development. In order to enable a broad range of electrical and electrochemical analysis\nmethods, COHESIVM uses a generalized design of the main components which facilitates the extension and adaptation to\ndifferent measurement devices and routines. These components are cohesively put together in an\n[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) class which runs compatibility checks and executes the actual measurements.\n\n### Key Features:\n\n- **Modular Design:** COHESIVM adopts a module-oriented approach where components such as measurement devices\n ([``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device)), contacting interfaces ([``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface)), and measurement\n routines ([``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement)) are abstracted into interchangeable units. This modular\n architecture enhances flexibility in experimental setups and makes it easy to add new component implementations.\n- **Combinatorial Flexibility:** By abstracting the class for the contacting interface, COHESIVM enables diverse\n configurations for sample investigation. Researchers can simply implement their combinatorial sample design or even\n interface a robotic contacting system.\n- **Data Handling:** Collected data is stored in a structured [HDF5](https://www.hdfgroup.org/solutions/hdf5/) database\n format using the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) class, ensuring efficient data management and accessibility.\n [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) is collected based on the [DCMI standard](http://purl.org/dc/terms/) which is\n extended by COHESIVM-specific metadata terms.\n- **Analysis and GUIs:** Alongside the measurement routines, analysis functions and plots can be implemented, extending\n the [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) base class. Together with the graphical user interface (also available for\n conducting experiments and reviewing the database contents), initial screening of the data is facilitated.\n \n## Table of Contents\n- [Getting Started](#getting-started)\n- [Graphical User Interfaces](#graphical-user-interfaces)\n- [Examples](#examples)\n- [Package Reference](#package-reference)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Getting Started <a name=\"getting-started\"></a>\n\n### Installation\n\n#### Using pip\nTo install the core COHESIVM package from the Python Package Index (PyPI), simply run:\n\n```console\npip install cohesivm\n```\n\nThis command will download and install the latest stable version of COHESIVM and its core dependencies.\n\nIf you want to use the Graphical User Interfaces inside your [Jupyter](https://jupyter.org/) environment, make sure to \nspecify the ``gui`` extra:\n\n```console\npip install cohesivm[gui]\n```\n\nFurther extras exist for the implemented devices and for developers (refer to the ``pyproject.toml`` in the source \nfiles to get a listing of all available optional dependencies).\n\n#### Cloning from GitHub\nIf you want to install the development version of the package from the GitHub repository, follow these steps:\n1. **Clone** the repository to your local machine:\n ```console\n git clone https://github.com/mxwalbert/cohesivm.git\n ```\n2. **Navigate** into the cloned directory:\n ```console\n cd cohesivm\n ```\n3. **Install** the package and its dependencies:\n ```console\n pip install .\n ```\n\n### Configuration\n\nA ``config.ini`` file should be placed in the root of your project to configure the hardware ports/addresses of the\ncontact interfaces and measurement devices. Some DCMI metadata terms also need to be defined there. COHESIVM implements\na config parser which allows to access these values, e.g.:\n\n```pycon\n>>> import cohesivm\n>>> cohesivm.config.get_option('DCMI', 'creator')\nDow, John\n```\n\nA working file with the implemented interfaces and devices can be copied from the GitHub repository, or you can create\nyour own from this template:\n\n```ini\n# This file is used to configure the project as well as the devices and interfaces (e.g., COM ports, addresses, ...).\n\n# METADATA ------------------------------------------------------------------------------------------------------------\n\n[DCMI]\n# The following options correspond to the terms defined by the Dublin Core Metadata Initiative.\n# See https://purl.org/dc/terms/ for detailed descriptions.\npublisher = \"Your Company Ltd.\"\ncreator = \"Dow, John\"\nrights = <https://link.to/licence>\nsubject = \"modular design\"; \"combinatorial flexibility\"; \"data handling\"; \"analysis and gui\"\n\n# ---------------------------------------------------------------------------------------------------------------------\n\n\n# INTERFACES ----------------------------------------------------------------------------------------------------------\n\n[NAME_OF_USB_INTERFACE]\ncom_port = 42\n\n# ---------------------------------------------------------------------------------------------------------------------\n\n\n# DEVICES -------------------------------------------------------------------------------------------------------------\n\n[NAME_OF_NETWORK_DEVICE]\naddress = localhost\nport = 8888\ntimeout = 0.1\n\n# ---------------------------------------------------------------------------------------------------------------------\n```\n\n### Basic Usage\n\nWith working implementations of the main components ([``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device),\n[``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface), [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement)), setting up and running an\nexperiment only takes a few lines of code:\n\n```python\nfrom cohesivm import config\nfrom cohesivm.database import Database, Dimensions\nfrom cohesivm.devices.agilent import Agilent4156C\nfrom cohesivm.measurements.iv import CurrentVoltageCharacteristic\nfrom cohesivm.interfaces import MA8X8\nfrom cohesivm.experiment import Experiment\nfrom cohesivm.progressbar import ProgressBar\n\n# Create a new or load an existing database\ndb = Database('Test.h5')\n\n# Configure the components\nsmu = Agilent4156C.SweepVoltageSMUChannel()\ndevice = Agilent4156C.Agilent4156C(channels=[smu], **config.get_section('Agilent4156C'))\ninterface = MA8X8(com_port=config.get_option('MA8X8', 'com_port'), pixel_dimensions=Dimensions.Circle(radius=0.425))\nmeasurement = CurrentVoltageCharacteristic(start_voltage=-2.0, end_voltage=2.0, voltage_step=0.01, illuminated=True)\n\n# Combine the components in an experiment\nexperiment = Experiment(\n database=db,\n device=device,\n interface=interface,\n measurement=measurement,\n sample_id='test_sample_42',\n selected_contacts=None\n)\n\n# Optionally set up a progressbar\npbar = ProgressBar(experiment)\n\n# Run the experiment\nwith pbar.show():\n experiment.quickstart()\n```\n\nIf you want to change the measurement device to a different one, you only need to adjust the lines for the\n[``Channel``](https://cohesivm.readthedocs.io/en/latest/reference/channels.html#cohesivm.channels.Channel) and the [``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device) accordingly:\n\n```python\nfrom cohesivm.devices.ossila import OssilaX200\nsmu = OssilaX200.VoltageSMUChannel()\ndevice = OssilaX200.OssilaX200(channels=[smu], **config.get_section('OssilaX200'))\n```\n\n## Graphical User Interfaces <a name=\"graphical-user-interfaces\"></a>\n\nIf you work with [Jupyter](https://jupyter.org/), you may use the Graphical User Interfaces (GUIs) which are implemented\nin the form of [Jupyter Widgets](https://ipywidgets.readthedocs.io/en/stable/). Currently, three GUIs are available:\n\n### Experiment GUI\n\n\nOn the left panel \"Control\", you see the current [``ExperimentState``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.ExperimentState), followed by a \nrepresentation of the [``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface) and the control buttons at the bottom. The circles are \nannotated with the [``contact_ids``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface.contact_ids) and the colors correspond to their current state. \nOn the right panel \"Plot\", the currently running [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) is displayed. The plot is \nautomatically updated as soon as new measurement data arrives in the \n[``data_stream``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.data_stream) of the [``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) object.\n\n### Database GUI\n\n\nThis GUI enables to display and filter the measurement data which is stored in an HDF5 file. At the top, you select to\ndisplay the data grouped in terms of the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) or by the\n[``sample_name``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_name) of the [``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) object. If you\nchoose the former one, you may additionally filter the data by means of measurement parameters. The button to the very\nright of each data row enables you to copy the dataset path, to access it in the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database).\n\n### Analysis GUI\n\n\nSimilar to the Experiment GUI, the \"Interface\" panel represents the contacts with their respective IDs. They can be\nclicked to display the measured data in the \"Plot\" panel to the right. There, the arrows can be used to switch between\n[``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions) that are defined in the [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) class. The\nresults of the [``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions), which are also implemented there, are shown in the table\nbelow.\n\nDetailed guides to work with the GUIs can be found in the [documentation](https://cohesivm.readthedocs.io/en/latest/).\n\n## Examples <a name=\"examples\"></a>\n\n### Run an Experiment\n\nFollow the [Basic Usage](https://cohesivm.readthedocs.io/en/latest//getting_started/basic_usage.html) example to set up and run an \n[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment). If you do not have all components ready yet, follow these tutorials:\n\n- [Implement a Device](https://cohesivm.readthedocs.io/en/latest//tutorials/device.html)\n- [Implement an Interface](https://cohesivm.readthedocs.io/en/latest//tutorials/interface.html)\n- [Implement a Measurement](https://cohesivm.readthedocs.io/en/latest//tutorials/measurement.html)\n\nTo follow the other examples you may just run the code from the [Basic Usage](https://cohesivm.readthedocs.io/en/latest//getting_started/basic_usage.html) \nexample even if you do not have access to the hardware. This will fail but create an HDF5 file and store an empty \ndataset entry.\n\n### Manage the Data\n\nAfter you collected some data and stored it in an HDF5 file, you can use the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) class\nto work with it. First, initialise the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) object and list the samples which are stored\nthere:\n\n```pycon\n>>> from cohesivm.database import Database\n>>> db = Database('Test.h5')\n>>> db.get_sample_ids()\n['test_sample_42']\n```\n\nThis is exactly the [``sample_id``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_id) which was specified when the\n[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) was configured, and it can be used to retrieve the actual\n[``dataset``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.dataset) path in the [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) object:\n\n```pycon\n>>> db.filter_by_sample_id('test_sample_42')\n['/CurrentVoltageCharacteristic/55d96687ee75aa11:26464063430fe52f:a69a946e7a02e547:c8965a35118ce6fc:67a8bfb44702cfc7:8131a44cea4d4bb8/2024-07-01T10:44:59.033161-test_sample_42']\n```\n\nThe resulting list contains the path strings for all experiments with the specified\n[``sample_id``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_id) (currently only one entry). These strings get quite long because they\ncontain the [``name``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement.name) of the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement)\nprocedure, followed by a hashed representation of the [``settings``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement.settings) dictionary,\nand finally the datetime combined with the [``sample_id``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.sample_id). With this\n[``dataset``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment.dataset) path, you may retrieve some information from the\n[``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) object which got created by the [``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment):\n\n```pycon\n>>> dataset = db.filter_by_sample_id('test_sample_42')[0]\n>>> metadata = db.load_metadata(dataset)\n>>> metadata.sample_id, metadata.device, metadata.interface, metadata.measurement\n('test_sample_42', 'Agilent4156C', 'MA8X8', 'CurrentVoltageCharacteristic')\n```\n\nStoring a new dataset is less trivial because you need a fully qualified [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) object,\nwhich asks for a large number of arguments. Anyway, this is usually handled by the\n[``Experiment``](https://cohesivm.readthedocs.io/en/latest/reference/experiment.html#cohesivm.experiment.Experiment) class because it guarantees that the specified components are compatible. For\ntesting, the [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) object from above may be used to initialize a new dataset:\n\n```pycon\n>>> db.initialize_dataset(metadata)\n'/CurrentVoltageCharacteristic/55d96687ee75aa11:26464063430fe52f:a69a946e7a02e547:c8965a35118ce6fc:67a8bfb44702cfc7:8131a44cea4d4bb8/2024-07-01T10:46:05.910371-test_sample_42'\n```\n\nThis yields practically the same ``dataset`` path as before, only the datetime is different. Adding data entries, on\nthe other hand, is fairly simple because you only need to specify the ``dataset`` and the ``contact_id`` (alongside\nthe ``data`` of course):\n\n```pycon\n>>> db.save_data(np.array([1]), dataset)\n>>> db.save_data(np.array([42]), dataset, '1')\n```\n\nFinally, you may load a data entry by specifying the ``contact_id`` (or a list of several) or load an entire dataset,\nincluding the [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata):\n\n```pycon\n>>> db.load_data(dataset, '0')\n[array([1])]\n>>> db.load_data(dataset, ['0', '1'])\n[array([1]), array([42])]\n>>> db.load_dataset(dataset)\n({'0': array([1]), '1': array([42])}, Metadata(CurrentVoltageCharacteristic, Agilent4156C, MA8X8))\n```\n\nThe [``Database``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database) class also implements methods for filtering datasets based on\n[``settings``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement.settings) of the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement). Check out the \ndocumentation of the [``filter_by_settings``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database.filter_by_settings) and \n[``filter_by_settings_batch``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database.filter_by_settings_batch) to learn more.\n### Analyse the Results\n\nThe [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) is tightly bound with the [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) because\nthis will determine how the data is shaped and which features you want to extract from it. Therefore, the base class\nshould be extended as explained in this tutorial:\n\n- [Implement an Analysis](https://cohesivm.readthedocs.io/en/latest//tutorials/analysis.html)\n\nHowever, in the following example, the base class will be used to show the basic functionality.\n\nSince the [``MA8X8``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.MA8X8) interface was used in the previous examples, the dataset should be filled\nwith ``data`` accordingly. If you already have an HDF5 file from following the basic usage example (\"Test.h5\"), then\nthis script should do the job:\n\n```python\nimport numpy as np\nfrom cohesivm.database import Database\n\n# load existing data and corresponding metadata\ndb = Database('Test.h5')\ndataset = db.filter_by_sample_id('test_sample_42')[0]\nmetadata = db.load_metadata(dataset)\n\n# create a new data to not interfere with previous examples\ndataset = db.initialize_dataset(metadata)\n\n# iterate over contact_ids and save data arrays\nfor contact_id in metadata.contact_ids:\n db.save_data(np.array(range(10), dtype=[('Voltage (V)', float)]), dataset, contact_id)\n\n# load the data\ndata, metadata = db.load_dataset(dataset)\n```\n\nThis time, the [``save_data``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Database.save_data) method was used correctly (contrary to the previous\nexamples) because the provided ``data`` should always be\na [structured array](https://numpy.org/doc/stable/user/basics.rec.html).\n\nNext, [``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions) and [``plots``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.plots) must be defined:\n\n```pycon\n>>> def maximum(contact_id: str) -> float:\n... return max(data[contact_id]['Voltage (V)'])\n>>> functions = {'Maximum': maximum}\n>>> plots = {} # will be spared for simplicity (refer to the tutorial instead)\n```\n\nThis approach seems too complex for what the function does, but it makes sense if you consider that this should be\nimplemented in a separate [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis) class. There, the data is stored as a property and the\n[``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions) (i.e., methods) have direct access to it. Due to the use of structured\narrays (which facilitate to store the quantity and the unit alongside the data), the label also needs to be stated\nexplicitly. But, again, this will normally be available as a property.\n\nIn the following, the class is initialized with and without using the [``Metadata``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata) from the\ndataset. The former approach has the advantage that all available fields could be accessed by the\n[``functions``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis.functions), e.g., values that are stored in the\n[``measurement_settings``](https://cohesivm.readthedocs.io/en/latest/reference/database.html#cohesivm.database.Metadata.measurement_settings).\n\n```pycon\n>>> from cohesivm.analysis import Analysis\n# without metadata, the contact_position_dict must be provided\n>>> analysis = Analysis(functions, plots, data, metadata.contact_position_dict)\n# with metadata, additional metadata fields can be used in the analysis\n>>> analysis = Analysis(functions, plots, (data, metadata))\n>>> analysis.metadata.measurement_settings['illuminated']\nTrue\n```\n\nThe main usage of the [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis), besides providing the framework for\nthe [Analysis GUI](https://cohesivm.readthedocs.io/en/latest//guis/analysis.html), is to quickly generate maps of analysis results:\n\n```pycon\n>>> analysis.generate_result_maps('Maximum')[0]\narray([[9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.],\n [9., 9., 9., 9., 9., 9., 9., 9.]])\n```\n\nAs expected, the maximum value of the generated data is placed in a 2D numpy array on locations corresponding to\nthe [``contact_positions``](https://cohesivm.readthedocs.io/en/latest/reference/Interface.html#interfaces.Interface.contact_positions).\n## Package Reference <a name=\"package-reference\"></a>\n\nThe package reference can be found in the [documentation](https://cohesivm.readthedocs.io/en/latest/).\n\n## Contributing <a name=\"contributing\"></a>\n\nWe welcome contributions from the community to make COHESIVM better! If you'd like to contribute an implementation \nof a [``Device``](https://cohesivm.readthedocs.io/en/latest/reference/devices.html#cohesivm.devices.Device), an [``Interface``](https://cohesivm.readthedocs.io/en/latest/reference/interfaces.html#cohesivm.interfaces.Interface), \na [``Measurement``](https://cohesivm.readthedocs.io/en/latest/reference/measurements.html#cohesivm.measurements.Measurement) or an [``Analysis``](https://cohesivm.readthedocs.io/en/latest/reference/analysis.html#cohesivm.analysis.Analysis), please follow these steps:\n\n1. Fork the repository to your own GitHub account.\n2. Clone your forked repository to your local machine.\n3. Create a new branch for your new component: git checkout -b my-new-component.\n4. Make your changes and ensure the code passes existing tests.\n5. Add new tests for your changes, if applicable.\n6. Commit your changes with clear and concise messages.\n7. Push your branch to your forked repository: git push origin my-new-feature.\n8. Open a pull request to the main repository, describing the changes and why they should be merged.\n\nPlease make sure to follow the project's structure. The best way to start is to have a look at the tutorials given in \nthe [documentation](https://cohesivm.readthedocs.io/en/latest/). Also, don't forget to write tests for your newly implemented feature which may requires a new custom \nmarker in the ``conftest.py`` (e.g., if you implement a physical device for which the tests will always fail if it is \nnot connected).\n\nYou may also contribute by submitting feature requests, bugs and other issues over GitHub.\n\nThank you for your contributions!\n\n\n## License <a name=\"license\"></a>\nThe source code of this project is licensed under the [MIT license](LICENSE), and the hardware design and schematics \nare licensed under the [CERN OHL v2 Permissive license](hardware/LICENSE).\n",
"bugtrack_url": null,
"license": null,
"summary": "Combinatorial h+/e- Sample Investigation using Voltaic Measurements",
"version": "1.0.0",
"project_urls": {
"Documentation": "https://cohesivm.readthedocs.io/en/latest",
"Homepage": "https://github.com/mxwalbert/cohesivm",
"Issues": "https://github.com/mxwalbert/cohesivm/issues"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "21df3172ae4eb9004352c7ef81ed6b5f7acf263e090f2aa1f6e5053658e0227a",
"md5": "1ff9da5eab4caa38ff0b8a2e1d83b0f6",
"sha256": "fb738abc6637a7fb1082f94e0b4156c9809df0b82b82ee2a33c610feb844243c"
},
"downloads": -1,
"filename": "cohesivm-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1ff9da5eab4caa38ff0b8a2e1d83b0f6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 59285,
"upload_time": "2024-08-12T13:57:51",
"upload_time_iso_8601": "2024-08-12T13:57:51.228300Z",
"url": "https://files.pythonhosted.org/packages/21/df/3172ae4eb9004352c7ef81ed6b5f7acf263e090f2aa1f6e5053658e0227a/cohesivm-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ffd5c4a0eae2b491cf61e75743e6c352ae5f323213bbc01f8526f8e26b7da7a4",
"md5": "aea8279b5bb5af12500461f7d636e49a",
"sha256": "053faea15a1b07eeadd9309ec1d83bf1a4910414eb91b63fbbd87173b9b00fa1"
},
"downloads": -1,
"filename": "cohesivm-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "aea8279b5bb5af12500461f7d636e49a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 415700,
"upload_time": "2024-08-12T13:57:53",
"upload_time_iso_8601": "2024-08-12T13:57:53.539422Z",
"url": "https://files.pythonhosted.org/packages/ff/d5/c4a0eae2b491cf61e75743e6c352ae5f323213bbc01f8526f8e26b7da7a4/cohesivm-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-12 13:57:53",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mxwalbert",
"github_project": "cohesivm",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "cohesivm"
}