Repository: PlasmaPy/PlasmaPy

Issue:
Too many custom exceptions
I get it, sometimes when you're programming everything looks like a base class. On second thought, I really take issue with this, though:

https://github.com/PlasmaPy/PlasmaPy/blob/c560583ee731aca18cb3562ef8240fc43da9d502/plasmapy/__init__.py#L19-L24

This is the only place `UnsupportedPythonError` gets called in PlasmaPy. And given that we have https://github.com/PlasmaPy/PlasmaPy/blob/c560583ee731aca18cb3562ef8240fc43da9d502/plasmapy/utils/exceptions.py, I think this might be a common antipattern.

A bunch of `atomic` exceptions (better located in `atomic`, perhaps?) is probably fine. Likewise for `RelativityException` etc (though it might be worthwhile just `nan`ing that output). But things like this we're probably better off without!

Discussion:
pep8speaks: Hello @StanczakDominik! Thanks for submitting your pull request.



* In the file [`plasmapy/atomic/atomic.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/atomic.py):

> [Line 20:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/atomic.py#L20): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (125 > 99 characters)

* In the file [`plasmapy/atomic/exceptions.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/exceptions.py):

> [Line 60:9](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/exceptions.py#L60): [W292](https://duckduckgo.com/?q=pep8%20W292) no newline at end of file









* In the file [`plasmapy/atomic/particle_class.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/particle_class.py):

> [Line 15:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/particle_class.py#L15): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (149 > 99 characters)

* In the file [`plasmapy/atomic/particle_input.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/particle_input.py):

> [Line 14:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/particle_input.py#L14): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (144 > 99 characters)

* In the file [`plasmapy/atomic/tests/test_atomic.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/tests/test_atomic.py):

> [Line 42:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/tests/test_atomic.py#L42): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (149 > 99 characters)









* In the file [`plasmapy/atomic/tests/test_particle_class.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/tests/test_particle_class.py):

> [Line 18:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/tests/test_particle_class.py#L18): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (149 > 99 characters)

* In the file [`plasmapy/atomic/tests/test_particle_input.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/tests/test_particle_input.py):

> [Line 4:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/atomic/tests/test_particle_input.py#L4): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (144 > 99 characters)

* In the file [`plasmapy/classes/exceptions.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/classes/exceptions.py):

> [Line 6:9](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/classes/exceptions.py#L6): [W292](https://duckduckgo.com/?q=pep8%20W292) no newline at end of file













* In the file [`plasmapy/transport/braginskii.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/transport/braginskii.py):

> [Line 391:33](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/transport/braginskii.py#L391): [E127](https://duckduckgo.com/?q=pep8%20E127) continuation line over-indented for visual indent









* In the file [`plasmapy/utils/tests/test_exceptions.py`](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/utils/tests/test_exceptions.py):

> [Line 4:100](https://github.com/PlasmaPy/PlasmaPy/blob/d9bb4ad944c23adc2066d1a322944a11a3583808/plasmapy/utils/tests/test_exceptions.py#L4): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (149 > 99 characters)



PlasmaPy follows the [PEP8 style guide](https://www.python.org/dev/peps/pep-0008/), which may be checked locally using [pycodestyle](http://pycodestyle.pycqa.org/en/latest/intro.html)
codecov[bot]: # [Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=h1) Report
> Merging [#616](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=desc) into [master](https://codecov.io/gh/PlasmaPy/PlasmaPy/commit/91848cbd399cc6133c78e1b3668d030051cbf65e?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `97.82%`.

[![Impacted file tree graph](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/graphs/tree.svg?width=650&token=LzZ5mnXVad&height=150&src=pr)](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #616      +/-   ##
==========================================
+ Coverage   96.47%   96.47%   +<.01%     
==========================================
  Files          49       51       +2     
  Lines        4507     4516       +9     
==========================================
+ Hits         4348     4357       +9     
  Misses        159      159
```


| [Impacted Files](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [plasmapy/utils/exceptions.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvdXRpbHMvZXhjZXB0aW9ucy5weQ==) | `100% <ø> (ø)` | :arrow_up: |
| [plasmapy/utils/\_\_init\_\_.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvdXRpbHMvX19pbml0X18ucHk=) | `100% <ø> (ø)` | :arrow_up: |
| [plasmapy/\_\_init\_\_.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvX19pbml0X18ucHk=) | `46.15% <0%> (-7.18%)` | :arrow_down: |
| [plasmapy/transport/braginskii.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvdHJhbnNwb3J0L2JyYWdpbnNraWkucHk=) | `99.71% <100%> (ø)` | :arrow_up: |
| [plasmapy/atomic/particle\_input.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvYXRvbWljL3BhcnRpY2xlX2lucHV0LnB5) | `100% <100%> (ø)` | :arrow_up: |
| [plasmapy/atomic/parsing.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvYXRvbWljL3BhcnNpbmcucHk=) | `97.86% <100%> (+0.01%)` | :arrow_up: |
| [plasmapy/classes/sources/openpmd\_hdf5.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvY2xhc3Nlcy9zb3VyY2VzL29wZW5wbWRfaGRmNS5weQ==) | `92.5% <100%> (ø)` | :arrow_up: |
| [plasmapy/atomic/ionization\_states.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvYXRvbWljL2lvbml6YXRpb25fc3RhdGVzLnB5) | `90.69% <100%> (+0.02%)` | :arrow_up: |
| [plasmapy/atomic/particle\_class.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvYXRvbWljL3BhcnRpY2xlX2NsYXNzLnB5) | `96.63% <100%> (ø)` | :arrow_up: |
| [plasmapy/classes/exceptions.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree#diff-cGxhc21hcHkvY2xhc3Nlcy9leGNlcHRpb25zLnB5) | `100% <100%> (ø)` | |
| ... and [7 more](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=footer). Last update [91848cb...71e1f7c](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/616?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/README.md b/README.md
index bf840653..2fa0db98 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,8 @@ # PlasmaPy
 <andy.j.leonard@gmail.com>.  You may also [sign up for PlasmaPy's email
 list](https://groups.google.com/forum/#!forum/plasmapy).
 
+We also have approximately bi-weekly online meetings at [Jitsi](https://meet.jit.si/plasmapy), for which we have a [calendar here](https://calendar.google.com/calendar?cid=bzVsb3ZkcW0zaWxsam00ZTlrMDd2cmw5bWdAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) and [written meeting notes here](https://drive.google.com/drive/folders/0ByPG8nie6fTPV1FQUEkzMTgtRTg?usp=sharing). Come discuss plasma software with us!
+
 ## Installation
 
 You can get PlasmaPy from pip via `pip install plasmapy`. To contribute
diff --git a/docs/atomic/functional.rst b/docs/atomic/functional.rst
index d06c92ed..f6800d6f 100644
--- a/docs/atomic/functional.rst
+++ b/docs/atomic/functional.rst
@@ -56,13 +56,13 @@ and `~plasmapy.atomic.electric_charge`.
 >>> electric_charge('muon antineutrino')
 <Quantity 0. C>
 
-These functions will raise a `~plasmapy.utils.ChargeError` for
+These functions will raise a `~plasmapy.atomic.exceptions.ChargeError` for
 elements and isotopes that lack explicit charge information.
 
 >>> electric_charge('H')
 Traceback (most recent call last):
   ...
-plasmapy.utils.exceptions.ChargeError: Charge information is required for electric_charge.
+plasmapy.atomic.exceptions.ChargeError: Charge information is required for electric_charge.
 
 The standard atomic weight for the terrestrial environment may be
 accessed using `~plasmapy.atomic.standard_atomic_weight`.
@@ -134,7 +134,7 @@ unstable), `~plasmapy.atomic.half_life` returns infinity seconds.
 
 If the particle's half-life is not known to sufficient precision, then
 `~plasmapy.atomic.half_life` returns a `str` with the estimated value
-while issuing a `~plasmapy.utils.MissingAtomicDataWarning`.
+while issuing a `~plasmapy.atomic.exceptions.MissingAtomicDataWarning`.
 
 Additional Properties
 =====================
diff --git a/plasmapy/__init__.py b/plasmapy/__init__.py
index c5ec62e4..65accb4a 100644
--- a/plasmapy/__init__.py
+++ b/plasmapy/__init__.py
@@ -51,12 +51,8 @@
 
 __name__ = "plasmapy"
 
-class UnsupportedPythonError(Exception):
-    pass
-
-
 if sys.version_info < tuple((int(val) for val in "3.6".split('.'))):
-    raise UnsupportedPythonError("plasmapy does not support Python < {}".format(3.6))
+    raise Exception("plasmapy does not support Python < {}".format(3.6))
 
 def online_help(query):
     """
diff --git a/plasmapy/atomic/atomic.py b/plasmapy/atomic/atomic.py
index 47eb8678..f356e3bb 100644
--- a/plasmapy/atomic/atomic.py
+++ b/plasmapy/atomic/atomic.py
@@ -17,12 +17,12 @@
 from plasmapy.atomic.particle_input import particle_input
 from plasmapy.atomic.symbols import atomic_symbol
 
-from plasmapy.utils import (
-    InvalidParticleError,
-    InvalidElementError,
-    InvalidIsotopeError,
+from plasmapy.atomic.exceptions import (
     MissingAtomicDataError,
-)
+    InvalidIsotopeError,
+    InvalidElementError,
+    InvalidParticleError,
+    )
 
 from numbers import (
     Integral,
diff --git a/plasmapy/atomic/exceptions.py b/plasmapy/atomic/exceptions.py
new file mode 100644
index 00000000..a37ed0f8
--- /dev/null
+++ b/plasmapy/atomic/exceptions.py
@@ -0,0 +1,60 @@
+from plasmapy.utils import PlasmaPyError, PlasmaPyWarning
+
+
+class AtomicError(PlasmaPyError):
+    """An exception for errors in the `~plasmapy.atomic` subpackage."""
+    pass
+
+
+class MissingAtomicDataError(AtomicError):
+    """An exception for missing atomic or particle data."""
+    pass
+
+
+class ChargeError(AtomicError):
+    """An exception for incorrect or missing charge information."""
+    pass
+
+
+class UnexpectedParticleError(AtomicError):
+    """An exception for when a particle is not of the expected category."""
+    pass
+
+
+class InvalidIonError(UnexpectedParticleError):
+    """
+    An exception for when an argument is a valid particle but not a
+    valid ion.
+    """
+    pass
+
+
+class InvalidIsotopeError(UnexpectedParticleError):
+    """
+    An exception for when an argument is a valid particle but not a
+    valid isotope.
+    """
+    pass
+
+
+class InvalidElementError(UnexpectedParticleError):
+    """
+    An exception for when an argument is a valid particle is not a
+    valid element.
+    """
+    pass
+
+
+class InvalidParticleError(AtomicError):
+    """An exception for when a particle is invalid."""
+    pass
+
+
+class AtomicWarning(PlasmaPyWarning):
+    """The base warning for the `~plasmapy.atomic` subpackage."""
+    pass
+
+
+class MissingAtomicDataWarning(AtomicWarning):
+    """Warning for use when atomic or particle data is missing."""
+    pass
diff --git a/plasmapy/atomic/ionization_state.py b/plasmapy/atomic/ionization_state.py
index ae833765..47423f6c 100644
--- a/plasmapy/atomic/ionization_state.py
+++ b/plasmapy/atomic/ionization_state.py
@@ -12,7 +12,8 @@
 import astropy.units as u
 
 from plasmapy.atomic import Particle, particle_input
-from plasmapy.utils import AtomicError, ChargeError, InvalidParticleError, check_quantity
+from plasmapy.utils import check_quantity
+from plasmapy.atomic.exceptions import AtomicError, ChargeError, InvalidParticleError
 
 __all__ = ["IonizationState", "State"]
 
diff --git a/plasmapy/atomic/ionization_states.py b/plasmapy/atomic/ionization_states.py
index a414c559..bd68f594 100644
--- a/plasmapy/atomic/ionization_states.py
+++ b/plasmapy/atomic/ionization_states.py
@@ -11,7 +11,8 @@
 import astropy.units as u
 
 from plasmapy.atomic import atomic_number, Particle, particle_symbol, IonizationState, State
-from plasmapy.utils import AtomicError, ChargeError, InvalidParticleError, check_quantity
+from plasmapy.utils import check_quantity
+from plasmapy.atomic.exceptions import AtomicError, ChargeError, InvalidParticleError
 
 __all__ = ["IonizationStates"]
 
diff --git a/plasmapy/atomic/nuclear.py b/plasmapy/atomic/nuclear.py
index 2ccc0266..41b1e789 100644
--- a/plasmapy/atomic/nuclear.py
+++ b/plasmapy/atomic/nuclear.py
@@ -4,10 +4,7 @@
 from typing import List, Union, Optional
 import re
 
-from plasmapy.utils import (
-    AtomicError,
-    InvalidParticleError,
-)
+from plasmapy.atomic.exceptions import AtomicError, InvalidParticleError
 
 from plasmapy.atomic.particle_class import Particle
 from plasmapy.atomic.particle_input import particle_input
diff --git a/plasmapy/atomic/parsing.py b/plasmapy/atomic/parsing.py
index 7906dc88..976a4bd2 100644
--- a/plasmapy/atomic/parsing.py
+++ b/plasmapy/atomic/parsing.py
@@ -15,11 +15,9 @@
 from plasmapy.atomic.special_particles import _Particles, ParticleZoo
 
 from plasmapy.utils import (
-                     AtomicWarning,
-                     InvalidElementError,
-                     InvalidParticleError,
-                     roman
+    roman
 )
+from plasmapy.atomic.exceptions import InvalidElementError, InvalidParticleError, AtomicWarning
 
 
 def _create_alias_dicts(Particles: dict) -> (Dict[str, str], Dict[str, str]):
diff --git a/plasmapy/atomic/particle_class.py b/plasmapy/atomic/particle_class.py
index de72deee..097f38f5 100644
--- a/plasmapy/atomic/particle_class.py
+++ b/plasmapy/atomic/particle_class.py
@@ -12,17 +12,17 @@
 import plasmapy.utils.roman as roman
 from plasmapy.atomic.elements import _Elements, _PeriodicTable
 from plasmapy.atomic.isotopes import _Isotopes
-from plasmapy.utils import (
+from plasmapy.atomic.exceptions import (
     AtomicError,
-    AtomicWarning,
-    InvalidParticleError,
-    InvalidElementError,
-    InvalidIsotopeError,
-    InvalidIonError,
-    ChargeError,
     MissingAtomicDataError,
+    ChargeError,
+    InvalidIonError,
+    InvalidIsotopeError,
+    InvalidElementError,
+    InvalidParticleError,
+    AtomicWarning,
     MissingAtomicDataWarning,
-)
+    )
 from plasmapy.atomic.parsing import (
     _dealias_particle_aliases,
     _parse_and_check_atomic_input,
diff --git a/plasmapy/atomic/particle_input.py b/plasmapy/atomic/particle_input.py
index f7f5bf10..65bbec79 100644
--- a/plasmapy/atomic/particle_input.py
+++ b/plasmapy/atomic/particle_input.py
@@ -11,14 +11,14 @@
 
 from .particle_class import Particle
 
-from plasmapy.utils import (
+from plasmapy.atomic.exceptions import (
     AtomicError,
-    InvalidParticleError,
-    InvalidElementError,
+    ChargeError,
     InvalidIonError,
     InvalidIsotopeError,
-    ChargeError,
-)
+    InvalidElementError,
+    InvalidParticleError,
+    )
 
 __all__ = [
     "particle_input",
diff --git a/plasmapy/classes/exceptions.py b/plasmapy/classes/exceptions.py
new file mode 100644
index 00000000..42d23da0
--- /dev/null
+++ b/plasmapy/classes/exceptions.py
@@ -0,0 +1,6 @@
+from plasmapy.utils import PlasmaPyError
+
+
+class DataStandardError(PlasmaPyError):
+    """An exception for when HDF5 is not defined by OpenPMD standard."""
+    pass
diff --git a/plasmapy/classes/sources/openpmd_hdf5.py b/plasmapy/classes/sources/openpmd_hdf5.py
index bf53b121..1dc04a23 100644
--- a/plasmapy/classes/sources/openpmd_hdf5.py
+++ b/plasmapy/classes/sources/openpmd_hdf5.py
@@ -7,7 +7,7 @@
     raise ImportError(h5py_import_error) from e
 
 from plasmapy.classes import GenericPlasma
-from plasmapy.utils import DataStandardError
+from plasmapy.classes.exceptions import DataStandardError
 
 import os
 from distutils.version import StrictVersion
diff --git a/plasmapy/physics/parameters.py b/plasmapy/physics/parameters.py
index 07eb2d33..8d1de4a8 100644
--- a/plasmapy/physics/parameters.py
+++ b/plasmapy/physics/parameters.py
@@ -34,7 +34,8 @@
 from plasmapy.constants import (m_p, m_e, c, mu0, k_B, e, eps0, pi)
 from plasmapy import atomic, utils
 
-from plasmapy.utils.exceptions import (PhysicsError, AtomicError)
+from plasmapy.utils.exceptions import (PhysicsError)
+from plasmapy.atomic.exceptions import AtomicError
 
 
 def _grab_charge(ion, z_mean=None):
diff --git a/plasmapy/transport/braginskii.py b/plasmapy/transport/braginskii.py
index cc2af9f4..0ae20181 100644
--- a/plasmapy/transport/braginskii.py
+++ b/plasmapy/transport/braginskii.py
@@ -129,6 +129,7 @@
 from plasmapy import utils
 from plasmapy import atomic
 from plasmapy.atomic.atomic import _is_electron
+from plasmapy.utils import PhysicsError
 from .collisions import Coulomb_logarithm
 from plasmapy.physics.parameters import (Hall_parameter,
                                          _grab_charge)
@@ -376,10 +377,6 @@ def __init__(self,
         # calculate Coulomb logs if not forced in input
         if coulomb_log_ei is not None:
             self.coulomb_log_ei = coulomb_log_ei
-            if self.coulomb_log_ei < 4:
-                warnings.warn(f"Coulomb logarithm is {coulomb_log_ei},"
-                              f" you might have strong coupling effects",
-                              utils.PhysicsWarning)
         else:
             self.coulomb_log_ei = Coulomb_logarithm(T_e,
                                                     n_e,
@@ -387,12 +384,18 @@ def __init__(self,
                                                      self.ion_particle),
                                                     V_ei,
                                                     method=coulomb_log_method)
+
+        if self.coulomb_log_ei < 1:
+            # TODO discuss whether this is not too strict
+            raise PhysicsError(f"Coulomb logarithm is {coulomb_log_ei} (below 1),"
+                               "this is probably not physical!")
+        elif self.coulomb_log_ei < 4:
+            warnings.warn(f"Coulomb logarithm is {coulomb_log_ei},"
+                          f" you might have strong coupling effects",
+                          utils.CouplingWarning)
+
         if coulomb_log_ii is not None:
             self.coulomb_log_ii = coulomb_log_ii
-            if self.coulomb_log_ii < 4:
-                warnings.warn(f"Coulomb logarithm is {coulomb_log_ii},"
-                              f" you might have strong coupling effects",
-                              utils.PhysicsWarning)
         else:
             self.coulomb_log_ii = Coulomb_logarithm(T_i,
                                                     n_e,  # this is not a typo!
@@ -401,6 +404,15 @@ def __init__(self,
                                                     V_ii,
                                                     method=coulomb_log_method)
 
+        if self.coulomb_log_ii < 1:
+            # TODO discuss whether this is not too strict
+            raise PhysicsError(f"Coulomb logarithm is {coulomb_log_ii} (below 1),"
+                               "this is probably not physical!")
+        elif self.coulomb_log_ii < 4:
+            warnings.warn(f"Coulomb logarithm is {coulomb_log_ii},"
+                          f" you might have strong coupling effects",
+                          utils.CouplingWarning)
+
         # calculate Hall parameters if not forced in input
         if hall_e is not None:
             self.hall_e = hall_e
diff --git a/plasmapy/utils/__init__.py b/plasmapy/utils/__init__.py
index 1fe40615..469b8b89 100644
--- a/plasmapy/utils/__init__.py
+++ b/plasmapy/utils/__init__.py
@@ -6,20 +6,10 @@
 from .exceptions import (PlasmaPyError,
                          PhysicsError,
                          RelativityError,
-                         AtomicError,
-                         MissingAtomicDataError,
-                         ChargeError,
-                         InvalidIonError,
-                         InvalidIsotopeError,
-                         InvalidElementError,
-                         InvalidParticleError,
-                         DataStandardError,
                          PlasmaPyWarning,
                          PhysicsWarning,
                          CouplingWarning,
-                         RelativityWarning,
-                         AtomicWarning,
-                         MissingAtomicDataWarning)
+                         RelativityWarning)
 
 from .pytest_helpers import (
     run_test,
diff --git a/plasmapy/utils/exceptions.py b/plasmapy/utils/exceptions.py
index 24b709c1..b2e31d53 100644
--- a/plasmapy/utils/exceptions.py
+++ b/plasmapy/utils/exceptions.py
@@ -29,60 +29,6 @@ class RelativityError(PhysicsError):
     pass
 
 
-class AtomicError(PlasmaPyError):
-    """An exception for errors in the `~plasmapy.atomic` subpackage."""
-    pass
-
-
-class MissingAtomicDataError(AtomicError):
-    """An exception for missing atomic or particle data."""
-    pass
-
-
-class ChargeError(AtomicError):
-    """An exception for incorrect or missing charge information."""
-    pass
-
-
-class UnexpectedParticleError(AtomicError):
-    """An exception for when a particle is not of the expected category."""
-    pass
-
-
-class InvalidIonError(UnexpectedParticleError):
-    """
-    An exception for when an argument is a valid particle but not a
-    valid ion.
-    """
-    pass
-
-
-class InvalidIsotopeError(UnexpectedParticleError):
-    """
-    An exception for when an argument is a valid particle but not a
-    valid isotope.
-    """
-    pass
-
-
-class InvalidElementError(UnexpectedParticleError):
-    """
-    An exception for when an argument is a valid particle is not a
-    valid element.
-    """
-    pass
-
-
-class InvalidParticleError(AtomicError):
-    """An exception for when a particle is invalid."""
-    pass
-
-
-class DataStandardError(PlasmaPyError):
-    """An exception for when HDF5 is not defined by OpenPMD standard."""
-    pass
-
-
 # ----------
 # Warnings:
 # ----------
@@ -120,11 +66,3 @@ class CouplingWarning(PhysicsWarning):
     """
 
 
-class AtomicWarning(PlasmaPyWarning):
-    """The base warning for the `~plasmapy.atomic` subpackage."""
-    pass
-
-
-class MissingAtomicDataWarning(AtomicWarning):
-    """Warning for use when atomic or particle data is missing."""
-    pass


Test patch:
diff --git a/plasmapy/atomic/tests/test_atomic.py b/plasmapy/atomic/tests/test_atomic.py
index 5b92eaf0..1a399101 100644
--- a/plasmapy/atomic/tests/test_atomic.py
+++ b/plasmapy/atomic/tests/test_atomic.py
@@ -38,15 +38,17 @@
 
 from ...utils import (
     run_test,
-    AtomicWarning,
-    InvalidElementError,
-    InvalidIsotopeError,
-    InvalidIonError,
-    ChargeError,
+    )
+from plasmapy.atomic.exceptions import (
     AtomicError,
     MissingAtomicDataError,
+    ChargeError,
+    InvalidIonError,
+    InvalidIsotopeError,
+    InvalidElementError,
     InvalidParticleError,
-)
+    AtomicWarning,
+    )
 
 # function to be tested, argument(s), expected result/outcome
 
diff --git a/plasmapy/atomic/tests/test_ionization_state.py b/plasmapy/atomic/tests/test_ionization_state.py
index af974818..d2220067 100644
--- a/plasmapy/atomic/tests/test_ionization_state.py
+++ b/plasmapy/atomic/tests/test_ionization_state.py
@@ -5,7 +5,8 @@
 import astropy.units as u
 
 from plasmapy.atomic.ionization_state import IonizationState
-from plasmapy.utils import AtomicError, InvalidIsotopeError, run_test
+from plasmapy.utils import run_test
+from plasmapy.atomic.exceptions import AtomicError, InvalidIsotopeError
 from plasmapy.atomic import (
     atomic_number,
     atomic_symbol,
diff --git a/plasmapy/atomic/tests/test_ionization_states.py b/plasmapy/atomic/tests/test_ionization_states.py
index df1b0d7d..844d582a 100644
--- a/plasmapy/atomic/tests/test_ionization_states.py
+++ b/plasmapy/atomic/tests/test_ionization_states.py
@@ -7,7 +7,8 @@
 import numpy as np
 import astropy.units as u
 
-from plasmapy.utils import AtomicError, InvalidIsotopeError, run_test
+from plasmapy.utils import run_test
+from plasmapy.atomic.exceptions import AtomicError, InvalidIsotopeError
 from plasmapy.atomic import (
     State,
     IonizationState,
diff --git a/plasmapy/atomic/tests/test_nuclear.py b/plasmapy/atomic/tests/test_nuclear.py
index 515e374c..1cb0d15a 100644
--- a/plasmapy/atomic/tests/test_nuclear.py
+++ b/plasmapy/atomic/tests/test_nuclear.py
@@ -2,12 +2,10 @@
 import numpy as np
 from ..nuclear import nuclear_binding_energy, nuclear_reaction_energy, mass_energy
 from ...utils import (
-    InvalidParticleError,
-    InvalidIsotopeError,
-    AtomicError,
     run_test,
     run_test_equivalent_calls,
 )
+from plasmapy.atomic.exceptions import AtomicError, InvalidIsotopeError, InvalidParticleError
 import pytest
 
 test_nuclear_table = [
diff --git a/plasmapy/atomic/tests/test_parsing.py b/plasmapy/atomic/tests/test_parsing.py
index de3337c5..cf098e5f 100644
--- a/plasmapy/atomic/tests/test_parsing.py
+++ b/plasmapy/atomic/tests/test_parsing.py
@@ -1,10 +1,6 @@
 import pytest
 
-from ...utils import (
-    InvalidParticleError,
-    InvalidElementError,
-    AtomicWarning,
-)
+from plasmapy.atomic.exceptions import InvalidElementError, InvalidParticleError, AtomicWarning
 
 from ..special_particles import ParticleZoo
 
diff --git a/plasmapy/atomic/tests/test_particle_class.py b/plasmapy/atomic/tests/test_particle_class.py
index 0dedd8ce..50e9f5e2 100644
--- a/plasmapy/atomic/tests/test_particle_class.py
+++ b/plasmapy/atomic/tests/test_particle_class.py
@@ -12,18 +12,20 @@
 from plasmapy.atomic.particle_class import Particle
 from plasmapy.atomic.special_particles import ParticleZoo
 from plasmapy.utils import (
-    AtomicWarning,
-    AtomicError,
-    MissingAtomicDataError,
-    MissingAtomicDataWarning,
-    InvalidParticleError,
-    InvalidElementError,
-    InvalidIsotopeError,
-    InvalidIonError,
-    ChargeError,
     call_string,
     run_test_equivalent_calls,
 )
+from plasmapy.atomic.exceptions import (
+    AtomicError,
+    MissingAtomicDataError,
+    ChargeError,
+    InvalidIonError,
+    InvalidIsotopeError,
+    InvalidElementError,
+    InvalidParticleError,
+    AtomicWarning,
+    MissingAtomicDataWarning,
+    )
 
 # (arg, kwargs, results_dict)
 test_Particle_table = [
diff --git a/plasmapy/atomic/tests/test_particle_input.py b/plasmapy/atomic/tests/test_particle_input.py
index 77f2b324..843adc9d 100644
--- a/plasmapy/atomic/tests/test_particle_input.py
+++ b/plasmapy/atomic/tests/test_particle_input.py
@@ -1,14 +1,14 @@
 import pytest
 from typing import Optional, Union, Tuple, List
 
-from ...utils import (
+from plasmapy.atomic.exceptions import (
     AtomicError,
-    InvalidParticleError,
     ChargeError,
-    InvalidElementError,
     InvalidIonError,
     InvalidIsotopeError,
-)
+    InvalidElementError,
+    InvalidParticleError,
+    )
 
 from ..particle_class import Particle
 from ..particle_input import particle_input
diff --git a/plasmapy/classes/sources/tests/test_openpmd_hdf5.py b/plasmapy/classes/sources/tests/test_openpmd_hdf5.py
index 3ce37b2c..9fa2357e 100644
--- a/plasmapy/classes/sources/tests/test_openpmd_hdf5.py
+++ b/plasmapy/classes/sources/tests/test_openpmd_hdf5.py
@@ -1,5 +1,5 @@
 from plasmapy.classes.sources import openpmd_hdf5
-from plasmapy.utils import DataStandardError
+from plasmapy.classes.exceptions import DataStandardError
 from plasmapy.data.test import rootdir
 
 from astropy import units as u
diff --git a/plasmapy/classes/sources/tests/test_plasma3d.py b/plasmapy/classes/sources/tests/test_plasma3d.py
index 92de1b38..dc19099a 100644
--- a/plasmapy/classes/sources/tests/test_plasma3d.py
+++ b/plasmapy/classes/sources/tests/test_plasma3d.py
@@ -3,7 +3,7 @@
 import astropy.units as u
 
 from plasmapy.classes.sources import plasma3d
-from plasmapy.utils.exceptions import InvalidParticleError
+from plasmapy.atomic.exceptions import InvalidParticleError
 
 
 @pytest.mark.parametrize('grid_dimensions, expected_size', [
diff --git a/plasmapy/classes/sources/tests/test_plasmablob.py b/plasmapy/classes/sources/tests/test_plasmablob.py
index 0b94f37e..36116fa3 100644
--- a/plasmapy/classes/sources/tests/test_plasmablob.py
+++ b/plasmapy/classes/sources/tests/test_plasmablob.py
@@ -4,7 +4,8 @@
 
 from plasmapy.classes.sources import plasma3d, plasmablob
 from plasmapy.physics import magnetostatics
-from plasmapy.utils.exceptions import InvalidParticleError
+from plasmapy.atomic.exceptions import InvalidParticleError
+
 
 @pytest.mark.parametrize('grid_dimensions, expected_size', [
     ((100, 1, 1), 100),  # Test 1D setup
diff --git a/plasmapy/physics/tests/test_parameters.py b/plasmapy/physics/tests/test_parameters.py
index a067fd38..244572dd 100644
--- a/plasmapy/physics/tests/test_parameters.py
+++ b/plasmapy/physics/tests/test_parameters.py
@@ -7,7 +7,8 @@
 from warnings import simplefilter
 
 from plasmapy.utils.exceptions import RelativityWarning, RelativityError
-from plasmapy.utils.exceptions import PhysicsError, InvalidParticleError
+from plasmapy.utils.exceptions import PhysicsError
+from plasmapy.atomic.exceptions import InvalidParticleError
 from plasmapy.utils.pytest_helpers import assert_can_handle_nparray
 from astropy.constants import c, m_p, m_e, e, mu0
 
diff --git a/plasmapy/transport/tests/test_collisions.py b/plasmapy/transport/tests/test_collisions.py
index 28858565..41ce12b0 100644
--- a/plasmapy/transport/tests/test_collisions.py
+++ b/plasmapy/transport/tests/test_collisions.py
@@ -3,6 +3,7 @@
 from astropy import units as u
 from astropy.tests.helper import assert_quantity_allclose
 
+import plasmapy.atomic.exceptions
 from plasmapy.transport import (Coulomb_logarithm,
                                 impact_parameter_perp,
                                 impact_parameter,
@@ -553,7 +554,7 @@ def test_invalid_particle_error(self):
         Tests whether an error is raised when an invalid particle name
         is given.
         """
-        with pytest.raises(exceptions.InvalidParticleError):
+        with pytest.raises(plasmapy.atomic.exceptions.InvalidParticleError):
             Coulomb_logarithm(1 * u.K, 5 * u.m ** -3, ('e', 'g'))
 
     n_e = np.array([1e9, 1e9, 1e24]) * u.cm ** -3
diff --git a/plasmapy/transport/tests/test_transport.py b/plasmapy/transport/tests/test_transport.py
index 65c14ece..4794baa6 100644
--- a/plasmapy/transport/tests/test_transport.py
+++ b/plasmapy/transport/tests/test_transport.py
@@ -6,9 +6,9 @@
 from plasmapy.atomic.atomic import particle_mass, integer_charge
 from astropy.tests.helper import assert_quantity_allclose
 from plasmapy.utils.exceptions import (PhysicsError,
-                                       PhysicsWarning,
-                                       InvalidParticleError,
+                                       CouplingWarning,
                                        RelativityWarning)
+from plasmapy.atomic.exceptions import InvalidParticleError
 from plasmapy.physics.parameters import Hall_parameter
 from plasmapy.constants import m_p, m_e
 
@@ -207,8 +207,8 @@ def test_Z_checks(self):
                                      Z=-1)
 
     def test_coulomb_log_warnings(self):
-        """should warn PhysicsWarning if coulomb log is near 1"""
-        with pytest.warns(PhysicsWarning):
+        """should warn CouplingWarning if coulomb log is near 1"""
+        with pytest.warns(CouplingWarning):
             ct2 = ClassicalTransport(T_e=self.T_e,
                                      n_e=self.n_e,
                                      T_i=self.T_i,
@@ -216,7 +216,7 @@ def test_coulomb_log_warnings(self):
                                      ion_particle=self.ion_particle,
                                      coulomb_log_ii=1.3)
 
-        with pytest.warns(PhysicsWarning):
+        with pytest.warns(CouplingWarning):
             ct2 = ClassicalTransport(T_e=self.T_e,
                                      n_e=self.n_e,
                                      T_i=self.T_i,
@@ -226,7 +226,7 @@ def test_coulomb_log_warnings(self):
 
     def test_coulomb_log_errors(self):
         """should raise PhysicsError if coulomb log is < 1"""
-        with pytest.warns(PhysicsWarning):
+        with pytest.raises(PhysicsError):
             ct2 = ClassicalTransport(T_e=self.T_e,
                                      n_e=self.n_e,
                                      T_i=self.T_i,
@@ -234,7 +234,7 @@ def test_coulomb_log_errors(self):
                                      ion_particle=self.ion_particle,
                                      coulomb_log_ii=0.3)
 
-        with pytest.warns(PhysicsWarning):
+        with pytest.raises(PhysicsError):
             ct2 = ClassicalTransport(T_e=self.T_e,
                                      n_e=self.n_e,
                                      T_i=self.T_i,
diff --git a/plasmapy/utils/tests/test_exceptions.py b/plasmapy/utils/tests/test_exceptions.py
index 88c1bf08..cecdafcb 100644
--- a/plasmapy/utils/tests/test_exceptions.py
+++ b/plasmapy/utils/tests/test_exceptions.py
@@ -1,6 +1,18 @@
 import pytest
 import warnings
 
+from plasmapy.atomic.exceptions import (
+    AtomicError,
+    MissingAtomicDataError,
+    ChargeError,
+    InvalidIonError,
+    InvalidIsotopeError,
+    InvalidElementError,
+    InvalidParticleError,
+    AtomicWarning,
+    MissingAtomicDataWarning,
+    )
+
 from ..exceptions import *
 
 plasmapy_exceptions = [
diff --git a/plasmapy/utils/tests/test_pytest_helpers.py b/plasmapy/utils/tests/test_pytest_helpers.py
index ca5a7bed..6477bc95 100644
--- a/plasmapy/utils/tests/test_pytest_helpers.py
+++ b/plasmapy/utils/tests/test_pytest_helpers.py
@@ -15,7 +15,8 @@
     MissingExceptionError,
 )
 
-from ..exceptions import PlasmaPyWarning, PlasmaPyError, AtomicError
+from ..exceptions import PlasmaPyWarning, PlasmaPyError
+from plasmapy.atomic.exceptions import AtomicError
 
 from ...atomic import Particle