Task: Fixes #6757 (rdkit/rdkit#7664)

org: rdkit
repo: rdkit
pr_number: 7664
issue_title: Fixes #6757
issue_body: Simple change to make sure we have called `assignStereochemistry()` before doing the work in `numAtomStereoCenters()` and `numUnspecifiedAtomStereoCenters()`
resolved_issues: number: 6757
title: rdMolDescriptors.CalcNumAtomStereoCenters fails on Sanitized Molecule
body: **Describe the bug**
`rdMolDescriptors.CalcNumAtomStereoCenters` fails to find stereo centers if a molecule is sanitized (twice).
I had assumed this was deuterium-related, but it appears to affect other stereo centers as well (see https://github.com/rdkit/rdkit/discussions/6657).

**To Reproduce**
```python
from rdkit import Chem
from rdkit.Chem import rdMolDescriptors

smi = "[2H][C@@](C)(N)C(=O)O"
mol1 = Chem.MolFromSmiles(smi)
cip_count1 = sum([atom.HasProp("_CIPCode") for atom in mol1.GetAtoms()])
stereocenters1 = rdMolDescriptors.CalcNumAtomStereoCenters(mol1)


mol2 = Chem.Mol(mol1)
Chem.SanitizeMol(mol2)
cip_count2 = sum([atom.HasProp("_CIPCode") for atom in mol2.GetAtoms()])
try:
    stereocenters2 = rdMolDescriptors.CalcNumAtomStereoCenters(mol2)
except: # ValueError: numStereoCenters called without stereo being assigned
    stereocenters2 = 0

assert cip_count1 == cip_count2
assert stereocenters1 == stereocenters2 # Fails. 1 != 0
```

**Expected behavior**
One stereo center should be detected for both molecules. 
The properties  `_CIPCode` and `_ChiralityPossible` are still correct so I've been counting atom CIPCodes as a workaround.


**Configuration:**
 - RDKit version: 2022.9.5 (linked discussion is on 2023.03.3)
 - OS: Ubuntu 22.04.3
 - Python version: 3.9
 - Are you using conda? No
 - How did you install the RDKit? rdkit-pypi via Poetry


Reference solution:
fix_patch: diff --git a/Code/GraphMol/Chirality.cpp b/Code/GraphMol/Chirality.cpp
index e5a40ddee69..d4c1f35e931 100644
--- a/Code/GraphMol/Chirality.cpp
+++ b/Code/GraphMol/Chirality.cpp
@@ -2771,6 +2771,10 @@ void assignStereochemistry(ROMol &mol, bool cleanIt, bool force,
     return;
   }
 
+  if (mol.needsUpdatePropertyCache()) {
+    mol.updatePropertyCache(false);
+  }
+
   if (!Chirality::getUseLegacyStereoPerception()) {
     Chirality::stereoPerception(mol, cleanIt, flagPossibleStereoCenters);
   } else {
diff --git a/Code/GraphMol/Descriptors/Lipinski.cpp b/Code/GraphMol/Descriptors/Lipinski.cpp
index e0b044aed34..d80727f8184 100644
--- a/Code/GraphMol/Descriptors/Lipinski.cpp
+++ b/Code/GraphMol/Descriptors/Lipinski.cpp
@@ -506,36 +506,46 @@ bool hasStereoAssigned(const ROMol &mol) {
   return mol.hasProp(common_properties::_StereochemDone);
 }
 }  // namespace
-const std::string NumAtomStereoCentersVersion = "1.0.0";
+const std::string NumAtomStereoCentersVersion = "1.0.1";
 unsigned int numAtomStereoCenters(const ROMol &mol) {
+  std::unique_ptr<ROMol> tmol;
+  const ROMol *mptr = &mol;
   if (!hasStereoAssigned(mol)) {
-    throw ValueErrorException(
-        "numStereoCenters called without stereo being assigned");
+    tmol.reset(new ROMol(mol));
+    constexpr bool cleanIt = true;
+    constexpr bool force = true;
+    constexpr bool flagPossible = true;
+    MolOps::assignStereochemistry(*tmol, cleanIt, force, flagPossible);
+    mptr = tmol.get();
   }
 
   unsigned int res = 0;
-  for (ROMol::ConstAtomIterator atom = mol.beginAtoms(); atom != mol.endAtoms();
-       ++atom) {
-    if ((*atom)->hasProp(common_properties::_ChiralityPossible)) {
-      res++;
+  for (const auto &atom : mptr->atoms()) {
+    if (atom->hasProp(common_properties::_ChiralityPossible)) {
+      ++res;
     }
   }
   return res;
 }
 
-const std::string NumUnspecifiedAtomStereoCentersVersion = "1.0.0";
+const std::string NumUnspecifiedAtomStereoCentersVersion = "1.0.1";
 unsigned int numUnspecifiedAtomStereoCenters(const ROMol &mol) {
+  std::unique_ptr<ROMol> tmol;
+  const ROMol *mptr = &mol;
   if (!hasStereoAssigned(mol)) {
-    throw ValueErrorException(
-        "numUnspecifiedStereoCenters called without stereo being assigned");
+    tmol.reset(new ROMol(mol));
+    constexpr bool cleanIt = true;
+    constexpr bool force = true;
+    constexpr bool flagPossible = true;
+    MolOps::assignStereochemistry(*tmol, cleanIt, force, flagPossible);
+    mptr = tmol.get();
   }
 
   unsigned int res = 0;
-  for (ROMol::ConstAtomIterator atom = mol.beginAtoms(); atom != mol.endAtoms();
-       ++atom) {
-    if ((*atom)->hasProp(common_properties::_ChiralityPossible) &&
-        (*atom)->getChiralTag() == Atom::CHI_UNSPECIFIED) {
-      res++;
+  for (const auto &atom : mptr->atoms()) {
+    if (atom->hasProp(common_properties::_ChiralityPossible) &&
+        atom->getChiralTag() == Atom::CHI_UNSPECIFIED) {
+      ++res;
     }
   }
   return res;